From 8ab5415d6c701a59dd6fc2bc93cf476ecc03ada5 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 9 May 2012 22:14:51 +0300 Subject: ath6kl: Use correct max-scan-SSIDs limit The currently used firmware images support 16 SSIDs in the scan request (indexes 0..15), so update the host driver to use the same limit to allow some more SSIDs to be scanned per request. In addition, change the max-index to max-SSIDs to make it easier to understand the implementation and fix couple of off-by-one checks that could limit the maximum number of entries too strictly. Signed-off-by: Jouni Malinen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 8 ++++---- drivers/net/wireless/ath/ath6kl/wmi.c | 2 +- drivers/net/wireless/ath/ath6kl/wmi.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b869a358ce43..a6bebc20fd18 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -888,7 +888,7 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar, { u8 i; - if (n_ssids > MAX_PROBED_SSID_INDEX) + if (n_ssids > MAX_PROBED_SSIDS) return -EINVAL; for (i = 0; i < n_ssids; i++) { @@ -900,7 +900,7 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar, } /* Make sure no old entries are left behind */ - for (i = n_ssids; i < MAX_PROBED_SSID_INDEX; i++) { + for (i = n_ssids; i < MAX_PROBED_SSIDS; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, DISABLE_SSID_FLAG, 0, NULL); } @@ -3470,7 +3470,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) } /* max num of ssids that can be probed during scanning */ - wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; + wiphy->max_scan_ssids = MAX_PROBED_SSIDS; wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ switch (ar->hw.cap) { case WMI_11AN_CAP: @@ -3527,7 +3527,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->wowlan.pattern_min_len = 1; wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; - wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX; + wiphy->max_sched_scan_ssids = MAX_PROBED_SSIDS; ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | WIPHY_FLAG_HAVE_AP_SME | diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index ee8ec2394c2c..bdd3b2c55637 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -1995,7 +1995,7 @@ int ath6kl_wmi_probedssid_cmd(struct wmi *wmi, u8 if_idx, u8 index, u8 flag, struct wmi_probed_ssid_cmd *cmd; int ret; - if (index > MAX_PROBED_SSID_INDEX) + if (index >= MAX_PROBED_SSIDS) return -EINVAL; if (ssid_len > sizeof(cmd->ssid)) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 9076bec3a2ba..3518550e3504 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -978,7 +978,7 @@ struct wmi_bss_filter_cmd { } __packed; /* WMI_SET_PROBED_SSID_CMDID */ -#define MAX_PROBED_SSID_INDEX 9 +#define MAX_PROBED_SSIDS 16 enum wmi_ssid_flag { /* disables entry */ @@ -992,7 +992,7 @@ enum wmi_ssid_flag { }; struct wmi_probed_ssid_cmd { - /* 0 to MAX_PROBED_SSID_INDEX */ + /* 0 to MAX_PROBED_SSIDS - 1 */ u8 entry_index; /* see, enum wmi_ssid_flg */ -- cgit v1.2.3 From 6821d4f08dcdc7d8c21a3280f57f53a080f19840 Mon Sep 17 00:00:00 2001 From: Naveen Gangadharan Date: Fri, 11 May 2012 14:19:09 -0700 Subject: ath6kl: Add wow multicast firmware capability support Infrastructure to enable Multicast WOW support based on firmware capability added to the driver.This enables different customers or chips to control this feature based on firmware capability. kvalo: Firmware capability infrastructure for multicast wow feature, indetation fixes. Signed-off-by: Naveen Gangadharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 8 ++++++-- drivers/net/wireless/ath/ath6kl/core.h | 7 +++++++ drivers/net/wireless/ath/ath6kl/main.c | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index a6bebc20fd18..22843a1b9f21 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2074,7 +2074,9 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) return -EINVAL; - if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) { + if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) && + test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, + ar->fw_capabilities)) { ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, false); if (ret) @@ -2209,7 +2211,9 @@ static int ath6kl_wow_resume(struct ath6kl *ar) ar->state = ATH6KL_STATE_ON; - if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags)) { + if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) && + test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, + ar->fw_capabilities)) { ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, true); if (ret) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 4d9c6f142698..79c7055674bd 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -100,6 +100,13 @@ enum ath6kl_fw_capability { /* Firmware has support to override rsn cap of rsn ie */ ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + /* + * Multicast support in WOW and host awake mode. + * Allow all multicast in host awake mode. + * Apply multicast filter in WOW mode. + */ + ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index e5524470529c..3e6768ae80af 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1167,7 +1167,10 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) else clear_bit(NETDEV_MCAST_ALL_ON, &vif->flags); - mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON); + if (test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, + vif->ar->fw_capabilities)) { + mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON); + } if (!(ndev->flags & IFF_MULTICAST)) { mc_all_on = false; -- cgit v1.2.3 From c422d52d0450988ce9a1ffdddb78807538396749 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Tue, 15 May 2012 00:09:23 -0700 Subject: ath6kl: enable enhanced bmiss detection Enable enhanced bmiss detection if the firmware supports it. This feature is only enabled on some firmwares since it comes with a power cost. Also add a few missing command ids to keep the enums straight. kvalo: fix a compiler with ath6kl_err(), add few empty lines Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 30 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/cfg80211.h | 2 ++ drivers/net/wireless/ath/ath6kl/core.h | 3 +++ drivers/net/wireless/ath/ath6kl/init.c | 3 +++ drivers/net/wireless/ath/ath6kl/wmi.c | 19 +++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 11 +++++++++++ 6 files changed, 68 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 22843a1b9f21..e68b1077816a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -576,6 +576,9 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->nw_type = vif->next_mode; + /* enable enhanced bmiss detection if applicable */ + ath6kl_cfg80211_sta_bmiss_enhance(vif, true); + if (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) nw_subtype = SUBTYPE_P2PCLIENT; @@ -1512,6 +1515,9 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, } } + /* need to clean up enhanced bmiss detection fw state */ + ath6kl_cfg80211_sta_bmiss_enhance(vif, false); + set_iface_type: switch (type) { case NL80211_IFTYPE_STATION: @@ -2618,6 +2624,30 @@ static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, return 0; } +void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable) +{ + int err; + + if (WARN_ON(!test_bit(WMI_READY, &vif->ar->flag))) + return; + + if (vif->nw_type != INFRA_NETWORK) + return; + + if (!test_bit(ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, + vif->ar->fw_capabilities)) + return; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s fw bmiss enhance\n", + enable ? "enable" : "disable"); + + err = ath6kl_wmi_sta_bmiss_enhance_cmd(vif->ar->wmi, + vif->fw_vif_idx, enable); + if (err) + ath6kl_err("failed to %s enhanced bmiss detection: %d\n", + enable ? "enable" : "disable", err); +} + static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon, u8 *rsn_capab) { diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index 5ea8cbb79f43..b992046a1b0e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -62,5 +62,7 @@ void ath6kl_cfg80211_cleanup(struct ath6kl *ar); struct ath6kl *ath6kl_cfg80211_create(void); void ath6kl_cfg80211_destroy(struct ath6kl *ar); +/* TODO: remove this once ath6kl_vif_cleanup() is moved to cfg80211.c */ +void ath6kl_cfg80211_sta_bmiss_enhance(struct ath6kl_vif *vif, bool enable); #endif /* ATH6KL_CFG80211_H */ diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 79c7055674bd..99169794ff3a 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -107,6 +107,9 @@ enum ath6kl_fw_capability { */ ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, + /* Firmware supports enhanced bmiss detection */ + ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 7eb0515f458a..10de1322e702 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1659,6 +1659,9 @@ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) cfg80211_scan_done(vif->scan_req, true); vif->scan_req = NULL; } + + /* need to clean up enhanced bmiss detection fw state */ + ath6kl_cfg80211_sta_bmiss_enhance(vif, false); } void ath6kl_stop_txrx(struct ath6kl *ar) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index bdd3b2c55637..6ad762daa425 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2997,6 +2997,25 @@ int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance) +{ + struct sk_buff *skb; + struct wmi_sta_bmiss_enhance_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_sta_bmiss_enhance_cmd *) skb->data; + cmd->enable = enhance ? 1 : 0; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_STA_BMISS_ENHANCE_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + s32 ath6kl_wmi_get_rate(s8 rate_index) { if (rate_index == RATE_AUTO) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 3518550e3504..8c07e3858b11 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -624,6 +624,10 @@ enum wmi_cmd_id { WMI_SEND_MGMT_CMDID, WMI_BEGIN_SCAN_CMDID, + WMI_SET_BLACK_LIST, + WMI_SET_MCASTRATE, + + WMI_STA_BMISS_ENHANCE_CMDID, }; enum wmi_mgmt_frame_type { @@ -1017,6 +1021,11 @@ struct wmi_bmiss_time_cmd { __le16 num_beacons; }; +/* WMI_STA_ENHANCE_BMISS_CMDID */ +struct wmi_sta_bmiss_enhance_cmd { + u8 enable; +} __packed; + /* WMI_SET_POWER_MODE_CMDID */ enum wmi_power_mode { REC_POWER = 0x01, @@ -2547,6 +2556,8 @@ int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode); int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, u8 *filter, bool add_filter); +int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); + /* AP mode uAPSD */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); -- cgit v1.2.3 From dd45b7598f1c52933f276ba7ce175fa1305b8ba0 Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Wed, 16 May 2012 13:29:00 +0300 Subject: ath6kl: Include match ssid list in scheduled scan Scheduled scan implementation was only taking probed list into consideration. The matched list was dropped. This would cause FW not to report the AP as the list never had that AP's SSID populated. This was causing long connection time when supplicant would just issue a wild card SSID in probed list. As a part of this implementation, ath6kl driver would create a complete list by taking both probed and matched list and pass it to FW. FW would probe for the SSID that it needs to and would match against the relevant SSIDS that is been configured. kvalo: whitespace changes, less indentation in the for loop, use ++ Signed-off-by: Naveen Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 98 +++++++++++++++++++++++++++--- drivers/net/wireless/ath/ath6kl/core.h | 5 ++ drivers/net/wireless/ath/ath6kl/wmi.h | 6 ++ 3 files changed, 99 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index e68b1077816a..f3a6cfc0ddc8 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -53,6 +53,11 @@ #define DEFAULT_BG_SCAN_PERIOD 60 +struct ath6kl_cfg80211_match_probe_ssid { + struct cfg80211_ssid ssid; + u8 flag; +}; + static struct ieee80211_rate ath6kl_rates[] = { RATETAB_ENT(10, 0x1, 0), RATETAB_ENT(20, 0x2, 0), @@ -887,23 +892,76 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, static int ath6kl_set_probed_ssids(struct ath6kl *ar, struct ath6kl_vif *vif, - struct cfg80211_ssid *ssids, int n_ssids) + struct cfg80211_ssid *ssids, int n_ssids, + struct cfg80211_match_set *match_set, + int n_match_ssid) { - u8 i; + u8 i, j, index_to_add, ssid_found = false; + struct ath6kl_cfg80211_match_probe_ssid ssid_list[MAX_PROBED_SSIDS]; + + memset(ssid_list, 0, sizeof(ssid_list)); - if (n_ssids > MAX_PROBED_SSIDS) + if (n_ssids > MAX_PROBED_SSIDS || + n_match_ssid > MAX_PROBED_SSIDS) return -EINVAL; for (i = 0; i < n_ssids; i++) { + memcpy(ssid_list[i].ssid.ssid, + ssids[i].ssid, + ssids[i].ssid_len); + ssid_list[i].ssid.ssid_len = ssids[i].ssid_len; + + if (ssids[i].ssid_len) + ssid_list[i].flag = SPECIFIC_SSID_FLAG; + else + ssid_list[i].flag = ANY_SSID_FLAG; + + if (n_match_ssid == 0) + ssid_list[i].flag |= MATCH_SSID_FLAG; + } + + index_to_add = i; + + for (i = 0; i < n_match_ssid; i++) { + ssid_found = false; + + for (j = 0; j < n_ssids; j++) { + if ((match_set[i].ssid.ssid_len == + ssid_list[j].ssid.ssid_len) && + (!memcmp(ssid_list[j].ssid.ssid, + match_set[i].ssid.ssid, + match_set[i].ssid.ssid_len))) { + ssid_list[j].flag |= MATCH_SSID_FLAG; + ssid_found = true; + break; + } + } + + if (ssid_found) + continue; + + if (index_to_add >= MAX_PROBED_SSIDS) + continue; + + ssid_list[index_to_add].ssid.ssid_len = + match_set[i].ssid.ssid_len; + memcpy(ssid_list[index_to_add].ssid.ssid, + match_set[i].ssid.ssid, + match_set[i].ssid.ssid_len); + ssid_list[index_to_add].flag |= MATCH_SSID_FLAG; + index_to_add++; + } + + for (i = 0; i < index_to_add; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, - ssids[i].ssid_len ? - SPECIFIC_SSID_FLAG : ANY_SSID_FLAG, - ssids[i].ssid_len, - ssids[i].ssid); + ssid_list[i].flag, + ssid_list[i].ssid.ssid_len, + ssid_list[i].ssid.ssid); + } /* Make sure no old entries are left behind */ - for (i = n_ssids; i < MAX_PROBED_SSIDS; i++) { + for (i = index_to_add; i < MAX_PROBED_SSIDS; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, DISABLE_SSID_FLAG, 0, NULL); } @@ -937,7 +995,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, } ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, - request->n_ssids); + request->n_ssids, NULL, 0); if (ret < 0) return ret; @@ -3194,10 +3252,24 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, ath6kl_cfg80211_scan_complete_event(vif, true); ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, - request->n_ssids); + request->n_ssids, + request->match_sets, + request->n_match_sets); if (ret < 0) return ret; + if (!request->n_match_sets) { + ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + ALL_BSS_FILTER, 0); + if (ret < 0) + return ret; + } else { + ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + MATCHED_SSID_FILTER, 0); + if (ret < 0) + return ret; + } + /* fw uses seconds, also make sure that it's >0 */ interval = max_t(u16, 1, request->interval / 1000); @@ -3505,6 +3577,12 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) /* max num of ssids that can be probed during scanning */ wiphy->max_scan_ssids = MAX_PROBED_SSIDS; + + /* max num of ssids that can be matched after scan */ + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, + ar->fw_capabilities)) + wiphy->max_match_sets = MAX_PROBED_SSIDS; + wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ switch (ar->hw.cap) { case WMI_11AN_CAP: diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 99169794ff3a..991bd96f7cf6 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -110,6 +110,11 @@ enum ath6kl_fw_capability { /* Firmware supports enhanced bmiss detection */ ATH6KL_FW_CAPABILITY_BMISS_ENHANCE, + /* + * FW supports matching of ssid in schedule scan + */ + ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 8c07e3858b11..47756795a26c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -964,6 +964,9 @@ enum wmi_bss_filter { /* beacons matching probed ssid */ PROBED_SSID_FILTER, + /* beacons matching matched ssid */ + MATCHED_SSID_FILTER, + /* marker only */ LAST_BSS_FILTER, }; @@ -993,6 +996,9 @@ enum wmi_ssid_flag { /* probes for any ssid */ ANY_SSID_FLAG = 0x02, + + /* match for ssid */ + MATCH_SSID_FLAG = 0x08, }; struct wmi_probed_ssid_cmd { -- cgit v1.2.3 From 33a6664a6e4b45814ef6e3129842f3fd7e5d1117 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Wed, 16 May 2012 13:41:13 -0700 Subject: ath6kl: issue wmi disconnect after notifying cfg80211 ath6kl would issue a wmi disconnect command in response to a remote disconnect and return early without notifying cfg80211, only sending a cfg80211_disconnected (with reason code always 3) in response to the second disconnect firmware event. Pass the right reason code to cfg80211 on the first disconnect instead. This fixes at least one bug where a p2p client would stop trying to connect after receiving a stale RSN deauth which was reported to cfg80211 as GO leaving BSS. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index f3a6cfc0ddc8..7845d33deed9 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -860,20 +860,6 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, } } - /* - * Send a disconnect command to target when a disconnect event is - * received with reason code other than 3 (DISCONNECT_CMD - disconnect - * request from host) to make the firmware stop trying to connect even - * after giving disconnect event. There will be one more disconnect - * event for this disconnect command with reason code DISCONNECT_CMD - * which will be notified to cfg80211. - */ - - if (reason != DISCONNECT_CMD) { - ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); - return; - } - clear_bit(CONNECT_PEND, &vif->flags); if (vif->sme_state == SME_CONNECTING) { @@ -883,11 +869,22 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); } else if (vif->sme_state == SME_CONNECTED) { - cfg80211_disconnected(vif->ndev, reason, + cfg80211_disconnected(vif->ndev, proto_reason, NULL, 0, GFP_KERNEL); } vif->sme_state = SME_DISCONNECTED; + + /* + * Send a disconnect command to target when a disconnect event is + * received with reason code other than 3 (DISCONNECT_CMD - disconnect + * request from host) to make the firmware stop trying to connect even + * after giving disconnect event. There will be one more disconnect + * event for this disconnect command with reason code DISCONNECT_CMD + * which won't be notified to cfg80211. + */ + if (reason != DISCONNECT_CMD) + ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); } static int ath6kl_set_probed_ssids(struct ath6kl *ar, -- cgit v1.2.3 From 8b5e218d8caa7592d26e68157bd71f50426bb7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eric=20B=C3=A9nard?= Date: Tue, 8 May 2012 17:12:17 +0200 Subject: can: flexcan: add PM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tested on an i.MX257 Signed-off-by: Eric Bénard Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 1efb08386c61..0d058b0a3cb6 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -1050,6 +1050,42 @@ static struct of_device_id flexcan_of_match[] = { {}, }; +#ifdef CONFIG_PM +static int flexcan_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv = netdev_priv(dev); + + flexcan_chip_disable(priv); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + priv->can.state = CAN_STATE_SLEEPING; + + return 0; +} + +static int flexcan_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct flexcan_priv *priv = netdev_priv(dev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + flexcan_chip_enable(priv); + + return 0; +} +#else +#define flexcan_suspend NULL +#define flexcan_resume NULL +#endif + static struct platform_driver flexcan_driver = { .driver = { .name = DRV_NAME, @@ -1058,6 +1094,8 @@ static struct platform_driver flexcan_driver = { }, .probe = flexcan_probe, .remove = __devexit_p(flexcan_remove), + .suspend = flexcan_suspend, + .resume = flexcan_resume, }; module_platform_driver(flexcan_driver); -- cgit v1.2.3 From fa338be062e31141a8dadd822a98f558785c8818 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Tue, 22 May 2012 12:52:48 +0530 Subject: ath6kl: Fix missing gpio pin 9 configuration GPIO pin 9 also needs to be configured along with other gpio pins to avoid sdio crc errors. I've not experienced any issue with missing this particular gpio pin configuration, found dunring code review. This can potentially improve rx performance. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 6 ++++++ drivers/net/wireless/ath/ath6kl/target.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 10de1322e702..241febcd7f7c 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1392,6 +1392,12 @@ static int ath6kl_init_upload(struct ath6kl *ar) ar->version.target_ver == AR6003_HW_2_1_1_VERSION) { ath6kl_err("temporary war to avoid sdio crc error\n"); + param = 0x28; + address = GPIO_BASE_ADDRESS + GPIO_PIN9_ADDRESS; + status = ath6kl_bmi_reg_write(ar, address, param); + if (status) + return status; + param = 0x20; address = GPIO_BASE_ADDRESS + GPIO_PIN10_ADDRESS; diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index 78e0ef4567a5..a98c12ba70c1 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -45,6 +45,7 @@ #define LPO_CAL_ENABLE_S 20 #define LPO_CAL_ENABLE 0x00100000 +#define GPIO_PIN9_ADDRESS 0x0000004c #define GPIO_PIN10_ADDRESS 0x00000050 #define GPIO_PIN11_ADDRESS 0x00000054 #define GPIO_PIN12_ADDRESS 0x00000058 -- cgit v1.2.3 From 06e360ace9434bf37164fd87941b797cc0f3cb7e Mon Sep 17 00:00:00 2001 From: Bala Shanmugam Date: Tue, 22 May 2012 13:23:12 +0530 Subject: ath6kl: Add support for setting tx rateset. Tx legacy and mcs rateset can configured using iw for 2.4 and 5 bands. Add support for the same in driver. kvalo: add an enum for the hw flags and rename the flag accordingly, rename ath6kl_cfg80211_set_bitrate_mask() to a shorter version to make it easier to indent Signed-off-by: Bala Shanmugam Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 24 +++++++ drivers/net/wireless/ath/ath6kl/core.h | 6 ++ drivers/net/wireless/ath/ath6kl/init.c | 5 ++ drivers/net/wireless/ath/ath6kl/wmi.c | 109 +++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 32 +++++++++ 5 files changed, 176 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 7845d33deed9..6a934e16ae83 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3320,6 +3320,18 @@ static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, return 0; } +static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + return ath6kl_wmi_set_bitrate_mask(ar->wmi, vif->fw_vif_idx, + mask); +} + static const struct ieee80211_txrx_stypes ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { @@ -3386,6 +3398,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .mgmt_frame_register = ath6kl_mgmt_frame_register, .sched_scan_start = ath6kl_cfg80211_sscan_start, .sched_scan_stop = ath6kl_cfg80211_sscan_stop, + .set_bitrate_mask = ath6kl_cfg80211_set_bitrate, }; void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) @@ -3616,6 +3629,17 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.cap = 0; ath6kl_band_5ghz.ht_cap.ht_supported = false; } + + if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) { + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[1] = 0xff; + } else { + ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; + ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; + } + if (band_2gig) wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; if (band_5gig) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 991bd96f7cf6..b1bc6bc69f2a 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -127,6 +127,10 @@ struct ath6kl_fw_ie { u8 data[0]; }; +enum ath6kl_hw_flags { + ATH6KL_HW_FLAG_64BIT_RATES = BIT(0), +}; + #define ATH6KL_FW_API2_FILE "fw-2.bin" #define ATH6KL_FW_API3_FILE "fw-3.bin" @@ -702,6 +706,8 @@ struct ath6kl { u32 testscript_addr; enum wmi_phy_cap cap; + u32 flags; + struct ath6kl_hw_fw { const char *dir; const char *otp; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 241febcd7f7c..daf24ee9d28a 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -42,6 +42,7 @@ static const struct ath6kl_hw hw_list[] = { .reserved_ram_size = 6912, .refclk_hz = 26000000, .uarttx_pin = 8, + .flags = 0, /* hw2.0 needs override address hardcoded */ .app_start_override_addr = 0x944C00, @@ -67,6 +68,7 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 26000000, .uarttx_pin = 8, .testscript_addr = 0x57ef74, + .flags = 0, .fw = { .dir = AR6003_HW_2_1_1_FW_DIR, @@ -91,6 +93,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, + .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_0_FW_DIR, @@ -110,6 +113,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, + .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_1_FW_DIR, @@ -129,6 +133,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x435c00, .refclk_hz = 40000000, .uarttx_pin = 11, + .flags = ATH6KL_HW_FLAG_64BIT_RATES, .fw = { .dir = AR6004_HW_1_2_FW_DIR, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 6ad762daa425..63dc4fd73c4c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2599,6 +2599,115 @@ static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi) spin_unlock_bh(&wmi->lock); } +static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask) +{ + struct sk_buff *skb; + int ret, mode, band; + u64 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + struct wmi_set_tx_select_rates64_cmd *cmd; + + memset(&ratemask, 0, sizeof(ratemask)); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + /* copy legacy rate mask */ + ratemask[band] = mask->control[band].legacy; + if (band == IEEE80211_BAND_5GHZ) + ratemask[band] = + mask->control[band].legacy << 4; + + /* copy mcs rate mask */ + mcsrate = mask->control[band].mcs[1]; + mcsrate <<= 8; + mcsrate |= mask->control[band].mcs[0]; + ratemask[band] |= mcsrate << 12; + ratemask[band] |= mcsrate << 28; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, + "Ratemask 64 bit: 2.4:%llx 5:%llx\n", + ratemask[0], ratemask[1]); + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_tx_select_rates64_cmd *) skb->data; + for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) { + /* A mode operate in 5GHZ band */ + if (mode == WMI_RATES_MODE_11A || + mode == WMI_RATES_MODE_11A_HT20 || + mode == WMI_RATES_MODE_11A_HT40) + band = IEEE80211_BAND_5GHZ; + else + band = IEEE80211_BAND_2GHZ; + cmd->ratemask[mode] = cpu_to_le64(ratemask[band]); + } + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_TX_SELECT_RATES_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask) +{ + struct sk_buff *skb; + int ret, mode, band; + u32 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + struct wmi_set_tx_select_rates32_cmd *cmd; + + memset(&ratemask, 0, sizeof(ratemask)); + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + /* copy legacy rate mask */ + ratemask[band] = mask->control[band].legacy; + if (band == IEEE80211_BAND_5GHZ) + ratemask[band] = + mask->control[band].legacy << 4; + + /* copy mcs rate mask */ + mcsrate = mask->control[band].mcs[0]; + ratemask[band] |= mcsrate << 12; + ratemask[band] |= mcsrate << 20; + } + + ath6kl_dbg(ATH6KL_DBG_WMI, + "Ratemask 32 bit: 2.4:%x 5:%x\n", + ratemask[0], ratemask[1]); + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd) * WMI_RATES_MODE_MAX); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_tx_select_rates32_cmd *) skb->data; + for (mode = 0; mode < WMI_RATES_MODE_MAX; mode++) { + /* A mode operate in 5GHZ band */ + if (mode == WMI_RATES_MODE_11A || + mode == WMI_RATES_MODE_11A_HT20 || + mode == WMI_RATES_MODE_11A_HT40) + band = IEEE80211_BAND_5GHZ; + else + band = IEEE80211_BAND_2GHZ; + cmd->ratemask[mode] = cpu_to_le32(ratemask[band]); + } + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_TX_SELECT_RATES_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + +int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath6kl *ar = wmi->parent_dev; + + if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) + return ath6kl_set_bitrate_mask64(wmi, if_idx, mask); + else + return ath6kl_set_bitrate_mask32(wmi, if_idx, mask); +} + int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode) { diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 47756795a26c..7c94fe3e9e6d 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1063,6 +1063,36 @@ struct wmi_power_params_cmd { __le16 ps_fail_event_policy; } __packed; +/* + * Ratemask for below modes should be passed + * to WMI_SET_TX_SELECT_RATES_CMDID. + * AR6003 has 32 bit mask for each modes. + * First 12 bits for legacy rates, 13 to 20 + * bits for HT 20 rates and 21 to 28 bits for + * HT 40 rates + */ +enum wmi_mode_phy { + WMI_RATES_MODE_11A = 0, + WMI_RATES_MODE_11G, + WMI_RATES_MODE_11B, + WMI_RATES_MODE_11GONLY, + WMI_RATES_MODE_11A_HT20, + WMI_RATES_MODE_11G_HT20, + WMI_RATES_MODE_11A_HT40, + WMI_RATES_MODE_11G_HT40, + WMI_RATES_MODE_MAX +}; + +/* WMI_SET_TX_SELECT_RATES_CMDID */ +struct wmi_set_tx_select_rates32_cmd { + __le32 ratemask[WMI_RATES_MODE_MAX]; +} __packed; + +/* WMI_SET_TX_SELECT_RATES_CMDID */ +struct wmi_set_tx_select_rates64_cmd { + __le64 ratemask[WMI_RATES_MODE_MAX]; +} __packed; + /* WMI_SET_DISC_TIMEOUT_CMDID */ struct wmi_disc_timeout_cmd { /* seconds */ @@ -2547,6 +2577,8 @@ int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, __be32 ips0, __be32 ips1); int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_host_mode host_mode); +int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, + const struct cfg80211_bitrate_mask *mask); int ath6kl_wmi_set_wow_mode_cmd(struct wmi *wmi, u8 if_idx, enum ath6kl_wow_mode wow_mode, u32 filter, u16 host_req_delay); -- cgit v1.2.3 From be9c00295b34760ea2f9667929049f094116b5a8 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 19:10:45 +0900 Subject: sh: intc: Kill off deprecated dynamic IRQ API. Now that all of the users have been converted away, kill off the remnants of the old API. Signed-off-by: Paul Mundt --- drivers/sh/intc/Makefile | 2 +- drivers/sh/intc/dynamic.c | 65 ----------------------------------------------- 2 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 drivers/sh/intc/dynamic.c (limited to 'drivers') diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile index bb5df868d77a..44f006d09471 100644 --- a/drivers/sh/intc/Makefile +++ b/drivers/sh/intc/Makefile @@ -1,4 +1,4 @@ -obj-y := access.o chip.o core.o dynamic.o handle.o virq.o +obj-y := access.o chip.o core.o handle.o virq.o obj-$(CONFIG_INTC_BALANCING) += balancing.o obj-$(CONFIG_INTC_USERIMASK) += userimask.o diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c deleted file mode 100644 index 5fea1ee8799a..000000000000 --- a/drivers/sh/intc/dynamic.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Dynamic IRQ management - * - * Copyright (C) 2010 Paul Mundt - * - * Modelled after arch/x86/kernel/apic/io_apic.c - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include -#include -#include -#include -#include "internals.h" /* only for activate_irq() damage.. */ - -/* - * The IRQ bitmap provides a global map of bound IRQ vectors for a - * given platform. Allocation of IRQs are either static through the CPU - * vector map, or dynamic in the case of board mux vectors or MSI. - * - * As this is a central point for all IRQ controllers on the system, - * each of the available sources are mapped out here. This combined with - * sparseirq makes it quite trivial to keep the vector map tightly packed - * when dynamically creating IRQs, as well as tying in to otherwise - * unused irq_desc positions in the sparse array. - */ - -/* - * Dynamic IRQ allocation and deallocation - */ -unsigned int create_irq_nr(unsigned int irq_want, int node) -{ - int irq = irq_alloc_desc_at(irq_want, node); - if (irq < 0) - return 0; - - activate_irq(irq); - return irq; -} - -int create_irq(void) -{ - int irq = irq_alloc_desc(numa_node_id()); - if (irq >= 0) - activate_irq(irq); - - return irq; -} - -void destroy_irq(unsigned int irq) -{ - irq_free_desc(irq); -} - -void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) -{ - int i; - - for (i = 0; i < nr_vecs; i++) - irq_reserve_irq(evt2irq(vectors[i].vect)); -} -- cgit v1.2.3 From a60977a51333a8108f0574aa26094d66b7fedf34 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 14:59:26 +0900 Subject: sh: clkfwk: Move to common clk_div_table accessors for div4/div6. This plugs in a generic clk_div_table, based on the div4 version. div6 is then adopted to use it for encapsulating its div table, which permits us to start div6/4 unification, as well as preparation for other div types. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 46 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index f0d015dd0fef..9dea32907795 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -71,6 +71,22 @@ static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) return clk_rate_table_round(clk, clk->freq_table, rate); } +/* + * Div/mult table lookup helpers + */ +static inline struct clk_div_table *clk_to_div_table(struct clk *clk) +{ + return clk->priv; +} + +static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) +{ + return clk_to_div_table(clk)->div_mult_table; +} + +/* + * div6 support + */ static int sh_clk_div6_divisors[64] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, @@ -78,14 +94,18 @@ static int sh_clk_div6_divisors[64] = { 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 }; -static struct clk_div_mult_table sh_clk_div6_table = { +static struct clk_div_mult_table div6_div_mult_table = { .divisors = sh_clk_div6_divisors, .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), }; +static struct clk_div_table sh_clk_div6_table = { + .div_mult_table = &div6_div_mult_table, +}; + static unsigned long sh_clk_div6_recalc(struct clk *clk) { - struct clk_div_mult_table *table = &sh_clk_div6_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); unsigned int idx; clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -98,7 +118,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) { - struct clk_div_mult_table *table = &sh_clk_div6_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); u32 value; int ret, i; @@ -223,7 +243,8 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, { struct clk *clkp; void *freq_table; - int nr_divs = sh_clk_div6_table.nr_divisors; + struct clk_div_table *table = &sh_clk_div6_table; + int nr_divs = table->div_mult_table->nr_divisors; int freq_table_size = sizeof(struct cpufreq_frequency_table); int ret = 0; int k; @@ -239,6 +260,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, clkp = clks + k; clkp->ops = ops; + clkp->priv = table; clkp->freq_table = freq_table + (k * freq_table_size); clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; ret = clk_register(clkp); @@ -262,10 +284,12 @@ int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) &sh_clk_div6_reparent_clk_ops); } +/* + * div4 support + */ static unsigned long sh_clk_div4_recalc(struct clk *clk) { - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); unsigned int idx; clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -278,8 +302,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) { - struct clk_div4_table *d4t = clk->priv; - struct clk_div_mult_table *table = d4t->div_mult_table; + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); u32 value; int ret; @@ -308,7 +331,7 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) { - struct clk_div4_table *d4t = clk->priv; + struct clk_div_table *dt = clk_to_div_table(clk); unsigned long value; int idx = clk_rate_table_find(clk, clk->freq_table, rate); if (idx < 0) @@ -319,8 +342,9 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) value |= (idx << clk->enable_bit); sh_clk_write(value, clk); - if (d4t->kick) - d4t->kick(clk); + /* XXX: Should use a post-change notifier */ + if (dt->kick) + dt->kick(clk); return 0; } -- cgit v1.2.3 From 1111cc1e8080b5ff46f5b945acb2f99d6176b2d1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:21:43 +0900 Subject: sh: clkfwk: Introduce a div_mask for variable div types. This plugs in a div_mask for the clock and sets it up for the existing div6/4 cases. This will make it possible to support other div types, as well as share more div6/4 infrastructure. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 9dea32907795..9386bd21c003 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -111,7 +111,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, NULL); - idx = sh_clk_read(clk) & 0x003f; + idx = sh_clk_read(clk) & clk->div_mask; return clk->freq_table[idx].frequency; } @@ -159,7 +159,7 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) return idx; value = sh_clk_read(clk); - value &= ~0x3f; + value &= ~clk->div_mask; value |= idx; sh_clk_write(value, clk); return 0; @@ -185,7 +185,7 @@ static void sh_clk_div6_disable(struct clk *clk) value = sh_clk_read(clk); value |= 0x100; /* stop clock */ - value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ + value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */ sh_clk_write(value, clk); } @@ -295,7 +295,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, &clk->arch_flags); - idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f; + idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; return clk->freq_table[idx].frequency; } @@ -338,7 +338,7 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) return idx; value = sh_clk_read(clk); - value &= ~(0xf << clk->enable_bit); + value &= ~(clk->div_mask << clk->enable_bit); value |= (idx << clk->enable_bit); sh_clk_write(value, clk); -- cgit v1.2.3 From 75f5f8a56e0fdf6d32b3ae9c44c10bc0acd3857c Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:26:01 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_recalc(). This generalizes the div4 recalc routine for use by div6 and others, then makes it the default. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 62 ++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 9386bd21c003..84aeeb8fe013 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -66,11 +66,6 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr) return ret; } -static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) -{ - return clk_rate_table_round(clk, clk->freq_table, rate); -} - /* * Div/mult table lookup helpers */ @@ -84,6 +79,27 @@ static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) return clk_to_div_table(clk)->div_mult_table; } +/* + * Common div ops + */ +static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) +{ + return clk_rate_table_round(clk, clk->freq_table, rate); +} + +static unsigned long sh_clk_div_recalc(struct clk *clk) +{ + struct clk_div_mult_table *table = clk_to_div_mult_table(clk); + unsigned int idx; + + clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, + table, clk->arch_flags ? &clk->arch_flags : NULL); + + idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; + + return clk->freq_table[idx].frequency; +} + /* * div6 support */ @@ -103,19 +119,6 @@ static struct clk_div_table sh_clk_div6_table = { .div_mult_table = &div6_div_mult_table, }; -static unsigned long sh_clk_div6_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = clk_to_div_mult_table(clk); - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, NULL); - - idx = sh_clk_read(clk) & clk->div_mask; - - return clk->freq_table[idx].frequency; -} - static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) { struct clk_div_mult_table *table = clk_to_div_mult_table(clk); @@ -190,7 +193,7 @@ static void sh_clk_div6_disable(struct clk *clk) } static struct sh_clk_ops sh_clk_div6_clk_ops = { - .recalc = sh_clk_div6_recalc, + .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div6_enable, @@ -198,7 +201,7 @@ static struct sh_clk_ops sh_clk_div6_clk_ops = { }; static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { - .recalc = sh_clk_div6_recalc, + .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div6_set_rate, .enable = sh_clk_div6_enable, @@ -287,19 +290,6 @@ int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) /* * div4 support */ -static unsigned long sh_clk_div4_recalc(struct clk *clk) -{ - struct clk_div_mult_table *table = clk_to_div_mult_table(clk); - unsigned int idx; - - clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, - table, &clk->arch_flags); - - idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; - - return clk->freq_table[idx].frequency; -} - static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) { struct clk_div_mult_table *table = clk_to_div_mult_table(clk); @@ -361,13 +351,13 @@ static void sh_clk_div4_disable(struct clk *clk) } static struct sh_clk_ops sh_clk_div4_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, }; static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, @@ -375,7 +365,7 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { }; static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { - .recalc = sh_clk_div4_recalc, + .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div4_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, -- cgit v1.2.3 From 0fa22168e00106797f28b2655aaefd0d16a6e67b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 15:52:10 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_set_rate() Follows the sh_clk_div_recalc() change. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 70 +++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 84aeeb8fe013..29ee5f7072a4 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -100,6 +100,28 @@ static unsigned long sh_clk_div_recalc(struct clk *clk) return clk->freq_table[idx].frequency; } +static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) +{ + struct clk_div_table *dt = clk_to_div_table(clk); + unsigned long value; + int idx; + + idx = clk_rate_table_find(clk, clk->freq_table, rate); + if (idx < 0) + return idx; + + value = sh_clk_read(clk); + value &= ~(clk->div_mask << clk->enable_bit); + value |= (idx << clk->enable_bit); + sh_clk_write(value, clk); + + /* XXX: Should use a post-change notifier */ + if (dt->kick) + dt->kick(clk); + + return 0; +} + /* * div6 support */ @@ -152,28 +174,12 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) -{ - unsigned long value; - int idx; - - idx = clk_rate_table_find(clk, clk->freq_table, rate); - if (idx < 0) - return idx; - - value = sh_clk_read(clk); - value &= ~clk->div_mask; - value |= idx; - sh_clk_write(value, clk); - return 0; -} - static int sh_clk_div6_enable(struct clk *clk) { unsigned long value; int ret; - ret = sh_clk_div6_set_rate(clk, clk->rate); + ret = sh_clk_div_set_rate(clk, clk->rate); if (ret == 0) { value = sh_clk_read(clk); value &= ~0x100; /* clear stop bit to enable clock */ @@ -195,7 +201,7 @@ static void sh_clk_div6_disable(struct clk *clk) static struct sh_clk_ops sh_clk_div6_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div6_set_rate, + .set_rate = sh_clk_div_set_rate, .enable = sh_clk_div6_enable, .disable = sh_clk_div6_disable, }; @@ -203,7 +209,7 @@ static struct sh_clk_ops sh_clk_div6_clk_ops = { static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div6_set_rate, + .set_rate = sh_clk_div_set_rate, .enable = sh_clk_div6_enable, .disable = sh_clk_div6_disable, .set_parent = sh_clk_div6_set_parent, @@ -319,26 +325,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) -{ - struct clk_div_table *dt = clk_to_div_table(clk); - unsigned long value; - int idx = clk_rate_table_find(clk, clk->freq_table, rate); - if (idx < 0) - return idx; - - value = sh_clk_read(clk); - value &= ~(clk->div_mask << clk->enable_bit); - value |= (idx << clk->enable_bit); - sh_clk_write(value, clk); - - /* XXX: Should use a post-change notifier */ - if (dt->kick) - dt->kick(clk); - - return 0; -} - static int sh_clk_div4_enable(struct clk *clk) { sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk); @@ -352,13 +338,13 @@ static void sh_clk_div4_disable(struct clk *clk) static struct sh_clk_ops sh_clk_div4_clk_ops = { .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div4_set_rate, + .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, }; static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div4_set_rate, + .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, .disable = sh_clk_div4_disable, @@ -366,7 +352,7 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div4_set_rate, + .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, .enable = sh_clk_div4_enable, .disable = sh_clk_div4_disable, -- cgit v1.2.3 From 764f4e4e33d18cde4dcaf8a0d860b749c6d6d08b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:34:48 +0900 Subject: sh: clkfwk: Use shared sh_clk_div_enable/disable(). This introduces a new flag for clocks that need to have their divisor ratio set back to their initial mask at disable time to prevent interactivity problems with the clock stop bit (presently div6 only). With this in place it's possible to handle the corner case on top of the div4 op without any particular need for leaving things split out. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 77 +++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 29ee5f7072a4..06537f2b2fb8 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -14,6 +14,8 @@ #include #include +#define CPG_CKSTP_BIT BIT(8) + static unsigned int sh_clk_read(struct clk *clk) { if (clk->flags & CLK_ENABLE_REG_8BIT) @@ -122,6 +124,30 @@ static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) return 0; } +static int sh_clk_div_enable(struct clk *clk) +{ + sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); + return 0; +} + +static void sh_clk_div_disable(struct clk *clk) +{ + unsigned int val; + + val = sh_clk_read(clk); + val |= CPG_CKSTP_BIT; + + /* + * div6 clocks require the divisor field to be non-zero or the + * above CKSTP toggle silently fails. Ensure that the divisor + * array is reset to its initial state on disable. + */ + if (clk->flags & CLK_MASK_DIV_ON_DISABLE) + val |= clk->div_mask; + + sh_clk_write(val, clk); +} + /* * div6 support */ @@ -174,44 +200,20 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div6_enable(struct clk *clk) -{ - unsigned long value; - int ret; - - ret = sh_clk_div_set_rate(clk, clk->rate); - if (ret == 0) { - value = sh_clk_read(clk); - value &= ~0x100; /* clear stop bit to enable clock */ - sh_clk_write(value, clk); - } - return ret; -} - -static void sh_clk_div6_disable(struct clk *clk) -{ - unsigned long value; - - value = sh_clk_read(clk); - value |= 0x100; /* stop clock */ - value |= clk->div_mask; /* VDIV bits must be non-zero, overwrite divider */ - sh_clk_write(value, clk); -} - static struct sh_clk_ops sh_clk_div6_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, }; static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div6_enable, - .disable = sh_clk_div6_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, .set_parent = sh_clk_div6_set_parent, }; @@ -325,17 +327,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int sh_clk_div4_enable(struct clk *clk) -{ - sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk); - return 0; -} - -static void sh_clk_div4_disable(struct clk *clk) -{ - sh_clk_write(sh_clk_read(clk) | (1 << 8), clk); -} - static struct sh_clk_ops sh_clk_div4_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, @@ -346,16 +337,16 @@ static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, }; static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div4_enable, - .disable = sh_clk_div4_disable, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, .set_parent = sh_clk_div4_set_parent, }; -- cgit v1.2.3 From e3c87607731e1a8937567e92a52eedee1bec622d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:43:42 +0900 Subject: sh: clkfwk: Consolidate div6/div4 clk_ops definitions. Everything with the exception of the _reparent ops are now shared, so switch everything over to common types. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 06537f2b2fb8..eeaec796a395 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -148,6 +148,20 @@ static void sh_clk_div_disable(struct clk *clk) sh_clk_write(val, clk); } +static struct sh_clk_ops sh_clk_div_clk_ops = { + .recalc = sh_clk_div_recalc, + .set_rate = sh_clk_div_set_rate, + .round_rate = sh_clk_div_round_rate, +}; + +static struct sh_clk_ops sh_clk_div_enable_clk_ops = { + .recalc = sh_clk_div_recalc, + .set_rate = sh_clk_div_set_rate, + .round_rate = sh_clk_div_round_rate, + .enable = sh_clk_div_enable, + .disable = sh_clk_div_disable, +}; + /* * div6 support */ @@ -200,14 +214,6 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) return 0; } -static struct sh_clk_ops sh_clk_div6_clk_ops = { - .recalc = sh_clk_div_recalc, - .round_rate = sh_clk_div_round_rate, - .set_rate = sh_clk_div_set_rate, - .enable = sh_clk_div_enable, - .disable = sh_clk_div_disable, -}; - static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .round_rate = sh_clk_div_round_rate, @@ -286,7 +292,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, int __init sh_clk_div6_register(struct clk *clks, int nr) { - return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops); + return sh_clk_div6_register_ops(clks, nr, &sh_clk_div_enable_clk_ops); } int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) @@ -327,20 +333,6 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) return 0; } -static struct sh_clk_ops sh_clk_div4_clk_ops = { - .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div_set_rate, - .round_rate = sh_clk_div_round_rate, -}; - -static struct sh_clk_ops sh_clk_div4_enable_clk_ops = { - .recalc = sh_clk_div_recalc, - .set_rate = sh_clk_div_set_rate, - .round_rate = sh_clk_div_round_rate, - .enable = sh_clk_div_enable, - .disable = sh_clk_div_disable, -}; - static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .recalc = sh_clk_div_recalc, .set_rate = sh_clk_div_set_rate, @@ -385,14 +377,14 @@ static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, int __init sh_clk_div4_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); + return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div_clk_ops); } int __init sh_clk_div4_enable_register(struct clk *clks, int nr, struct clk_div4_table *table) { return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div4_enable_clk_ops); + &sh_clk_div_enable_clk_ops); } int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, -- cgit v1.2.3 From 609d7558f232e583a31951c65a6ee43d81c65720 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 25 May 2012 16:55:05 +0900 Subject: sh: clkfwk: Consolidate div clk registration helper. This consolidates the div6/4 versions of the clk registration wrapper. The existing wrappers with their own sh_clk_ops are maintained for API compatability, though in the future it should be possible to be rid of them entirely. Signed-off-by: Paul Mundt --- drivers/sh/clk/cpg.c | 182 +++++++++++++++++++++------------------------------ 1 file changed, 75 insertions(+), 107 deletions(-) (limited to 'drivers') diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index eeaec796a395..07e9fb4f8041 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -162,6 +162,72 @@ static struct sh_clk_ops sh_clk_div_enable_clk_ops = { .disable = sh_clk_div_disable, }; +static int __init sh_clk_init_parent(struct clk *clk) +{ + u32 val; + + if (clk->parent) + return 0; + + if (!clk->parent_table || !clk->parent_num) + return 0; + + if (!clk->src_width) { + pr_err("sh_clk_init_parent: cannot select parent clock\n"); + return -EINVAL; + } + + val = (sh_clk_read(clk) >> clk->src_shift); + val &= (1 << clk->src_width) - 1; + + if (val >= clk->parent_num) { + pr_err("sh_clk_init_parent: parent table size failed\n"); + return -EINVAL; + } + + clk_reparent(clk, clk->parent_table[val]); + if (!clk->parent) { + pr_err("sh_clk_init_parent: unable to set parent"); + return -EINVAL; + } + + return 0; +} + +static int __init sh_clk_div_register_ops(struct clk *clks, int nr, + struct clk_div_table *table, struct sh_clk_ops *ops) +{ + struct clk *clkp; + void *freq_table; + int nr_divs = table->div_mult_table->nr_divisors; + int freq_table_size = sizeof(struct cpufreq_frequency_table); + int ret = 0; + int k; + + freq_table_size *= (nr_divs + 1); + freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); + if (!freq_table) { + pr_err("%s: unable to alloc memory\n", __func__); + return -ENOMEM; + } + + for (k = 0; !ret && (k < nr); k++) { + clkp = clks + k; + + clkp->ops = ops; + clkp->priv = table; + + clkp->freq_table = freq_table + (k * freq_table_size); + clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; + + ret = clk_register(clkp); + if (ret == 0) + ret = sh_clk_init_parent(clkp); + } + + return ret; +} + /* * div6 support */ @@ -223,82 +289,16 @@ static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { .set_parent = sh_clk_div6_set_parent, }; -static int __init sh_clk_init_parent(struct clk *clk) -{ - u32 val; - - if (clk->parent) - return 0; - - if (!clk->parent_table || !clk->parent_num) - return 0; - - if (!clk->src_width) { - pr_err("sh_clk_init_parent: cannot select parent clock\n"); - return -EINVAL; - } - - val = (sh_clk_read(clk) >> clk->src_shift); - val &= (1 << clk->src_width) - 1; - - if (val >= clk->parent_num) { - pr_err("sh_clk_init_parent: parent table size failed\n"); - return -EINVAL; - } - - clk_reparent(clk, clk->parent_table[val]); - if (!clk->parent) { - pr_err("sh_clk_init_parent: unable to set parent"); - return -EINVAL; - } - - return 0; -} - -static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, - struct sh_clk_ops *ops) -{ - struct clk *clkp; - void *freq_table; - struct clk_div_table *table = &sh_clk_div6_table; - int nr_divs = table->div_mult_table->nr_divisors; - int freq_table_size = sizeof(struct cpufreq_frequency_table); - int ret = 0; - int k; - - freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); - if (!freq_table) { - pr_err("sh_clk_div6_register: unable to alloc memory\n"); - return -ENOMEM; - } - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - - clkp->ops = ops; - clkp->priv = table; - clkp->freq_table = freq_table + (k * freq_table_size); - clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; - ret = clk_register(clkp); - if (ret < 0) - break; - - ret = sh_clk_init_parent(clkp); - } - - return ret; -} - int __init sh_clk_div6_register(struct clk *clks, int nr) { - return sh_clk_div6_register_ops(clks, nr, &sh_clk_div_enable_clk_ops); + return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, + &sh_clk_div_enable_clk_ops); } int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) { - return sh_clk_div6_register_ops(clks, nr, - &sh_clk_div6_reparent_clk_ops); + return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, + &sh_clk_div6_reparent_clk_ops); } /* @@ -342,54 +342,22 @@ static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { .set_parent = sh_clk_div4_set_parent, }; -static int __init sh_clk_div4_register_ops(struct clk *clks, int nr, - struct clk_div4_table *table, struct sh_clk_ops *ops) -{ - struct clk *clkp; - void *freq_table; - int nr_divs = table->div_mult_table->nr_divisors; - int freq_table_size = sizeof(struct cpufreq_frequency_table); - int ret = 0; - int k; - - freq_table_size *= (nr_divs + 1); - freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); - if (!freq_table) { - pr_err("sh_clk_div4_register: unable to alloc memory\n"); - return -ENOMEM; - } - - for (k = 0; !ret && (k < nr); k++) { - clkp = clks + k; - - clkp->ops = ops; - clkp->priv = table; - - clkp->freq_table = freq_table + (k * freq_table_size); - clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; - - ret = clk_register(clkp); - } - - return ret; -} - int __init sh_clk_div4_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div_clk_ops); + return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops); } int __init sh_clk_div4_enable_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div_enable_clk_ops); + return sh_clk_div_register_ops(clks, nr, table, + &sh_clk_div_enable_clk_ops); } int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, struct clk_div4_table *table) { - return sh_clk_div4_register_ops(clks, nr, table, - &sh_clk_div4_reparent_clk_ops); + return sh_clk_div_register_ops(clks, nr, table, + &sh_clk_div4_reparent_clk_ops); } -- cgit v1.2.3 From 824174c3b5a962ff706738e36317451a2343355c Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Mon, 28 May 2012 11:51:12 +0530 Subject: ath6kl: Remove unneeded memset in roam related config func No need to clear requested memory after allocating new SKB with help of ath6kl_wmi_get_new_buf(). This clear part is already taken care in ath6kl_wmi_get_new_buf(). Found this on code review. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/wmi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 63dc4fd73c4c..43bce9c8a86f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -743,7 +743,6 @@ int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid) return -ENOMEM; cmd = (struct roam_ctrl_cmd *) skb->data; - memset(cmd, 0, sizeof(*cmd)); memcpy(cmd->info.bssid, bssid, ETH_ALEN); cmd->roam_ctrl = WMI_FORCE_ROAM; @@ -763,7 +762,6 @@ int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode) return -ENOMEM; cmd = (struct roam_ctrl_cmd *) skb->data; - memset(cmd, 0, sizeof(*cmd)); cmd->info.roam_mode = mode; cmd->roam_ctrl = WMI_SET_ROAM_MODE; -- cgit v1.2.3 From df6a7072ac3213229c788913f25779e8ab93c9b5 Mon Sep 17 00:00:00 2001 From: Raja Mani Date: Mon, 28 May 2012 11:51:13 +0530 Subject: ath6kl: Fix typo in htc mbox debug print msg Add missing ZERO (x%x to 0x%x) in the format specifier while printing hex value in htc module. Signed-off-by: Raja Mani Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_mbox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index 2798624d3a9d..cd0e1ba410d6 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -1309,7 +1309,7 @@ static int ath6kl_htc_rx_packet(struct htc_target *target, } ath6kl_dbg(ATH6KL_DBG_HTC, - "htc rx 0x%p hdr x%x len %d mbox 0x%x\n", + "htc rx 0x%p hdr 0x%x len %d mbox 0x%x\n", packet, packet->info.rx.exp_hdr, padded_len, dev->ar->mbox_info.htc_addr); -- cgit v1.2.3 From 67b3f1299ab73259aed5871488188a9c59025a54 Mon Sep 17 00:00:00 2001 From: Kiran Reddy Date: Tue, 29 May 2012 11:12:50 -0700 Subject: ath6kl: separate ht cap for each band In virtual interface structure, for each band separate ht cap is needed. so that one can disable or enable ht capability band wise. This will fix the following issue: 1) Disable 11n from supplicant and start a P2P GO. 2) In beacon frames no HT-CAP IE is seen which is expected. 3) Now remove the P2P GO and kill the supplicant. 4) Beacon stops 5) Now using iw associate to an external AP in 5 GHZ 6) In 5 GHZ no HT IE going in assoc request but when associated in 2.4 GHZ can see HT IES over the air in assoc request. In the code for del_beacon in cfg80211.c,set_ht_cap is being called first for 2.4 GHZ and then for 5 GHZ. When called for the first time for 2.4 GHZ the enable flag will be set to true and so when called for the second time for 5 GHZ it just returns after checking the flag. Also using this one can have different HT capabilities per band (for example one may decide not to use 20/40 in 2.4 GHZ but use it in 5 GHZ). So maintaining a single context is not ok. it is true for even the enable/disable flag and other HT capabilities as well Signed-off-by: Kiran Reddy Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 5 +++-- drivers/net/wireless/ath/ath6kl/core.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 6a934e16ae83..6f20998beceb 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2540,7 +2540,7 @@ void ath6kl_check_wow_status(struct ath6kl *ar) static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band, bool ht_enable) { - struct ath6kl_htcap *htcap = &vif->htcap; + struct ath6kl_htcap *htcap = &vif->htcap[band]; if (htcap->ht_enable == ht_enable) return 0; @@ -3526,7 +3526,8 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; vif->bg_scan_period = 0; - vif->htcap.ht_enable = true; + vif->htcap[IEEE80211_BAND_2GHZ].ht_enable = true; + vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); if (fw_vif_idx != 0) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index b1bc6bc69f2a..17a44fad859b 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -560,7 +560,7 @@ struct ath6kl_vif { struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; struct aggr_info *aggr_cntxt; - struct ath6kl_htcap htcap; + struct ath6kl_htcap htcap[IEEE80211_NUM_BANDS]; struct timer_list disconnect_timer; struct timer_list sched_scan_timer; -- cgit v1.2.3 From 8c9ce606a60e4a0cb447bdc082ce383b96b227b4 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 25 May 2012 16:11:09 -0400 Subject: xen/blkback: Copy id field when doing BLKIF_DISCARD. We weren't copying the id field so when we sent the response back to the frontend (especially with a 64-bit host and 32-bit guest), we ended up using a random value. This lead to the frontend crashing as it would try to pass to __blk_end_request_all a NULL 'struct request' (b/c it would use the 'id' to find the proper 'struct request' in its shadow array) and end up crashing: BUG: unable to handle kernel NULL pointer dereference at 000000e4 IP: [] __blk_end_request_all+0xc/0x40 .. snip.. EIP is at __blk_end_request_all+0xc/0x40 .. snip.. [] blkif_interrupt+0x172/0x330 [xen_blkfront] This fixes the bug by passing in the proper id for the response. Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=824641 CC: stable@kernel.org Tested-by: William Dauchy Acked-by: Stefano Stabellini Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkback/common.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index 773cf27dc23f..9ad3b5ec1dc1 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -257,6 +257,7 @@ static inline void blkif_get_x86_32_req(struct blkif_request *dst, break; case BLKIF_OP_DISCARD: dst->u.discard.flag = src->u.discard.flag; + dst->u.discard.id = src->u.discard.id; dst->u.discard.sector_number = src->u.discard.sector_number; dst->u.discard.nr_sectors = src->u.discard.nr_sectors; break; @@ -287,6 +288,7 @@ static inline void blkif_get_x86_64_req(struct blkif_request *dst, break; case BLKIF_OP_DISCARD: dst->u.discard.flag = src->u.discard.flag; + dst->u.discard.id = src->u.discard.id; dst->u.discard.sector_number = src->u.discard.sector_number; dst->u.discard.nr_sectors = src->u.discard.nr_sectors; break; -- cgit v1.2.3 From 21ff63ad131218048525fbd37d065ce61f03bcbd Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Wed, 25 Apr 2012 16:43:17 +0530 Subject: ARM: OMAP: SmartReflex: Move smartreflex driver to drivers/ After a clean-up of the interfaces the OMAP Smartreflex IP driver is now a generic driver. Move it to drivers/power/avs/. The build is controlled by the following Kconfig options: . CONFIG_POWER_AVS: general knob for Adaptive Voltage Scaling support, . CONFIG_POWER_AVS_OMAP: AVS(Adaptive Voltage Scaling) support on OMAP containing the version 1 or version 2 of the SmartReflex IP, . CONFIG_POWER_AVS_OMAP_CLASS3: Class 3 implementation of Smartreflex. Signed-off-by: Jean Pihet Signed-off-by: J Keerthy Acked-by: Rafael J. Wysocki Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- drivers/power/Kconfig | 2 + drivers/power/Makefile | 1 + drivers/power/avs/Kconfig | 12 + drivers/power/avs/Makefile | 1 + drivers/power/avs/smartreflex.c | 1126 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 1142 insertions(+) create mode 100644 drivers/power/avs/Kconfig create mode 100644 drivers/power/avs/Makefile create mode 100644 drivers/power/avs/smartreflex.c (limited to 'drivers') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 99dc29f2f2f2..d416773771ef 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -308,3 +308,5 @@ config AB8500_BATTERY_THERM_ON_BATCTRL Say Y to enable battery temperature measurements using thermistor connected on BATCTRL ADC. endif # POWER_SUPPLY + +source "drivers/power/avs/Kconfig" diff --git a/drivers/power/Makefile b/drivers/power/Makefile index b6b243416c0e..ee58afb1e71f 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -43,4 +43,5 @@ obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o +obj-$(CONFIG_POWER_AVS) += avs/ obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig new file mode 100644 index 000000000000..18493f7a8f6f --- /dev/null +++ b/drivers/power/avs/Kconfig @@ -0,0 +1,12 @@ +menuconfig POWER_AVS + tristate "Adaptive Voltage Scaling class support" + help + AVS is a power management technique which finely controls the + operating voltage of a device in order to optimize (i.e. reduce) + its power consumption. + At a given operating point the voltage is adapted depending on + static factors (chip manufacturing process) and dynamic factors + (temperature depending performance). + AVS is also called SmartReflex on OMAP devices. + + Say Y here to enable Adaptive Voltage Scaling class support. diff --git a/drivers/power/avs/Makefile b/drivers/power/avs/Makefile new file mode 100644 index 000000000000..0843386a6c19 --- /dev/null +++ b/drivers/power/avs/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_POWER_AVS_OMAP) += smartreflex.o diff --git a/drivers/power/avs/smartreflex.c b/drivers/power/avs/smartreflex.c new file mode 100644 index 000000000000..44efc6e202af --- /dev/null +++ b/drivers/power/avs/smartreflex.c @@ -0,0 +1,1126 @@ +/* + * OMAP SmartReflex Voltage Control + * + * Author: Thara Gopinath + * + * Copyright (C) 2012 Texas Instruments, Inc. + * Thara Gopinath + * + * Copyright (C) 2008 Nokia Corporation + * Kalle Jokiniemi + * + * Copyright (C) 2007 Texas Instruments, Inc. + * Lesly A M + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SMARTREFLEX_NAME_LEN 16 +#define NVALUE_NAME_LEN 40 +#define SR_DISABLE_TIMEOUT 200 + +/* sr_list contains all the instances of smartreflex module */ +static LIST_HEAD(sr_list); + +static struct omap_sr_class_data *sr_class; +static struct omap_sr_pmic_data *sr_pmic_data; +static struct dentry *sr_dbg_dir; + +static inline void sr_write_reg(struct omap_sr *sr, unsigned offset, u32 value) +{ + __raw_writel(value, (sr->base + offset)); +} + +static inline void sr_modify_reg(struct omap_sr *sr, unsigned offset, u32 mask, + u32 value) +{ + u32 reg_val; + + /* + * Smartreflex error config register is special as it contains + * certain status bits which if written a 1 into means a clear + * of those bits. So in order to make sure no accidental write of + * 1 happens to those status bits, do a clear of them in the read + * value. This mean this API doesn't rewrite values in these bits + * if they are currently set, but does allow the caller to write + * those bits. + */ + if (sr->ip_type == SR_TYPE_V1 && offset == ERRCONFIG_V1) + mask |= ERRCONFIG_STATUS_V1_MASK; + else if (sr->ip_type == SR_TYPE_V2 && offset == ERRCONFIG_V2) + mask |= ERRCONFIG_VPBOUNDINTST_V2; + + reg_val = __raw_readl(sr->base + offset); + reg_val &= ~mask; + + value &= mask; + + reg_val |= value; + + __raw_writel(reg_val, (sr->base + offset)); +} + +static inline u32 sr_read_reg(struct omap_sr *sr, unsigned offset) +{ + return __raw_readl(sr->base + offset); +} + +static struct omap_sr *_sr_lookup(struct voltagedomain *voltdm) +{ + struct omap_sr *sr_info; + + if (!voltdm) { + pr_err("%s: Null voltage domain passed!\n", __func__); + return ERR_PTR(-EINVAL); + } + + list_for_each_entry(sr_info, &sr_list, node) { + if (voltdm == sr_info->voltdm) + return sr_info; + } + + return ERR_PTR(-ENODATA); +} + +static irqreturn_t sr_interrupt(int irq, void *data) +{ + struct omap_sr *sr_info = data; + u32 status = 0; + + switch (sr_info->ip_type) { + case SR_TYPE_V1: + /* Read the status bits */ + status = sr_read_reg(sr_info, ERRCONFIG_V1); + + /* Clear them by writing back */ + sr_write_reg(sr_info, ERRCONFIG_V1, status); + break; + case SR_TYPE_V2: + /* Read the status bits */ + status = sr_read_reg(sr_info, IRQSTATUS); + + /* Clear them by writing back */ + sr_write_reg(sr_info, IRQSTATUS, status); + break; + default: + dev_err(&sr_info->pdev->dev, "UNKNOWN IP type %d\n", + sr_info->ip_type); + return IRQ_NONE; + } + + if (sr_class->notify) + sr_class->notify(sr_info, status); + + return IRQ_HANDLED; +} + +static void sr_set_clk_length(struct omap_sr *sr) +{ + struct clk *sys_ck; + u32 sys_clk_speed; + + if (cpu_is_omap34xx()) + sys_ck = clk_get(NULL, "sys_ck"); + else + sys_ck = clk_get(NULL, "sys_clkin_ck"); + + if (IS_ERR(sys_ck)) { + dev_err(&sr->pdev->dev, "%s: unable to get sys clk\n", + __func__); + return; + } + + sys_clk_speed = clk_get_rate(sys_ck); + clk_put(sys_ck); + + switch (sys_clk_speed) { + case 12000000: + sr->clk_length = SRCLKLENGTH_12MHZ_SYSCLK; + break; + case 13000000: + sr->clk_length = SRCLKLENGTH_13MHZ_SYSCLK; + break; + case 19200000: + sr->clk_length = SRCLKLENGTH_19MHZ_SYSCLK; + break; + case 26000000: + sr->clk_length = SRCLKLENGTH_26MHZ_SYSCLK; + break; + case 38400000: + sr->clk_length = SRCLKLENGTH_38MHZ_SYSCLK; + break; + default: + dev_err(&sr->pdev->dev, "%s: Invalid sysclk value: %d\n", + __func__, sys_clk_speed); + break; + } +} + +static void sr_set_regfields(struct omap_sr *sr) +{ + /* + * For time being these values are defined in smartreflex.h + * and populated during init. May be they can be moved to board + * file or pmic specific data structure. In that case these structure + * fields will have to be populated using the pdata or pmic structure. + */ + if (cpu_is_omap34xx() || cpu_is_omap44xx()) { + sr->err_weight = OMAP3430_SR_ERRWEIGHT; + sr->err_maxlimit = OMAP3430_SR_ERRMAXLIMIT; + sr->accum_data = OMAP3430_SR_ACCUMDATA; + if (!(strcmp(sr->name, "smartreflex_mpu_iva"))) { + sr->senn_avgweight = OMAP3430_SR1_SENNAVGWEIGHT; + sr->senp_avgweight = OMAP3430_SR1_SENPAVGWEIGHT; + } else { + sr->senn_avgweight = OMAP3430_SR2_SENNAVGWEIGHT; + sr->senp_avgweight = OMAP3430_SR2_SENPAVGWEIGHT; + } + } +} + +static void sr_start_vddautocomp(struct omap_sr *sr) +{ + if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { + dev_warn(&sr->pdev->dev, + "%s: smartreflex class driver not registered\n", + __func__); + return; + } + + if (!sr_class->enable(sr)) + sr->autocomp_active = true; +} + +static void sr_stop_vddautocomp(struct omap_sr *sr) +{ + if (!sr_class || !(sr_class->disable)) { + dev_warn(&sr->pdev->dev, + "%s: smartreflex class driver not registered\n", + __func__); + return; + } + + if (sr->autocomp_active) { + sr_class->disable(sr, 1); + sr->autocomp_active = false; + } +} + +/* + * This function handles the intializations which have to be done + * only when both sr device and class driver regiter has + * completed. This will be attempted to be called from both sr class + * driver register and sr device intializtion API's. Only one call + * will ultimately succeed. + * + * Currently this function registers interrupt handler for a particular SR + * if smartreflex class driver is already registered and has + * requested for interrupts and the SR interrupt line in present. + */ +static int sr_late_init(struct omap_sr *sr_info) +{ + struct omap_sr_data *pdata = sr_info->pdev->dev.platform_data; + struct resource *mem; + int ret = 0; + + if (sr_class->notify && sr_class->notify_flags && sr_info->irq) { + ret = request_irq(sr_info->irq, sr_interrupt, + 0, sr_info->name, sr_info); + if (ret) + goto error; + disable_irq(sr_info->irq); + } + + if (pdata && pdata->enable_on_init) + sr_start_vddautocomp(sr_info); + + return ret; + +error: + iounmap(sr_info->base); + mem = platform_get_resource(sr_info->pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + list_del(&sr_info->node); + dev_err(&sr_info->pdev->dev, "%s: ERROR in registering" + "interrupt handler. Smartreflex will" + "not function as desired\n", __func__); + kfree(sr_info); + + return ret; +} + +static void sr_v1_disable(struct omap_sr *sr) +{ + int timeout = 0; + int errconf_val = ERRCONFIG_MCUACCUMINTST | ERRCONFIG_MCUVALIDINTST | + ERRCONFIG_MCUBOUNDINTST; + + /* Enable MCUDisableAcknowledge interrupt */ + sr_modify_reg(sr, ERRCONFIG_V1, + ERRCONFIG_MCUDISACKINTEN, ERRCONFIG_MCUDISACKINTEN); + + /* SRCONFIG - disable SR */ + sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0); + + /* Disable all other SR interrupts and clear the status as needed */ + if (sr_read_reg(sr, ERRCONFIG_V1) & ERRCONFIG_VPBOUNDINTST_V1) + errconf_val |= ERRCONFIG_VPBOUNDINTST_V1; + sr_modify_reg(sr, ERRCONFIG_V1, + (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN | + ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_VPBOUNDINTEN_V1), + errconf_val); + + /* + * Wait for SR to be disabled. + * wait until ERRCONFIG.MCUDISACKINTST = 1. Typical latency is 1us. + */ + sr_test_cond_timeout((sr_read_reg(sr, ERRCONFIG_V1) & + ERRCONFIG_MCUDISACKINTST), SR_DISABLE_TIMEOUT, + timeout); + + if (timeout >= SR_DISABLE_TIMEOUT) + dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n", + __func__); + + /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ + sr_modify_reg(sr, ERRCONFIG_V1, ERRCONFIG_MCUDISACKINTEN, + ERRCONFIG_MCUDISACKINTST); +} + +static void sr_v2_disable(struct omap_sr *sr) +{ + int timeout = 0; + + /* Enable MCUDisableAcknowledge interrupt */ + sr_write_reg(sr, IRQENABLE_SET, IRQENABLE_MCUDISABLEACKINT); + + /* SRCONFIG - disable SR */ + sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, 0x0); + + /* + * Disable all other SR interrupts and clear the status + * write to status register ONLY on need basis - only if status + * is set. + */ + if (sr_read_reg(sr, ERRCONFIG_V2) & ERRCONFIG_VPBOUNDINTST_V2) + sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, + ERRCONFIG_VPBOUNDINTST_V2); + else + sr_modify_reg(sr, ERRCONFIG_V2, ERRCONFIG_VPBOUNDINTEN_V2, + 0x0); + sr_write_reg(sr, IRQENABLE_CLR, (IRQENABLE_MCUACCUMINT | + IRQENABLE_MCUVALIDINT | + IRQENABLE_MCUBOUNDSINT)); + sr_write_reg(sr, IRQSTATUS, (IRQSTATUS_MCUACCUMINT | + IRQSTATUS_MCVALIDINT | + IRQSTATUS_MCBOUNDSINT)); + + /* + * Wait for SR to be disabled. + * wait until IRQSTATUS.MCUDISACKINTST = 1. Typical latency is 1us. + */ + sr_test_cond_timeout((sr_read_reg(sr, IRQSTATUS) & + IRQSTATUS_MCUDISABLEACKINT), SR_DISABLE_TIMEOUT, + timeout); + + if (timeout >= SR_DISABLE_TIMEOUT) + dev_warn(&sr->pdev->dev, "%s: Smartreflex disable timedout\n", + __func__); + + /* Disable MCUDisableAcknowledge interrupt & clear pending interrupt */ + sr_write_reg(sr, IRQENABLE_CLR, IRQENABLE_MCUDISABLEACKINT); + sr_write_reg(sr, IRQSTATUS, IRQSTATUS_MCUDISABLEACKINT); +} + +static struct omap_sr_nvalue_table *sr_retrieve_nvalue_row( + struct omap_sr *sr, u32 efuse_offs) +{ + int i; + + if (!sr->nvalue_table) { + dev_warn(&sr->pdev->dev, "%s: Missing ntarget value table\n", + __func__); + return NULL; + } + + for (i = 0; i < sr->nvalue_count; i++) { + if (sr->nvalue_table[i].efuse_offs == efuse_offs) + return &sr->nvalue_table[i]; + } + + return NULL; +} + +/* Public Functions */ + +/** + * sr_configure_errgen() - Configures the smrtreflex to perform AVS using the + * error generator module. + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the smartreflex class driver to + * configure the error generator module inside the smartreflex module. + * SR settings if using the ERROR module inside Smartreflex. + * SR CLASS 3 by default uses only the ERROR module where as + * SR CLASS 2 can choose between ERROR module and MINMAXAVG + * module. Returns 0 on success and error value in case of failure. + */ +int sr_configure_errgen(struct voltagedomain *voltdm) +{ + u32 sr_config, sr_errconfig, errconfig_offs; + u32 vpboundint_en, vpboundint_st; + u32 senp_en = 0, senn_en = 0; + u8 senp_shift, senn_shift; + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return PTR_ERR(sr); + } + + if (!sr->clk_length) + sr_set_clk_length(sr); + + senp_en = sr->senp_mod; + senn_en = sr->senn_mod; + + sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) | + SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN; + + switch (sr->ip_type) { + case SR_TYPE_V1: + sr_config |= SRCONFIG_DELAYCTRL; + senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT; + senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT; + errconfig_offs = ERRCONFIG_V1; + vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1; + vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; + break; + case SR_TYPE_V2: + senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT; + senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT; + errconfig_offs = ERRCONFIG_V2; + vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2; + vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; + break; + default: + dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" + "module without specifying the ip\n", __func__); + return -EINVAL; + } + + sr_config |= ((senn_en << senn_shift) | (senp_en << senp_shift)); + sr_write_reg(sr, SRCONFIG, sr_config); + sr_errconfig = (sr->err_weight << ERRCONFIG_ERRWEIGHT_SHIFT) | + (sr->err_maxlimit << ERRCONFIG_ERRMAXLIMIT_SHIFT) | + (sr->err_minlimit << ERRCONFIG_ERRMINLIMIT_SHIFT); + sr_modify_reg(sr, errconfig_offs, (SR_ERRWEIGHT_MASK | + SR_ERRMAXLIMIT_MASK | SR_ERRMINLIMIT_MASK), + sr_errconfig); + + /* Enabling the interrupts if the ERROR module is used */ + sr_modify_reg(sr, errconfig_offs, (vpboundint_en | vpboundint_st), + vpboundint_en); + + return 0; +} + +/** + * sr_disable_errgen() - Disables SmartReflex AVS module's errgen component + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the smartreflex class driver to + * disable the error generator module inside the smartreflex module. + * + * Returns 0 on success and error value in case of failure. + */ +int sr_disable_errgen(struct voltagedomain *voltdm) +{ + u32 errconfig_offs; + u32 vpboundint_en, vpboundint_st; + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return PTR_ERR(sr); + } + + switch (sr->ip_type) { + case SR_TYPE_V1: + errconfig_offs = ERRCONFIG_V1; + vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V1; + vpboundint_st = ERRCONFIG_VPBOUNDINTST_V1; + break; + case SR_TYPE_V2: + errconfig_offs = ERRCONFIG_V2; + vpboundint_en = ERRCONFIG_VPBOUNDINTEN_V2; + vpboundint_st = ERRCONFIG_VPBOUNDINTST_V2; + break; + default: + dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" + "module without specifying the ip\n", __func__); + return -EINVAL; + } + + /* Disable the interrupts of ERROR module */ + sr_modify_reg(sr, errconfig_offs, vpboundint_en | vpboundint_st, 0); + + /* Disable the Sensor and errorgen */ + sr_modify_reg(sr, SRCONFIG, SRCONFIG_SENENABLE | SRCONFIG_ERRGEN_EN, 0); + + return 0; +} + +/** + * sr_configure_minmax() - Configures the smrtreflex to perform AVS using the + * minmaxavg module. + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the smartreflex class driver to + * configure the minmaxavg module inside the smartreflex module. + * SR settings if using the ERROR module inside Smartreflex. + * SR CLASS 3 by default uses only the ERROR module where as + * SR CLASS 2 can choose between ERROR module and MINMAXAVG + * module. Returns 0 on success and error value in case of failure. + */ +int sr_configure_minmax(struct voltagedomain *voltdm) +{ + u32 sr_config, sr_avgwt; + u32 senp_en = 0, senn_en = 0; + u8 senp_shift, senn_shift; + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return PTR_ERR(sr); + } + + if (!sr->clk_length) + sr_set_clk_length(sr); + + senp_en = sr->senp_mod; + senn_en = sr->senn_mod; + + sr_config = (sr->clk_length << SRCONFIG_SRCLKLENGTH_SHIFT) | + SRCONFIG_SENENABLE | + (sr->accum_data << SRCONFIG_ACCUMDATA_SHIFT); + + switch (sr->ip_type) { + case SR_TYPE_V1: + sr_config |= SRCONFIG_DELAYCTRL; + senn_shift = SRCONFIG_SENNENABLE_V1_SHIFT; + senp_shift = SRCONFIG_SENPENABLE_V1_SHIFT; + break; + case SR_TYPE_V2: + senn_shift = SRCONFIG_SENNENABLE_V2_SHIFT; + senp_shift = SRCONFIG_SENPENABLE_V2_SHIFT; + break; + default: + dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" + "module without specifying the ip\n", __func__); + return -EINVAL; + } + + sr_config |= ((senn_en << senn_shift) | (senp_en << senp_shift)); + sr_write_reg(sr, SRCONFIG, sr_config); + sr_avgwt = (sr->senp_avgweight << AVGWEIGHT_SENPAVGWEIGHT_SHIFT) | + (sr->senn_avgweight << AVGWEIGHT_SENNAVGWEIGHT_SHIFT); + sr_write_reg(sr, AVGWEIGHT, sr_avgwt); + + /* + * Enabling the interrupts if MINMAXAVG module is used. + * TODO: check if all the interrupts are mandatory + */ + switch (sr->ip_type) { + case SR_TYPE_V1: + sr_modify_reg(sr, ERRCONFIG_V1, + (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUVALIDINTEN | + ERRCONFIG_MCUBOUNDINTEN), + (ERRCONFIG_MCUACCUMINTEN | ERRCONFIG_MCUACCUMINTST | + ERRCONFIG_MCUVALIDINTEN | ERRCONFIG_MCUVALIDINTST | + ERRCONFIG_MCUBOUNDINTEN | ERRCONFIG_MCUBOUNDINTST)); + break; + case SR_TYPE_V2: + sr_write_reg(sr, IRQSTATUS, + IRQSTATUS_MCUACCUMINT | IRQSTATUS_MCVALIDINT | + IRQSTATUS_MCBOUNDSINT | IRQSTATUS_MCUDISABLEACKINT); + sr_write_reg(sr, IRQENABLE_SET, + IRQENABLE_MCUACCUMINT | IRQENABLE_MCUVALIDINT | + IRQENABLE_MCUBOUNDSINT | IRQENABLE_MCUDISABLEACKINT); + break; + default: + dev_err(&sr->pdev->dev, "%s: Trying to Configure smartreflex" + "module without specifying the ip\n", __func__); + return -EINVAL; + } + + return 0; +} + +/** + * sr_enable() - Enables the smartreflex module. + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * @volt: The voltage at which the Voltage domain associated with + * the smartreflex module is operating at. + * This is required only to program the correct Ntarget value. + * + * This API is to be called from the smartreflex class driver to + * enable a smartreflex module. Returns 0 on success. Returns error + * value if the voltage passed is wrong or if ntarget value is wrong. + */ +int sr_enable(struct voltagedomain *voltdm, unsigned long volt) +{ + struct omap_volt_data *volt_data; + struct omap_sr *sr = _sr_lookup(voltdm); + struct omap_sr_nvalue_table *nvalue_row; + int ret; + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return PTR_ERR(sr); + } + + volt_data = omap_voltage_get_voltdata(sr->voltdm, volt); + + if (IS_ERR(volt_data)) { + dev_warn(&sr->pdev->dev, "%s: Unable to get voltage table" + "for nominal voltage %ld\n", __func__, volt); + return PTR_ERR(volt_data); + } + + nvalue_row = sr_retrieve_nvalue_row(sr, volt_data->sr_efuse_offs); + + if (!nvalue_row) { + dev_warn(&sr->pdev->dev, "%s: failure getting SR data for this voltage %ld\n", + __func__, volt); + return -ENODATA; + } + + /* errminlimit is opp dependent and hence linked to voltage */ + sr->err_minlimit = nvalue_row->errminlimit; + + pm_runtime_get_sync(&sr->pdev->dev); + + /* Check if SR is already enabled. If yes do nothing */ + if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) + return 0; + + /* Configure SR */ + ret = sr_class->configure(sr); + if (ret) + return ret; + + sr_write_reg(sr, NVALUERECIPROCAL, nvalue_row->nvalue); + + /* SRCONFIG - enable SR */ + sr_modify_reg(sr, SRCONFIG, SRCONFIG_SRENABLE, SRCONFIG_SRENABLE); + return 0; +} + +/** + * sr_disable() - Disables the smartreflex module. + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the smartreflex class driver to + * disable a smartreflex module. + */ +void sr_disable(struct voltagedomain *voltdm) +{ + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return; + } + + /* Check if SR clocks are already disabled. If yes do nothing */ + if (pm_runtime_suspended(&sr->pdev->dev)) + return; + + /* + * Disable SR if only it is indeed enabled. Else just + * disable the clocks. + */ + if (sr_read_reg(sr, SRCONFIG) & SRCONFIG_SRENABLE) { + switch (sr->ip_type) { + case SR_TYPE_V1: + sr_v1_disable(sr); + break; + case SR_TYPE_V2: + sr_v2_disable(sr); + break; + default: + dev_err(&sr->pdev->dev, "UNKNOWN IP type %d\n", + sr->ip_type); + } + } + + pm_runtime_put_sync_suspend(&sr->pdev->dev); +} + +/** + * sr_register_class() - API to register a smartreflex class parameters. + * @class_data: The structure containing various sr class specific data. + * + * This API is to be called by the smartreflex class driver to register itself + * with the smartreflex driver during init. Returns 0 on success else the + * error value. + */ +int sr_register_class(struct omap_sr_class_data *class_data) +{ + struct omap_sr *sr_info; + + if (!class_data) { + pr_warning("%s:, Smartreflex class data passed is NULL\n", + __func__); + return -EINVAL; + } + + if (sr_class) { + pr_warning("%s: Smartreflex class driver already registered\n", + __func__); + return -EBUSY; + } + + sr_class = class_data; + + /* + * Call into late init to do intializations that require + * both sr driver and sr class driver to be initiallized. + */ + list_for_each_entry(sr_info, &sr_list, node) + sr_late_init(sr_info); + + return 0; +} + +/** + * omap_sr_enable() - API to enable SR clocks and to call into the + * registered smartreflex class enable API. + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the kernel in order to enable + * a particular smartreflex module. This API will do the initial + * configurations to turn on the smartreflex module and in turn call + * into the registered smartreflex class enable API. + */ +void omap_sr_enable(struct voltagedomain *voltdm) +{ + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return; + } + + if (!sr->autocomp_active) + return; + + if (!sr_class || !(sr_class->enable) || !(sr_class->configure)) { + dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" + "registered\n", __func__); + return; + } + + sr_class->enable(sr); +} + +/** + * omap_sr_disable() - API to disable SR without resetting the voltage + * processor voltage + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the kernel in order to disable + * a particular smartreflex module. This API will in turn call + * into the registered smartreflex class disable API. This API will tell + * the smartreflex class disable not to reset the VP voltage after + * disabling smartreflex. + */ +void omap_sr_disable(struct voltagedomain *voltdm) +{ + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return; + } + + if (!sr->autocomp_active) + return; + + if (!sr_class || !(sr_class->disable)) { + dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" + "registered\n", __func__); + return; + } + + sr_class->disable(sr, 0); +} + +/** + * omap_sr_disable_reset_volt() - API to disable SR and reset the + * voltage processor voltage + * @voltdm: VDD pointer to which the SR module to be configured belongs to. + * + * This API is to be called from the kernel in order to disable + * a particular smartreflex module. This API will in turn call + * into the registered smartreflex class disable API. This API will tell + * the smartreflex class disable to reset the VP voltage after + * disabling smartreflex. + */ +void omap_sr_disable_reset_volt(struct voltagedomain *voltdm) +{ + struct omap_sr *sr = _sr_lookup(voltdm); + + if (IS_ERR(sr)) { + pr_warning("%s: omap_sr struct for voltdm not found\n", __func__); + return; + } + + if (!sr->autocomp_active) + return; + + if (!sr_class || !(sr_class->disable)) { + dev_warn(&sr->pdev->dev, "%s: smartreflex class driver not" + "registered\n", __func__); + return; + } + + sr_class->disable(sr, 1); +} + +/** + * omap_sr_register_pmic() - API to register pmic specific info. + * @pmic_data: The structure containing pmic specific data. + * + * This API is to be called from the PMIC specific code to register with + * smartreflex driver pmic specific info. Currently the only info required + * is the smartreflex init on the PMIC side. + */ +void omap_sr_register_pmic(struct omap_sr_pmic_data *pmic_data) +{ + if (!pmic_data) { + pr_warning("%s: Trying to register NULL PMIC data structure" + "with smartreflex\n", __func__); + return; + } + + sr_pmic_data = pmic_data; +} + +/* PM Debug FS entries to enable and disable smartreflex. */ +static int omap_sr_autocomp_show(void *data, u64 *val) +{ + struct omap_sr *sr_info = data; + + if (!sr_info) { + pr_warning("%s: omap_sr struct not found\n", __func__); + return -EINVAL; + } + + *val = sr_info->autocomp_active; + + return 0; +} + +static int omap_sr_autocomp_store(void *data, u64 val) +{ + struct omap_sr *sr_info = data; + + if (!sr_info) { + pr_warning("%s: omap_sr struct not found\n", __func__); + return -EINVAL; + } + + /* Sanity check */ + if (val > 1) { + pr_warning("%s: Invalid argument %lld\n", __func__, val); + return -EINVAL; + } + + /* control enable/disable only if there is a delta in value */ + if (sr_info->autocomp_active != val) { + if (!val) + sr_stop_vddautocomp(sr_info); + else + sr_start_vddautocomp(sr_info); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(pm_sr_fops, omap_sr_autocomp_show, + omap_sr_autocomp_store, "%llu\n"); + +static int __init omap_sr_probe(struct platform_device *pdev) +{ + struct omap_sr *sr_info; + struct omap_sr_data *pdata = pdev->dev.platform_data; + struct resource *mem, *irq; + struct dentry *nvalue_dir; + int i, ret = 0; + + sr_info = kzalloc(sizeof(struct omap_sr), GFP_KERNEL); + if (!sr_info) { + dev_err(&pdev->dev, "%s: unable to allocate sr_info\n", + __func__); + return -ENOMEM; + } + + platform_set_drvdata(pdev, sr_info); + + if (!pdata) { + dev_err(&pdev->dev, "%s: platform data missing\n", __func__); + ret = -EINVAL; + goto err_free_devinfo; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "%s: no mem resource\n", __func__); + ret = -ENODEV; + goto err_free_devinfo; + } + + mem = request_mem_region(mem->start, resource_size(mem), + dev_name(&pdev->dev)); + if (!mem) { + dev_err(&pdev->dev, "%s: no mem region\n", __func__); + ret = -EBUSY; + goto err_free_devinfo; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + + pm_runtime_enable(&pdev->dev); + pm_runtime_irq_safe(&pdev->dev); + + sr_info->name = kasprintf(GFP_KERNEL, "%s", pdata->name); + if (!sr_info->name) { + dev_err(&pdev->dev, "%s: Unable to alloc SR instance name\n", + __func__); + ret = -ENOMEM; + goto err_release_region; + } + + sr_info->pdev = pdev; + sr_info->srid = pdev->id; + sr_info->voltdm = pdata->voltdm; + sr_info->nvalue_table = pdata->nvalue_table; + sr_info->nvalue_count = pdata->nvalue_count; + sr_info->senn_mod = pdata->senn_mod; + sr_info->senp_mod = pdata->senp_mod; + sr_info->autocomp_active = false; + sr_info->ip_type = pdata->ip_type; + sr_info->base = ioremap(mem->start, resource_size(mem)); + if (!sr_info->base) { + dev_err(&pdev->dev, "%s: ioremap fail\n", __func__); + ret = -ENOMEM; + goto err_release_region; + } + + if (irq) + sr_info->irq = irq->start; + + sr_set_clk_length(sr_info); + sr_set_regfields(sr_info); + + list_add(&sr_info->node, &sr_list); + + /* + * Call into late init to do intializations that require + * both sr driver and sr class driver to be initiallized. + */ + if (sr_class) { + ret = sr_late_init(sr_info); + if (ret) { + pr_warning("%s: Error in SR late init\n", __func__); + goto err_iounmap; + } + } + + dev_info(&pdev->dev, "%s: SmartReflex driver initialized\n", __func__); + if (!sr_dbg_dir) { + sr_dbg_dir = debugfs_create_dir("smartreflex", NULL); + if (IS_ERR_OR_NULL(sr_dbg_dir)) { + ret = PTR_ERR(sr_dbg_dir); + pr_err("%s:sr debugfs dir creation failed(%d)\n", + __func__, ret); + goto err_iounmap; + } + } + + sr_info->dbg_dir = debugfs_create_dir(sr_info->name, sr_dbg_dir); + if (IS_ERR_OR_NULL(sr_info->dbg_dir)) { + dev_err(&pdev->dev, "%s: Unable to create debugfs directory\n", + __func__); + ret = PTR_ERR(sr_info->dbg_dir); + goto err_free_name; + } + + (void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR, + sr_info->dbg_dir, (void *)sr_info, &pm_sr_fops); + (void) debugfs_create_x32("errweight", S_IRUGO, sr_info->dbg_dir, + &sr_info->err_weight); + (void) debugfs_create_x32("errmaxlimit", S_IRUGO, sr_info->dbg_dir, + &sr_info->err_maxlimit); + + nvalue_dir = debugfs_create_dir("nvalue", sr_info->dbg_dir); + if (IS_ERR_OR_NULL(nvalue_dir)) { + dev_err(&pdev->dev, "%s: Unable to create debugfs directory" + "for n-values\n", __func__); + ret = PTR_ERR(nvalue_dir); + goto err_debugfs; + } + + if (sr_info->nvalue_count == 0 || !sr_info->nvalue_table) { + dev_warn(&pdev->dev, "%s: %s: No Voltage table for the corresponding vdd. Cannot create debugfs entries for n-values\n", + __func__, sr_info->name); + + ret = -ENODATA; + goto err_debugfs; + } + + for (i = 0; i < sr_info->nvalue_count; i++) { + char name[NVALUE_NAME_LEN + 1]; + + snprintf(name, sizeof(name), "volt_%lu", + sr_info->nvalue_table[i].volt_nominal); + (void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir, + &(sr_info->nvalue_table[i].nvalue)); + snprintf(name, sizeof(name), "errminlimit_%lu", + sr_info->nvalue_table[i].volt_nominal); + (void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir, + &(sr_info->nvalue_table[i].errminlimit)); + + } + + return ret; + +err_debugfs: + debugfs_remove_recursive(sr_info->dbg_dir); +err_free_name: + kfree(sr_info->name); +err_iounmap: + list_del(&sr_info->node); + iounmap(sr_info->base); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); +err_free_devinfo: + kfree(sr_info); + + return ret; +} + +static int __devexit omap_sr_remove(struct platform_device *pdev) +{ + struct omap_sr_data *pdata = pdev->dev.platform_data; + struct omap_sr *sr_info; + struct resource *mem; + + if (!pdata) { + dev_err(&pdev->dev, "%s: platform data missing\n", __func__); + return -EINVAL; + } + + sr_info = _sr_lookup(pdata->voltdm); + if (IS_ERR(sr_info)) { + dev_warn(&pdev->dev, "%s: omap_sr struct not found\n", + __func__); + return PTR_ERR(sr_info); + } + + if (sr_info->autocomp_active) + sr_stop_vddautocomp(sr_info); + if (sr_info->dbg_dir) + debugfs_remove_recursive(sr_info->dbg_dir); + + list_del(&sr_info->node); + iounmap(sr_info->base); + kfree(sr_info->name); + kfree(sr_info); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + + return 0; +} + +static void __devexit omap_sr_shutdown(struct platform_device *pdev) +{ + struct omap_sr_data *pdata = pdev->dev.platform_data; + struct omap_sr *sr_info; + + if (!pdata) { + dev_err(&pdev->dev, "%s: platform data missing\n", __func__); + return; + } + + sr_info = _sr_lookup(pdata->voltdm); + if (IS_ERR(sr_info)) { + dev_warn(&pdev->dev, "%s: omap_sr struct not found\n", + __func__); + return; + } + + if (sr_info->autocomp_active) + sr_stop_vddautocomp(sr_info); + + return; +} + +static struct platform_driver smartreflex_driver = { + .remove = __devexit_p(omap_sr_remove), + .shutdown = __devexit_p(omap_sr_shutdown), + .driver = { + .name = "smartreflex", + }, +}; + +static int __init sr_init(void) +{ + int ret = 0; + + /* + * sr_init is a late init. If by then a pmic specific API is not + * registered either there is no need for anything to be done on + * the PMIC side or somebody has forgotten to register a PMIC + * handler. Warn for the second condition. + */ + if (sr_pmic_data && sr_pmic_data->sr_pmic_init) + sr_pmic_data->sr_pmic_init(); + else + pr_warning("%s: No PMIC hook to init smartreflex\n", __func__); + + ret = platform_driver_probe(&smartreflex_driver, omap_sr_probe); + if (ret) { + pr_err("%s: platform driver register failed for SR\n", + __func__); + return ret; + } + + return 0; +} +late_initcall(sr_init); + +static void __exit sr_exit(void) +{ + platform_driver_unregister(&smartreflex_driver); +} +module_exit(sr_exit); + +MODULE_DESCRIPTION("OMAP Smartreflex Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Texas Instruments Inc"); -- cgit v1.2.3 From 92a9f14b8b8e5ade5b49bcd6b95fc05f85a39e90 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 30 May 2012 21:16:16 +0100 Subject: Fix comment typo multipy -> multiply Signed-off-by: Ralf Baechle Signed-off-by: Jiri Kosina --- drivers/input/misc/cma3000_d0x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c index a3735a01e9fd..df9b756594f8 100644 --- a/drivers/input/misc/cma3000_d0x.c +++ b/drivers/input/misc/cma3000_d0x.c @@ -58,7 +58,7 @@ /* * Bit weights in mg for bit 0, other bits need - * multipy factor 2^n. Eight bit is the sign bit. + * multiply factor 2^n. Eight bit is the sign bit. */ #define BIT_TO_2G 18 #define BIT_TO_8G 71 -- cgit v1.2.3 From 141eba2e006dd8145bed2e49fae3de5af65ab9b0 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 24 May 2012 10:47:26 -0600 Subject: regmap: allow busses to request formatting with specific endianness Add a field to struct regmap_bus that allows bus drivers to request that register addresses and values be formatted with a specific endianness. The default endianness is unchanged from current operation: Big. Implement native endian formatting/parsing for 16- and 32-bit values. This will be enough to support regmap-mmio.c. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 101 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0bcda488f11c..1cf721421bec 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -119,13 +119,19 @@ static void regmap_format_8(void *buf, unsigned int val, unsigned int shift) b[0] = val << shift; } -static void regmap_format_16(void *buf, unsigned int val, unsigned int shift) +static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift) { __be16 *b = buf; b[0] = cpu_to_be16(val << shift); } +static void regmap_format_16_native(void *buf, unsigned int val, + unsigned int shift) +{ + *(u16 *)buf = val << shift; +} + static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) { u8 *b = buf; @@ -137,13 +143,19 @@ static void regmap_format_24(void *buf, unsigned int val, unsigned int shift) b[2] = val; } -static void regmap_format_32(void *buf, unsigned int val, unsigned int shift) +static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift) { __be32 *b = buf; b[0] = cpu_to_be32(val << shift); } +static void regmap_format_32_native(void *buf, unsigned int val, + unsigned int shift) +{ + *(u32 *)buf = val << shift; +} + static unsigned int regmap_parse_8(void *buf) { u8 *b = buf; @@ -151,7 +163,7 @@ static unsigned int regmap_parse_8(void *buf) return b[0]; } -static unsigned int regmap_parse_16(void *buf) +static unsigned int regmap_parse_16_be(void *buf) { __be16 *b = buf; @@ -160,6 +172,11 @@ static unsigned int regmap_parse_16(void *buf) return b[0]; } +static unsigned int regmap_parse_16_native(void *buf) +{ + return *(u16 *)buf; +} + static unsigned int regmap_parse_24(void *buf) { u8 *b = buf; @@ -170,7 +187,7 @@ static unsigned int regmap_parse_24(void *buf) return ret; } -static unsigned int regmap_parse_32(void *buf) +static unsigned int regmap_parse_32_be(void *buf) { __be32 *b = buf; @@ -179,6 +196,11 @@ static unsigned int regmap_parse_32(void *buf) return b[0]; } +static unsigned int regmap_parse_32_native(void *buf) +{ + return *(u32 *)buf; +} + static void regmap_lock_mutex(struct regmap *map) { mutex_lock(&map->mutex); @@ -227,6 +249,7 @@ struct regmap *regmap_init(struct device *dev, { struct regmap *map, **m; int ret = -EINVAL; + enum regmap_endian reg_endian, val_endian; if (!bus || !config) goto err; @@ -275,6 +298,18 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } + reg_endian = config->reg_format_endian; + if (reg_endian == REGMAP_ENDIAN_DEFAULT) + reg_endian = bus->reg_format_endian_default; + if (reg_endian == REGMAP_ENDIAN_DEFAULT) + reg_endian = REGMAP_ENDIAN_BIG; + + val_endian = config->val_format_endian; + if (val_endian == REGMAP_ENDIAN_DEFAULT) + val_endian = bus->val_format_endian_default; + if (val_endian == REGMAP_ENDIAN_DEFAULT) + val_endian = REGMAP_ENDIAN_BIG; + switch (config->reg_bits + map->reg_shift) { case 2: switch (config->val_bits) { @@ -321,11 +356,29 @@ struct regmap *regmap_init(struct device *dev, break; case 16: - map->format.format_reg = regmap_format_16; + switch (reg_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_reg = regmap_format_16_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_reg = regmap_format_16_native; + break; + default: + goto err_map; + } break; case 32: - map->format.format_reg = regmap_format_32; + switch (reg_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_reg = regmap_format_32_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_reg = regmap_format_32_native; + break; + default: + goto err_map; + } break; default: @@ -338,21 +391,47 @@ struct regmap *regmap_init(struct device *dev, map->format.parse_val = regmap_parse_8; break; case 16: - map->format.format_val = regmap_format_16; - map->format.parse_val = regmap_parse_16; + switch (val_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_val = regmap_format_16_be; + map->format.parse_val = regmap_parse_16_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_val = regmap_format_16_native; + map->format.parse_val = regmap_parse_16_native; + break; + default: + goto err_map; + } break; case 24: + if (val_endian != REGMAP_ENDIAN_BIG) + goto err_map; map->format.format_val = regmap_format_24; map->format.parse_val = regmap_parse_24; break; case 32: - map->format.format_val = regmap_format_32; - map->format.parse_val = regmap_parse_32; + switch (val_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_val = regmap_format_32_be; + map->format.parse_val = regmap_parse_32_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_val = regmap_format_32_native; + map->format.parse_val = regmap_parse_32_native; + break; + default: + goto err_map; + } break; } - if (map->format.format_write) + if (map->format.format_write) { + if ((reg_endian != REGMAP_ENDIAN_BIG) || + (val_endian != REGMAP_ENDIAN_BIG)) + goto err_map; map->use_single_rw = true; + } if (!map->format.format_write && !(map->format.format_reg && map->format.format_val)) -- cgit v1.2.3 From 6a55244e897d32952832a67cb35cfbfa3f722c50 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 24 May 2012 10:47:27 -0600 Subject: regmap: mmio: request native endian formatting This will avoid the regmap core converting all addresses and values into big endian, only for the mmio bus driver to have to convert them back to native endian. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index febd6de6c8ac..eec86639cac4 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -37,7 +37,7 @@ static int regmap_mmio_gather_write(void *context, BUG_ON(reg_size != 4); - offset = be32_to_cpup(reg); + offset = *(u32 *)reg; while (val_size) { switch (ctx->val_bytes) { @@ -45,14 +45,14 @@ static int regmap_mmio_gather_write(void *context, writeb(*(u8 *)val, ctx->regs + offset); break; case 2: - writew(be16_to_cpup(val), ctx->regs + offset); + writew(*(u16 *)val, ctx->regs + offset); break; case 4: - writel(be32_to_cpup(val), ctx->regs + offset); + writel(*(u32 *)val, ctx->regs + offset); break; #ifdef CONFIG_64BIT case 8: - writeq(be64_to_cpup(val), ctx->regs + offset); + writeq(*(u64 *)val, ctx->regs + offset); break; #endif default: @@ -83,7 +83,7 @@ static int regmap_mmio_read(void *context, BUG_ON(reg_size != 4); - offset = be32_to_cpup(reg); + offset = *(u32 *)reg; while (val_size) { switch (ctx->val_bytes) { @@ -91,14 +91,14 @@ static int regmap_mmio_read(void *context, *(u8 *)val = readb(ctx->regs + offset); break; case 2: - *(u16 *)val = cpu_to_be16(readw(ctx->regs + offset)); + *(u16 *)val = readw(ctx->regs + offset); break; case 4: - *(u32 *)val = cpu_to_be32(readl(ctx->regs + offset)); + *(u32 *)val = readl(ctx->regs + offset); break; #ifdef CONFIG_64BIT case 8: - *(u64 *)val = cpu_to_be32(readq(ctx->regs + offset)); + *(u64 *)val = readq(ctx->regs + offset); break; #endif default: @@ -124,6 +124,8 @@ static struct regmap_bus regmap_mmio = { .gather_write = regmap_mmio_gather_write, .read = regmap_mmio_read, .free_context = regmap_mmio_free_context, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, @@ -162,6 +164,14 @@ struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, if (config->reg_stride < min_stride) return ERR_PTR(-EINVAL); + switch (config->reg_format_endian) { + case REGMAP_ENDIAN_DEFAULT: + case REGMAP_ENDIAN_NATIVE: + break; + default: + return ERR_PTR(-EINVAL); + } + ctx = kzalloc(GFP_KERNEL, sizeof(*ctx)); if (!ctx) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From b026ddbbd25e3560c8d69beb96a5980d96c59b43 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 31 May 2012 21:01:46 +0100 Subject: regmap: Constify regmap_irq_chip We should never be modifying it and it lets drivers declare it const. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 4fac4b9be88f..c190229daa59 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -24,7 +24,7 @@ struct regmap_irq_chip_data { struct mutex lock; struct regmap *map; - struct regmap_irq_chip *chip; + const struct regmap_irq_chip *chip; int irq_base; struct irq_domain *domain; @@ -103,7 +103,7 @@ static struct irq_chip regmap_irq_chip = { static irqreturn_t regmap_irq_thread(int irq, void *d) { struct regmap_irq_chip_data *data = d; - struct regmap_irq_chip *chip = data->chip; + const struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; int ret, i; bool handled = false; @@ -195,7 +195,7 @@ static struct irq_domain_ops regmap_domain_ops = { * register values used by the IRQ controller over suspend and resume. */ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, - int irq_base, struct regmap_irq_chip *chip, + int irq_base, const struct regmap_irq_chip *chip, struct regmap_irq_chip_data **data) { struct regmap_irq_chip_data *d; -- cgit v1.2.3 From cffc9592fde309deafce12362e0a285108cfa3c8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:30:21 +0800 Subject: regulator: core: Allow drivers to set voltage mapping table in regulator_desc Some regulator hardware use table based mapping can set volt_table in regulator_desc and use regulator_list_voltage_table() for their list_voltage callback. Signed-off-by: Axel Lin Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- drivers/regulator/core.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7584a74eec8a..333b7ebe7cae 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1882,6 +1882,31 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); +/** + * regulator_list_voltage_table - List voltages with table based mapping + * + * @rdev: Regulator device + * @selector: Selector to convert into a voltage + * + * Regulators with table based mapping between voltages and + * selectors can set volt_table in the regulator descriptor + * and then use this function as their list_voltage() operation. + */ +int regulator_list_voltage_table(struct regulator_dev *rdev, + unsigned int selector) +{ + if (!rdev->desc->volt_table) { + BUG_ON(!rdev->desc->volt_table); + return -EINVAL; + } + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + return rdev->desc->volt_table[selector]; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage_table); + /** * regulator_list_voltage - enumerate supported voltages * @regulator: regulator source -- cgit v1.2.3 From 8b7485ef623b9171e5a58c67eef5912a27db5822 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 21 May 2012 09:37:52 +0800 Subject: regulator: core: Call set_voltage_time_sel() only when the regulator is on If the regulator is not on, it won't take time setting new voltage. So only call set_voltage_time_sel() to get the necessary delay when the regulator is on. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 333b7ebe7cae..58a4749c6347 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2106,7 +2106,8 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, * If we can't obtain the old selector there is not enough * info to call set_voltage_time_sel(). */ - if (rdev->desc->ops->set_voltage_time_sel && + if (_regulator_is_enabled(rdev) && + rdev->desc->ops->set_voltage_time_sel && rdev->desc->ops->get_voltage_sel) { old_selector = rdev->desc->ops->get_voltage_sel(rdev); if (old_selector < 0) @@ -2138,7 +2139,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, best_val = -1; /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (ret == 0 && old_selector >= 0 && + if (_regulator_is_enabled(rdev) && ret == 0 && old_selector >= 0 && rdev->desc->ops->set_voltage_time_sel) { delay = rdev->desc->ops->set_voltage_time_sel(rdev, -- cgit v1.2.3 From 361ff5017446605dca8b0a084c826e3d2a0d0a99 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 7 May 2012 14:14:30 +0100 Subject: regulator: Use newly added devres_release() rather than open coding devres_release() will call the destructor for the resource as well as freeing the devres data. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 58a4749c6347..7965e86a3fbb 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1459,7 +1459,7 @@ void devm_regulator_put(struct regulator *regulator) { int rc; - rc = devres_destroy(regulator->dev, devm_regulator_release, + rc = devres_release(regulator->dev, devm_regulator_release, devm_regulator_match, regulator); if (rc == 0) regulator_put(regulator); -- cgit v1.2.3 From 3a4b0a07fa69cbfbdd4bc2ebe769cf789949db46 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 May 2012 18:10:45 +0100 Subject: regulator: core: Use dev_get_regmap() to find the regmap If no regmap is explicitly specified then use dev_get_regmap() to obtain one. The driver must explicitly enable any actual usage of the regmap so there's no concern with unwanted usage. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 7965e86a3fbb..8521e0d6b3bc 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3128,7 +3128,10 @@ regulator_register(const struct regulator_desc *regulator_desc, rdev->reg_data = config->driver_data; rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; - rdev->regmap = config->regmap; + if (config->regmap) + rdev->regmap = config->regmap; + else + rdev->regmap = dev_get_regmap(dev, NULL); INIT_LIST_HEAD(&rdev->consumer_list); INIT_LIST_HEAD(&rdev->list); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); -- cgit v1.2.3 From a3beb74261f26142019847128b2441b0301797ac Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:31:58 +0800 Subject: regulator: ab3100: Use regulator_list_voltage_table() Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab3100.c | 50 +++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 03f4d9c604ec..b088b6c228c8 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -45,9 +45,6 @@ * @regreg: regulator register number in the AB3100 * @fixed_voltage: a fixed voltage for this regulator, if this * 0 the voltages array is used instead. - * @typ_voltages: an array of available typical voltages for - * this regulator - * @voltages_len: length of the array of available voltages */ struct ab3100_regulator { struct regulator_dev *rdev; @@ -55,8 +52,6 @@ struct ab3100_regulator { struct ab3100_platform_data *plfdata; u8 regreg; int fixed_voltage; - int const *typ_voltages; - u8 voltages_len; }; /* The order in which registers are initialized */ @@ -80,7 +75,7 @@ static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = { #define LDO_C_VOLTAGE 2650000 #define LDO_D_VOLTAGE 2650000 -static const int ldo_e_buck_typ_voltages[] = { +static const unsigned int ldo_e_buck_typ_voltages[] = { 1800000, 1400000, 1300000, @@ -90,7 +85,7 @@ static const int ldo_e_buck_typ_voltages[] = { 900000, }; -static const int ldo_f_typ_voltages[] = { +static const unsigned int ldo_f_typ_voltages[] = { 1800000, 1400000, 1300000, @@ -101,21 +96,21 @@ static const int ldo_f_typ_voltages[] = { 2650000, }; -static const int ldo_g_typ_voltages[] = { +static const unsigned int ldo_g_typ_voltages[] = { 2850000, 2750000, 1800000, 1500000, }; -static const int ldo_h_typ_voltages[] = { +static const unsigned int ldo_h_typ_voltages[] = { 2750000, 1800000, 1500000, 1200000, }; -static const int ldo_k_typ_voltages[] = { +static const unsigned int ldo_k_typ_voltages[] = { 2750000, 1800000, }; @@ -138,28 +133,18 @@ ab3100_regulators[AB3100_NUM_REGULATORS] = { }, { .regreg = AB3100_LDO_E, - .typ_voltages = ldo_e_buck_typ_voltages, - .voltages_len = ARRAY_SIZE(ldo_e_buck_typ_voltages), }, { .regreg = AB3100_LDO_F, - .typ_voltages = ldo_f_typ_voltages, - .voltages_len = ARRAY_SIZE(ldo_f_typ_voltages), }, { .regreg = AB3100_LDO_G, - .typ_voltages = ldo_g_typ_voltages, - .voltages_len = ARRAY_SIZE(ldo_g_typ_voltages), }, { .regreg = AB3100_LDO_H, - .typ_voltages = ldo_h_typ_voltages, - .voltages_len = ARRAY_SIZE(ldo_h_typ_voltages), }, { .regreg = AB3100_LDO_K, - .typ_voltages = ldo_k_typ_voltages, - .voltages_len = ARRAY_SIZE(ldo_k_typ_voltages), }, { .regreg = AB3100_LDO_EXT, @@ -167,8 +152,6 @@ ab3100_regulators[AB3100_NUM_REGULATORS] = { }, { .regreg = AB3100_BUCK, - .typ_voltages = ldo_e_buck_typ_voltages, - .voltages_len = ARRAY_SIZE(ldo_e_buck_typ_voltages), }, }; @@ -257,16 +240,6 @@ static int ab3100_is_enabled_regulator(struct regulator_dev *reg) return regval & AB3100_REG_ON_MASK; } -static int ab3100_list_voltage_regulator(struct regulator_dev *reg, - unsigned selector) -{ - struct ab3100_regulator *abreg = reg->reg_data; - - if (selector >= abreg->voltages_len) - return -EINVAL; - return abreg->typ_voltages[selector]; -} - static int ab3100_get_voltage_regulator(struct regulator_dev *reg) { struct ab3100_regulator *abreg = reg->reg_data; @@ -294,14 +267,14 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg) regval &= 0xE0; regval >>= 5; - if (regval >= abreg->voltages_len) { + if (regval >= reg->desc->n_voltages) { dev_err(®->dev, "regulator register %02x contains an illegal voltage setting\n", abreg->regreg); return -EINVAL; } - return abreg->typ_voltages[regval]; + return reg->desc->volt_table[regval]; } static int ab3100_set_voltage_regulator_sel(struct regulator_dev *reg, @@ -423,7 +396,7 @@ static struct regulator_ops regulator_ops_variable = { .is_enabled = ab3100_is_enabled_regulator, .get_voltage = ab3100_get_voltage_regulator, .set_voltage_sel = ab3100_set_voltage_regulator_sel, - .list_voltage = ab3100_list_voltage_regulator, + .list_voltage = regulator_list_voltage_table, .enable_time = ab3100_enable_time_regulator, }; @@ -434,7 +407,7 @@ static struct regulator_ops regulator_ops_variable_sleepable = { .get_voltage = ab3100_get_voltage_regulator, .set_voltage_sel = ab3100_set_voltage_regulator_sel, .set_suspend_voltage = ab3100_set_suspend_voltage_regulator, - .list_voltage = ab3100_list_voltage_regulator, + .list_voltage = regulator_list_voltage_table, .enable_time = ab3100_enable_time_regulator, }; @@ -479,6 +452,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .id = AB3100_LDO_E, .ops = ®ulator_ops_variable_sleepable, .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages), + .volt_table = ldo_e_buck_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -487,6 +461,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .id = AB3100_LDO_F, .ops = ®ulator_ops_variable, .n_voltages = ARRAY_SIZE(ldo_f_typ_voltages), + .volt_table = ldo_f_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -495,6 +470,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .id = AB3100_LDO_G, .ops = ®ulator_ops_variable, .n_voltages = ARRAY_SIZE(ldo_g_typ_voltages), + .volt_table = ldo_g_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -503,6 +479,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .id = AB3100_LDO_H, .ops = ®ulator_ops_variable, .n_voltages = ARRAY_SIZE(ldo_h_typ_voltages), + .volt_table = ldo_h_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -511,6 +488,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .id = AB3100_LDO_K, .ops = ®ulator_ops_variable, .n_voltages = ARRAY_SIZE(ldo_k_typ_voltages), + .volt_table = ldo_k_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, -- cgit v1.2.3 From ec1cc4d9da39b58764fdb6f3d1aebcfe709a688f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:33:35 +0800 Subject: regulator: ab8500: Use regulator_list_voltage_table() Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 64 +++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index e1b8c54ace5a..da883e661b38 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -40,8 +40,6 @@ * @voltage_bank: bank to control regulator voltage * @voltage_reg: register to control regulator voltage * @voltage_mask: mask to control regulator voltage - * @voltages: supported voltage table - * @voltages_len: number of supported voltages for the regulator * @delay: startup/set voltage delay in us */ struct ab8500_regulator_info { @@ -58,13 +56,11 @@ struct ab8500_regulator_info { u8 voltage_bank; u8 voltage_reg; u8 voltage_mask; - int const *voltages; - int voltages_len; unsigned int delay; }; /* voltage tables for the vauxn/vintcore supplies */ -static const int ldo_vauxn_voltages[] = { +static const unsigned int ldo_vauxn_voltages[] = { 1100000, 1200000, 1300000, @@ -83,7 +79,7 @@ static const int ldo_vauxn_voltages[] = { 3300000, }; -static const int ldo_vaux3_voltages[] = { +static const unsigned int ldo_vaux3_voltages[] = { 1200000, 1500000, 1800000, @@ -94,7 +90,7 @@ static const int ldo_vaux3_voltages[] = { 2910000, }; -static const int ldo_vintcore_voltages[] = { +static const unsigned int ldo_vintcore_voltages[] = { 1200000, 1225000, 1250000, @@ -185,25 +181,6 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev) return false; } -static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector) -{ - struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); - - if (info == NULL) { - dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); - return -EINVAL; - } - - /* return the uV for the fixed regulators */ - if (info->fixed_uV) - return info->fixed_uV; - - if (selector >= info->voltages_len) - return -EINVAL; - - return info->voltages[selector]; -} - static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev) { int ret, val; @@ -296,11 +273,24 @@ static struct regulator_ops ab8500_regulator_ops = { .is_enabled = ab8500_regulator_is_enabled, .get_voltage_sel = ab8500_regulator_get_voltage_sel, .set_voltage_sel = ab8500_regulator_set_voltage_sel, - .list_voltage = ab8500_list_voltage, + .list_voltage = regulator_list_voltage_table, .enable_time = ab8500_regulator_enable_time, .set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel, }; +static int ab8500_fixed_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); + + if (info == NULL) { + dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); + return -EINVAL; + } + + return info->fixed_uV; +} + static int ab8500_fixed_get_voltage(struct regulator_dev *rdev) { struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); @@ -318,7 +308,7 @@ static struct regulator_ops ab8500_regulator_fixed_ops = { .disable = ab8500_regulator_disable, .is_enabled = ab8500_regulator_is_enabled, .get_voltage = ab8500_fixed_get_voltage, - .list_voltage = ab8500_list_voltage, + .list_voltage = ab8500_fixed_list_voltage, .enable_time = ab8500_regulator_enable_time, .set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel, }; @@ -329,7 +319,7 @@ static struct ab8500_regulator_info * Variable Voltage Regulators * name, min mV, max mV, * update bank, reg, mask, enable val - * volt bank, reg, mask, table, table length + * volt bank, reg, mask */ [AB8500_LDO_AUX1] = { .desc = { @@ -339,6 +329,7 @@ static struct ab8500_regulator_info .id = AB8500_LDO_AUX1, .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, }, .min_uV = 1100000, .max_uV = 3300000, @@ -349,8 +340,6 @@ static struct ab8500_regulator_info .voltage_bank = 0x04, .voltage_reg = 0x1f, .voltage_mask = 0x0f, - .voltages = ldo_vauxn_voltages, - .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages), }, [AB8500_LDO_AUX2] = { .desc = { @@ -360,6 +349,7 @@ static struct ab8500_regulator_info .id = AB8500_LDO_AUX2, .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), + .volt_table = ldo_vauxn_voltages, }, .min_uV = 1100000, .max_uV = 3300000, @@ -370,8 +360,6 @@ static struct ab8500_regulator_info .voltage_bank = 0x04, .voltage_reg = 0x20, .voltage_mask = 0x0f, - .voltages = ldo_vauxn_voltages, - .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages), }, [AB8500_LDO_AUX3] = { .desc = { @@ -381,6 +369,7 @@ static struct ab8500_regulator_info .id = AB8500_LDO_AUX3, .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), + .volt_table = ldo_vaux3_voltages, }, .min_uV = 1100000, .max_uV = 3300000, @@ -391,8 +380,6 @@ static struct ab8500_regulator_info .voltage_bank = 0x04, .voltage_reg = 0x21, .voltage_mask = 0x07, - .voltages = ldo_vaux3_voltages, - .voltages_len = ARRAY_SIZE(ldo_vaux3_voltages), }, [AB8500_LDO_INTCORE] = { .desc = { @@ -402,6 +389,7 @@ static struct ab8500_regulator_info .id = AB8500_LDO_INTCORE, .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), + .volt_table = ldo_vintcore_voltages, }, .min_uV = 1100000, .max_uV = 3300000, @@ -412,8 +400,6 @@ static struct ab8500_regulator_info .voltage_bank = 0x03, .voltage_reg = 0x80, .voltage_mask = 0x38, - .voltages = ldo_vintcore_voltages, - .voltages_len = ARRAY_SIZE(ldo_vintcore_voltages), }, /* @@ -769,9 +755,7 @@ static __devinit int ab8500_regulator_register(struct platform_device *pdev, if (info->desc.id == AB8500_LDO_AUX3) { info->desc.n_voltages = ARRAY_SIZE(ldo_vauxn_voltages); - info->voltages = ldo_vauxn_voltages; - info->voltages_len = - ARRAY_SIZE(ldo_vauxn_voltages); + info->desc.volt_table = ldo_vauxn_voltages; info->voltage_mask = 0xf; } } -- cgit v1.2.3 From 4f73ccad5ca0ce25d0fd73cf3b65975e93a45ce4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:36:17 +0800 Subject: regulator: lp3972: Use regulator_list_voltage_table() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/lp3972.c | 102 ++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index de073df7d344..3cdc755d9b22 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -74,54 +74,40 @@ struct lp3972 { #define LP3972_OVER2_LDO4_EN BIT(4) #define LP3972_OVER1_S_EN BIT(2) -static const int ldo1_voltage_map[] = { - 1700, 1725, 1750, 1775, 1800, 1825, 1850, 1875, - 1900, 1925, 1950, 1975, 2000, +static const unsigned int ldo1_voltage_map[] = { + 1700000, 1725000, 1750000, 1775000, 1800000, 1825000, 1850000, 1875000, + 1900000, 1925000, 1950000, 1975000, 2000000, }; -static const int ldo23_voltage_map[] = { - 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, - 2600, 2700, 2800, 2900, 3000, 3100, 3200, 3300, +static const unsigned int ldo23_voltage_map[] = { + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, }; -static const int ldo4_voltage_map[] = { - 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, - 1400, 1500, 1800, 1900, 2500, 2800, 3000, 3300, +static const unsigned int ldo4_voltage_map[] = { + 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000, 1350000, + 1400000, 1500000, 1800000, 1900000, 2500000, 2800000, 3000000, 3300000, }; -static const int ldo5_voltage_map[] = { - 0, 0, 0, 0, 0, 850, 875, 900, - 925, 950, 975, 1000, 1025, 1050, 1075, 1100, - 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, - 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, +static const unsigned int ldo5_voltage_map[] = { + 0, 0, 0, 0, 0, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, }; -static const int buck1_voltage_map[] = { - 725, 750, 775, 800, 825, 850, 875, 900, - 925, 950, 975, 1000, 1025, 1050, 1075, 1100, - 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, - 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, +static const unsigned int buck1_voltage_map[] = { + 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, }; -static const int buck23_voltage_map[] = { - 0, 800, 850, 900, 950, 1000, 1050, 1100, - 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, - 1550, 1600, 1650, 1700, 1800, 1900, 2500, 2800, - 3000, 3300, -}; - -static const int *ldo_voltage_map[] = { - ldo1_voltage_map, - ldo23_voltage_map, - ldo23_voltage_map, - ldo4_voltage_map, - ldo5_voltage_map, -}; - -static const int *buck_voltage_map[] = { - buck1_voltage_map, - buck23_voltage_map, - buck23_voltage_map, +static const unsigned int buck23_voltage_map[] = { + 0, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1800000, 1900000, 2500000, 2800000, + 3000000, 3300000, }; static const int ldo_output_enable_mask[] = { @@ -160,7 +146,6 @@ static const int buck_base_addr[] = { LP3972_B3TV_REG, }; -#define LP3972_LDO_VOL_VALUE_MAP(x) (ldo_voltage_map[x]) #define LP3972_LDO_OUTPUT_ENABLE_MASK(x) (ldo_output_enable_mask[x]) #define LP3972_LDO_OUTPUT_ENABLE_REG(x) (ldo_output_enable_addr[x]) @@ -177,7 +162,6 @@ static const int buck_base_addr[] = { #define LP3972_LDO_VOL_MIN_IDX(x) (((x) == 4) ? 0x05 : 0x00) #define LP3972_LDO_VOL_MAX_IDX(x) ((x) ? (((x) == 4) ? 0x1f : 0x0f) : 0x0c) -#define LP3972_BUCK_VOL_VALUE_MAP(x) (buck_voltage_map[x]) #define LP3972_BUCK_VOL_ENABLE_REG(x) (buck_vol_enable_addr[x]) #define LP3972_BUCK_VOL1_REG(x) (buck_base_addr[x]) #define LP3972_BUCK_VOL_MASK 0x1f @@ -242,17 +226,6 @@ static int lp3972_set_bits(struct lp3972 *lp3972, u8 reg, u16 mask, u16 val) return ret; } -static int lp3972_ldo_list_voltage(struct regulator_dev *dev, unsigned index) -{ - int ldo = rdev_get_id(dev) - LP3972_LDO1; - - if (index < LP3972_LDO_VOL_MIN_IDX(ldo) || - index > LP3972_LDO_VOL_MAX_IDX(ldo)) - return -EINVAL; - - return 1000 * LP3972_LDO_VOL_VALUE_MAP(ldo)[index]; -} - static int lp3972_ldo_is_enabled(struct regulator_dev *dev) { struct lp3972 *lp3972 = rdev_get_drvdata(dev); @@ -294,7 +267,7 @@ static int lp3972_ldo_get_voltage(struct regulator_dev *dev) reg = lp3972_reg_read(lp3972, LP3972_LDO_VOL_CONTR_REG(ldo)); val = (reg >> LP3972_LDO_VOL_CONTR_SHIFT(ldo)) & mask; - return 1000 * LP3972_LDO_VOL_VALUE_MAP(ldo)[val]; + return dev->desc->volt_table[val]; } static int lp3972_ldo_set_voltage_sel(struct regulator_dev *dev, @@ -337,7 +310,7 @@ static int lp3972_ldo_set_voltage_sel(struct regulator_dev *dev, } static struct regulator_ops lp3972_ldo_ops = { - .list_voltage = lp3972_ldo_list_voltage, + .list_voltage = regulator_list_voltage_table, .is_enabled = lp3972_ldo_is_enabled, .enable = lp3972_ldo_enable, .disable = lp3972_ldo_disable, @@ -345,17 +318,6 @@ static struct regulator_ops lp3972_ldo_ops = { .set_voltage_sel = lp3972_ldo_set_voltage_sel, }; -static int lp3972_dcdc_list_voltage(struct regulator_dev *dev, unsigned index) -{ - int buck = rdev_get_id(dev) - LP3972_DCDC1; - - if (index < LP3972_BUCK_VOL_MIN_IDX(buck) || - index > LP3972_BUCK_VOL_MAX_IDX(buck)) - return -EINVAL; - - return 1000 * buck_voltage_map[buck][index]; -} - static int lp3972_dcdc_is_enabled(struct regulator_dev *dev) { struct lp3972 *lp3972 = rdev_get_drvdata(dev); @@ -401,7 +363,7 @@ static int lp3972_dcdc_get_voltage(struct regulator_dev *dev) reg = lp3972_reg_read(lp3972, LP3972_BUCK_VOL1_REG(buck)); reg &= LP3972_BUCK_VOL_MASK; if (reg <= LP3972_BUCK_VOL_MAX_IDX(buck)) - val = 1000 * buck_voltage_map[buck][reg]; + val = dev->desc->volt_table[reg]; else { val = 0; dev_warn(&dev->dev, "chip reported incorrect voltage value." @@ -436,7 +398,7 @@ static int lp3972_dcdc_set_voltage_sel(struct regulator_dev *dev, } static struct regulator_ops lp3972_dcdc_ops = { - .list_voltage = lp3972_dcdc_list_voltage, + .list_voltage = regulator_list_voltage_table, .is_enabled = lp3972_dcdc_is_enabled, .enable = lp3972_dcdc_enable, .disable = lp3972_dcdc_disable, @@ -450,6 +412,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_LDO1, .ops = &lp3972_ldo_ops, .n_voltages = ARRAY_SIZE(ldo1_voltage_map), + .volt_table = ldo1_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -458,6 +421,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_LDO2, .ops = &lp3972_ldo_ops, .n_voltages = ARRAY_SIZE(ldo23_voltage_map), + .volt_table = ldo23_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -466,6 +430,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_LDO3, .ops = &lp3972_ldo_ops, .n_voltages = ARRAY_SIZE(ldo23_voltage_map), + .volt_table = ldo23_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -474,6 +439,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_LDO4, .ops = &lp3972_ldo_ops, .n_voltages = ARRAY_SIZE(ldo4_voltage_map), + .volt_table = ldo4_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -482,6 +448,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_LDO5, .ops = &lp3972_ldo_ops, .n_voltages = ARRAY_SIZE(ldo5_voltage_map), + .volt_table = ldo5_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -490,6 +457,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_DCDC1, .ops = &lp3972_dcdc_ops, .n_voltages = ARRAY_SIZE(buck1_voltage_map), + .volt_table = buck1_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -498,6 +466,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_DCDC2, .ops = &lp3972_dcdc_ops, .n_voltages = ARRAY_SIZE(buck23_voltage_map), + .volt_table = buck23_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -506,6 +475,7 @@ static const struct regulator_desc regulators[] = { .id = LP3972_DCDC3, .ops = &lp3972_dcdc_ops, .n_voltages = ARRAY_SIZE(buck23_voltage_map), + .volt_table = buck23_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, -- cgit v1.2.3 From c55979b7bd78b8ee7999bcf5211bf0243260c675 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:37:33 +0800 Subject: regulator: tps6105x: Use regulator_list_voltage_table() Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/tps6105x-regulator.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6105x-regulator.c b/drivers/regulator/tps6105x-regulator.c index d840d8440a91..1378409efaec 100644 --- a/drivers/regulator/tps6105x-regulator.c +++ b/drivers/regulator/tps6105x-regulator.c @@ -20,7 +20,7 @@ #include #include -static const int tps6105x_voltages[] = { +static const unsigned int tps6105x_voltages[] = { 4500000, 5000000, 5250000, @@ -105,22 +105,13 @@ static int tps6105x_regulator_set_voltage_sel(struct regulator_dev *rdev, return 0; } -static int tps6105x_regulator_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - if (selector >= ARRAY_SIZE(tps6105x_voltages)) - return -EINVAL; - - return tps6105x_voltages[selector]; -} - static struct regulator_ops tps6105x_regulator_ops = { .enable = tps6105x_regulator_enable, .disable = tps6105x_regulator_disable, .is_enabled = tps6105x_regulator_is_enabled, .get_voltage_sel = tps6105x_regulator_get_voltage_sel, .set_voltage_sel = tps6105x_regulator_set_voltage_sel, - .list_voltage = tps6105x_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static const struct regulator_desc tps6105x_regulator_desc = { @@ -130,6 +121,7 @@ static const struct regulator_desc tps6105x_regulator_desc = { .id = 0, .owner = THIS_MODULE, .n_voltages = ARRAY_SIZE(tps6105x_voltages), + .volt_table = tps6105x_voltages, }; /* -- cgit v1.2.3 From 055917ac560a4185b75511b512f2db941b984672 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 10:39:12 +0800 Subject: regulator: tps6507x: Use regulator_list_voltage_table() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6507x-regulator.c | 100 ++++++++++++++------------------- 1 file changed, 43 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index da38be1016aa..c771e1077cc1 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -43,50 +43,50 @@ /* Number of total regulators available */ #define TPS6507X_NUM_REGULATOR (TPS6507X_NUM_DCDC + TPS6507X_NUM_LDO) -/* Supported voltage values for regulators (in milliVolts) */ -static const u16 VDCDCx_VSEL_table[] = { - 725, 750, 775, 800, - 825, 850, 875, 900, - 925, 950, 975, 1000, - 1025, 1050, 1075, 1100, - 1125, 1150, 1175, 1200, - 1225, 1250, 1275, 1300, - 1325, 1350, 1375, 1400, - 1425, 1450, 1475, 1500, - 1550, 1600, 1650, 1700, - 1750, 1800, 1850, 1900, - 1950, 2000, 2050, 2100, - 2150, 2200, 2250, 2300, - 2350, 2400, 2450, 2500, - 2550, 2600, 2650, 2700, - 2750, 2800, 2850, 2900, - 3000, 3100, 3200, 3300, +/* Supported voltage values for regulators (in microVolts) */ +static const unsigned int VDCDCx_VSEL_table[] = { + 725000, 750000, 775000, 800000, + 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, + 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, + 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, + 1425000, 1450000, 1475000, 1500000, + 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, + 2150000, 2200000, 2250000, 2300000, + 2350000, 2400000, 2450000, 2500000, + 2550000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, + 3000000, 3100000, 3200000, 3300000, }; -static const u16 LDO1_VSEL_table[] = { - 1000, 1100, 1200, 1250, - 1300, 1350, 1400, 1500, - 1600, 1800, 2500, 2750, - 2800, 3000, 3100, 3300, +static const unsigned int LDO1_VSEL_table[] = { + 1000000, 1100000, 1200000, 1250000, + 1300000, 1350000, 1400000, 1500000, + 1600000, 1800000, 2500000, 2750000, + 2800000, 3000000, 3100000, 3300000, }; -static const u16 LDO2_VSEL_table[] = { - 725, 750, 775, 800, - 825, 850, 875, 900, - 925, 950, 975, 1000, - 1025, 1050, 1075, 1100, - 1125, 1150, 1175, 1200, - 1225, 1250, 1275, 1300, - 1325, 1350, 1375, 1400, - 1425, 1450, 1475, 1500, - 1550, 1600, 1650, 1700, - 1750, 1800, 1850, 1900, - 1950, 2000, 2050, 2100, - 2150, 2200, 2250, 2300, - 2350, 2400, 2450, 2500, - 2550, 2600, 2650, 2700, - 2750, 2800, 2850, 2900, - 3000, 3100, 3200, 3300, +static const unsigned int LDO2_VSEL_table[] = { + 725000, 750000, 775000, 800000, + 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, + 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, + 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, + 1425000, 1450000, 1475000, 1500000, + 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, + 2150000, 2200000, 2250000, 2300000, + 2350000, 2400000, 2450000, 2500000, + 2550000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, + 3000000, 3100000, 3200000, 3300000, }; struct tps_info { @@ -94,7 +94,7 @@ struct tps_info { unsigned min_uV; unsigned max_uV; u8 table_len; - const u16 *table; + const unsigned int *table; /* Does DCDC high or the low register defines output voltage? */ bool defdcdc_default; @@ -375,28 +375,13 @@ static int tps6507x_pmic_set_voltage_sel(struct regulator_dev *dev, return tps6507x_pmic_reg_write(tps, reg, data); } -static int tps6507x_pmic_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct tps6507x_pmic *tps = rdev_get_drvdata(dev); - int rid = rdev_get_id(dev); - - if (rid < TPS6507X_DCDC_1 || rid > TPS6507X_LDO_2) - return -EINVAL; - - if (selector >= tps->info[rid]->table_len) - return -EINVAL; - else - return tps->info[rid]->table[selector] * 1000; -} - static struct regulator_ops tps6507x_pmic_ops = { .is_enabled = tps6507x_pmic_is_enabled, .enable = tps6507x_pmic_enable, .disable = tps6507x_pmic_disable, .get_voltage_sel = tps6507x_pmic_get_voltage_sel, .set_voltage_sel = tps6507x_pmic_set_voltage_sel, - .list_voltage = tps6507x_pmic_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) @@ -449,6 +434,7 @@ static __devinit int tps6507x_pmic_probe(struct platform_device *pdev) tps->desc[i].name = info->name; tps->desc[i].id = i; tps->desc[i].n_voltages = info->table_len; + tps->desc[i].volt_table = info->table; tps->desc[i].ops = &tps6507x_pmic_ops; tps->desc[i].type = REGULATOR_VOLTAGE; tps->desc[i].owner = THIS_MODULE; -- cgit v1.2.3 From cad8d76e20eda5ccfcfbccdd711b91e053b4ab05 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 20 May 2012 13:30:28 +0800 Subject: regulator: lp3971: Use regulator_list_voltage_table() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/lp3971.c | 66 ++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 981bea9cb9d7..7c6e3b8ff484 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -65,11 +65,11 @@ static const int buck_base_addr[] = { #define LP3971_BUCK_TARGET_VOL1_REG(x) (buck_base_addr[x]) #define LP3971_BUCK_TARGET_VOL2_REG(x) (buck_base_addr[x]+1) -static const int buck_voltage_map[] = { - 0, 800, 850, 900, 950, 1000, 1050, 1100, - 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, - 1550, 1600, 1650, 1700, 1800, 1900, 2500, 2800, - 3000, 3300, +static const unsigned int buck_voltage_map[] = { + 0, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1800000, 1900000, 2500000, 2800000, + 3000000, 3300000, }; #define BUCK_TARGET_VOL_MASK 0x3f @@ -98,39 +98,19 @@ static const int buck_voltage_map[] = { #define LDO_VOL_CONTR_SHIFT(x) ((x & 1) << 2) #define LDO_VOL_CONTR_MASK 0x0f -static const int ldo45_voltage_map[] = { - 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, - 1400, 1500, 1800, 1900, 2500, 2800, 3000, 3300, +static const unsigned int ldo45_voltage_map[] = { + 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, 1300000, 1350000, + 1400000, 1500000, 1800000, 1900000, 2500000, 2800000, 3000000, 3300000, }; -static const int ldo123_voltage_map[] = { - 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, - 2600, 2700, 2800, 2900, 3000, 3100, 3200, 3300, +static const unsigned int ldo123_voltage_map[] = { + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, }; -static const int *ldo_voltage_map[] = { - ldo123_voltage_map, /* LDO1 */ - ldo123_voltage_map, /* LDO2 */ - ldo123_voltage_map, /* LDO3 */ - ldo45_voltage_map, /* LDO4 */ - ldo45_voltage_map, /* LDO5 */ -}; - -#define LDO_VOL_VALUE_MAP(x) (ldo_voltage_map[(x - LP3971_LDO1)]) - #define LDO_VOL_MIN_IDX 0x00 #define LDO_VOL_MAX_IDX 0x0f -static int lp3971_ldo_list_voltage(struct regulator_dev *dev, unsigned index) -{ - int ldo = rdev_get_id(dev) - LP3971_LDO1; - - if (index > LDO_VOL_MAX_IDX) - return -EINVAL; - - return 1000 * LDO_VOL_VALUE_MAP(ldo)[index]; -} - static int lp3971_ldo_is_enabled(struct regulator_dev *dev) { struct lp3971 *lp3971 = rdev_get_drvdata(dev); @@ -169,7 +149,7 @@ static int lp3971_ldo_get_voltage(struct regulator_dev *dev) reg = lp3971_reg_read(lp3971, LP3971_LDO_VOL_CONTR_REG(ldo)); val = (reg >> LDO_VOL_CONTR_SHIFT(ldo)) & LDO_VOL_CONTR_MASK; - return 1000 * LDO_VOL_VALUE_MAP(ldo)[val]; + return dev->desc->volt_table[val]; } static int lp3971_ldo_set_voltage_sel(struct regulator_dev *dev, @@ -184,7 +164,7 @@ static int lp3971_ldo_set_voltage_sel(struct regulator_dev *dev, } static struct regulator_ops lp3971_ldo_ops = { - .list_voltage = lp3971_ldo_list_voltage, + .list_voltage = regulator_list_voltage_table, .is_enabled = lp3971_ldo_is_enabled, .enable = lp3971_ldo_enable, .disable = lp3971_ldo_disable, @@ -192,14 +172,6 @@ static struct regulator_ops lp3971_ldo_ops = { .set_voltage_sel = lp3971_ldo_set_voltage_sel, }; -static int lp3971_dcdc_list_voltage(struct regulator_dev *dev, unsigned index) -{ - if (index < BUCK_TARGET_VOL_MIN_IDX || index > BUCK_TARGET_VOL_MAX_IDX) - return -EINVAL; - - return 1000 * buck_voltage_map[index]; -} - static int lp3971_dcdc_is_enabled(struct regulator_dev *dev) { struct lp3971 *lp3971 = rdev_get_drvdata(dev); @@ -240,7 +212,7 @@ static int lp3971_dcdc_get_voltage(struct regulator_dev *dev) reg &= BUCK_TARGET_VOL_MASK; if (reg <= BUCK_TARGET_VOL_MAX_IDX) - val = 1000 * buck_voltage_map[reg]; + val = buck_voltage_map[reg]; else { val = 0; dev_warn(&dev->dev, "chip reported incorrect voltage value.\n"); @@ -273,7 +245,7 @@ static int lp3971_dcdc_set_voltage_sel(struct regulator_dev *dev, } static struct regulator_ops lp3971_dcdc_ops = { - .list_voltage = lp3971_dcdc_list_voltage, + .list_voltage = regulator_list_voltage_table, .is_enabled = lp3971_dcdc_is_enabled, .enable = lp3971_dcdc_enable, .disable = lp3971_dcdc_disable, @@ -287,6 +259,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_LDO1, .ops = &lp3971_ldo_ops, .n_voltages = ARRAY_SIZE(ldo123_voltage_map), + .volt_table = ldo123_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -295,6 +268,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_LDO2, .ops = &lp3971_ldo_ops, .n_voltages = ARRAY_SIZE(ldo123_voltage_map), + .volt_table = ldo123_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -303,6 +277,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_LDO3, .ops = &lp3971_ldo_ops, .n_voltages = ARRAY_SIZE(ldo123_voltage_map), + .volt_table = ldo123_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -311,6 +286,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_LDO4, .ops = &lp3971_ldo_ops, .n_voltages = ARRAY_SIZE(ldo45_voltage_map), + .volt_table = ldo45_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -319,6 +295,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_LDO5, .ops = &lp3971_ldo_ops, .n_voltages = ARRAY_SIZE(ldo45_voltage_map), + .volt_table = ldo45_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -327,6 +304,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_DCDC1, .ops = &lp3971_dcdc_ops, .n_voltages = ARRAY_SIZE(buck_voltage_map), + .volt_table = buck_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -335,6 +313,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_DCDC2, .ops = &lp3971_dcdc_ops, .n_voltages = ARRAY_SIZE(buck_voltage_map), + .volt_table = buck_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -343,6 +322,7 @@ static const struct regulator_desc regulators[] = { .id = LP3971_DCDC3, .ops = &lp3971_dcdc_ops, .n_voltages = ARRAY_SIZE(buck_voltage_map), + .volt_table = buck_voltage_map, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, -- cgit v1.2.3 From 6333e9ddf4787ec42cbb4cbb482168094e54b337 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 21 May 2012 09:39:08 +0800 Subject: regulator: ab8500: Let regulator core handle the case no delay for setting new voltage if regulator is off Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index da883e661b38..f15807449011 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -256,14 +256,7 @@ static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int new_sel) { struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); - int ret; - /* If the regulator isn't on, it won't take time here */ - ret = ab8500_regulator_is_enabled(rdev); - if (ret < 0) - return ret; - if (!ret) - return 0; return info->delay; } -- cgit v1.2.3 From b4ec87aedbdae602dbc232915ff5a8c1fad89b36 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Apr 2012 21:00:10 +0100 Subject: regulator: wm8350: Convert to use core regmap vsel readback Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 75 +++++++++--------------------------- 1 file changed, 19 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 94e550dc70b6..5ccab371b408 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -405,34 +405,6 @@ static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV, return 0; } -static int wm8350_dcdc_get_voltage_sel(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int volt_reg, dcdc = rdev_get_id(rdev); - - switch (dcdc) { - case WM8350_DCDC_1: - volt_reg = WM8350_DCDC1_CONTROL; - break; - case WM8350_DCDC_3: - volt_reg = WM8350_DCDC3_CONTROL; - break; - case WM8350_DCDC_4: - volt_reg = WM8350_DCDC4_CONTROL; - break; - case WM8350_DCDC_6: - volt_reg = WM8350_DCDC6_CONTROL; - break; - case WM8350_DCDC_2: - case WM8350_DCDC_5: - default: - return -EINVAL; - } - - /* all DCDCs have same mV bits */ - return wm8350_reg_read(wm8350, volt_reg) & WM8350_DC1_VSEL_MASK; -} - static int wm8350_dcdc_list_voltage(struct regulator_dev *rdev, unsigned selector) { @@ -805,32 +777,6 @@ static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, return 0; } -static int wm8350_ldo_get_voltage_sel(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int volt_reg, ldo = rdev_get_id(rdev); - - switch (ldo) { - case WM8350_LDO_1: - volt_reg = WM8350_LDO1_CONTROL; - break; - case WM8350_LDO_2: - volt_reg = WM8350_LDO2_CONTROL; - break; - case WM8350_LDO_3: - volt_reg = WM8350_LDO3_CONTROL; - break; - case WM8350_LDO_4: - volt_reg = WM8350_LDO4_CONTROL; - break; - default: - return -EINVAL; - } - - /* all LDOs have same mV bits */ - return wm8350_reg_read(wm8350, volt_reg) & WM8350_LDO1_VSEL_MASK; -} - static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, unsigned selector) { @@ -1225,7 +1171,7 @@ static int wm8350_ldo_is_enabled(struct regulator_dev *rdev) static struct regulator_ops wm8350_dcdc_ops = { .set_voltage = wm8350_dcdc_set_voltage, - .get_voltage_sel = wm8350_dcdc_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = wm8350_dcdc_list_voltage, .enable = wm8350_dcdc_enable, .disable = wm8350_dcdc_disable, @@ -1249,7 +1195,7 @@ static struct regulator_ops wm8350_dcdc2_5_ops = { static struct regulator_ops wm8350_ldo_ops = { .set_voltage = wm8350_ldo_set_voltage, - .get_voltage_sel = wm8350_ldo_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = wm8350_ldo_list_voltage, .enable = wm8350_ldo_enable, .disable = wm8350_ldo_disable, @@ -1277,6 +1223,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC1, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .vsel_reg = WM8350_DCDC1_CONTROL, + .vsel_mask = WM8350_DC1_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1294,6 +1242,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC3, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .vsel_reg = WM8350_DCDC3_CONTROL, + .vsel_mask = WM8350_DC3_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1303,6 +1253,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC4, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .vsel_reg = WM8350_DCDC4_CONTROL, + .vsel_mask = WM8350_DC4_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1320,6 +1272,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC6, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .vsel_reg = WM8350_DCDC6_CONTROL, + .vsel_mask = WM8350_DC6_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1329,6 +1283,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO1, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO1_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO1_CONTROL, + .vsel_mask = WM8350_LDO1_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1338,6 +1294,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO2, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO2_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO2_CONTROL, + .vsel_mask = WM8350_LDO2_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1347,6 +1305,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO3, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO3_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO3_CONTROL, + .vsel_mask = WM8350_LDO3_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1356,6 +1316,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_LDO4, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_LDO4_VSEL_MASK + 1, + .vsel_reg = WM8350_LDO4_CONTROL, + .vsel_mask = WM8350_LDO4_VSEL_MASK, .owner = THIS_MODULE, }, { @@ -1429,6 +1391,7 @@ static int wm8350_regulator_probe(struct platform_device *pdev) config.dev = &pdev->dev; config.init_data = pdev->dev.platform_data; config.driver_data = dev_get_drvdata(&pdev->dev); + config.regmap = wm8350->regmap; /* register regulator */ rdev = regulator_register(&wm8350_reg[pdev->id], &config); -- cgit v1.2.3 From a540f682860b3ce11fbfd36118bf64d5d4152bc1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 30 Apr 2012 21:08:59 +0100 Subject: regulator: wm8350: Convert to core regmap-based enable operations Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 121 +++++++++-------------------------- 1 file changed, 29 insertions(+), 92 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 5ccab371b408..12ecaec770a1 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -905,63 +905,6 @@ int wm8350_dcdc25_set_mode(struct wm8350 *wm8350, int dcdc, u16 mode, } EXPORT_SYMBOL_GPL(wm8350_dcdc25_set_mode); -static int wm8350_dcdc_enable(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int dcdc = rdev_get_id(rdev); - u16 shift; - - if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) - return -EINVAL; - - shift = dcdc - WM8350_DCDC_1; - wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); - return 0; -} - -static int wm8350_dcdc_disable(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int dcdc = rdev_get_id(rdev); - u16 shift; - - if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) - return -EINVAL; - - shift = dcdc - WM8350_DCDC_1; - wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); - - return 0; -} - -static int wm8350_ldo_enable(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int ldo = rdev_get_id(rdev); - u16 shift; - - if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) - return -EINVAL; - - shift = (ldo - WM8350_LDO_1) + 8; - wm8350_set_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); - return 0; -} - -static int wm8350_ldo_disable(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int ldo = rdev_get_id(rdev); - u16 shift; - - if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) - return -EINVAL; - - shift = (ldo - WM8350_LDO_1) + 8; - wm8350_clear_bits(wm8350, WM8350_DCDC_LDO_REQUESTED, 1 << shift); - return 0; -} - static int force_continuous_enable(struct wm8350 *wm8350, int dcdc, int enable) { int reg = 0, ret; @@ -1143,42 +1086,16 @@ static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, return mode; } -static int wm8350_dcdc_is_enabled(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int dcdc = rdev_get_id(rdev), shift; - - if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) - return -EINVAL; - - shift = dcdc - WM8350_DCDC_1; - return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED) - & (1 << shift); -} - -static int wm8350_ldo_is_enabled(struct regulator_dev *rdev) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int ldo = rdev_get_id(rdev), shift; - - if (ldo < WM8350_LDO_1 || ldo > WM8350_LDO_4) - return -EINVAL; - - shift = (ldo - WM8350_LDO_1) + 8; - return wm8350_reg_read(wm8350, WM8350_DCDC_LDO_REQUESTED) - & (1 << shift); -} - static struct regulator_ops wm8350_dcdc_ops = { .set_voltage = wm8350_dcdc_set_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = wm8350_dcdc_list_voltage, - .enable = wm8350_dcdc_enable, - .disable = wm8350_dcdc_disable, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, .get_mode = wm8350_dcdc_get_mode, .set_mode = wm8350_dcdc_set_mode, .get_optimum_mode = wm8350_dcdc_get_optimum_mode, - .is_enabled = wm8350_dcdc_is_enabled, .set_suspend_voltage = wm8350_dcdc_set_suspend_voltage, .set_suspend_enable = wm8350_dcdc_set_suspend_enable, .set_suspend_disable = wm8350_dcdc_set_suspend_disable, @@ -1186,9 +1103,9 @@ static struct regulator_ops wm8350_dcdc_ops = { }; static struct regulator_ops wm8350_dcdc2_5_ops = { - .enable = wm8350_dcdc_enable, - .disable = wm8350_dcdc_disable, - .is_enabled = wm8350_dcdc_is_enabled, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, .set_suspend_enable = wm8350_dcdc25_set_suspend_enable, .set_suspend_disable = wm8350_dcdc25_set_suspend_disable, }; @@ -1197,9 +1114,9 @@ static struct regulator_ops wm8350_ldo_ops = { .set_voltage = wm8350_ldo_set_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = wm8350_ldo_list_voltage, - .enable = wm8350_ldo_enable, - .disable = wm8350_ldo_disable, - .is_enabled = wm8350_ldo_is_enabled, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, .get_mode = wm8350_ldo_get_mode, .set_suspend_voltage = wm8350_ldo_set_suspend_voltage, .set_suspend_enable = wm8350_ldo_set_suspend_enable, @@ -1225,6 +1142,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .vsel_reg = WM8350_DCDC1_CONTROL, .vsel_mask = WM8350_DC1_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC1_ENA, .owner = THIS_MODULE, }, { @@ -1233,6 +1152,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_dcdc2_5_ops, .irq = WM8350_IRQ_UV_DC2, .type = REGULATOR_VOLTAGE, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC2_ENA, .owner = THIS_MODULE, }, { @@ -1244,6 +1165,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .vsel_reg = WM8350_DCDC3_CONTROL, .vsel_mask = WM8350_DC3_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC3_ENA, .owner = THIS_MODULE, }, { @@ -1255,6 +1178,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .vsel_reg = WM8350_DCDC4_CONTROL, .vsel_mask = WM8350_DC4_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC4_ENA, .owner = THIS_MODULE, }, { @@ -1263,6 +1188,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_dcdc2_5_ops, .irq = WM8350_IRQ_UV_DC5, .type = REGULATOR_VOLTAGE, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC5_ENA, .owner = THIS_MODULE, }, { @@ -1274,6 +1201,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .vsel_reg = WM8350_DCDC6_CONTROL, .vsel_mask = WM8350_DC6_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_DC6_ENA, .owner = THIS_MODULE, }, { @@ -1285,6 +1214,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_LDO1_VSEL_MASK + 1, .vsel_reg = WM8350_LDO1_CONTROL, .vsel_mask = WM8350_LDO1_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO1_ENA, .owner = THIS_MODULE, }, { @@ -1296,6 +1227,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_LDO2_VSEL_MASK + 1, .vsel_reg = WM8350_LDO2_CONTROL, .vsel_mask = WM8350_LDO2_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO2_ENA, .owner = THIS_MODULE, }, { @@ -1307,6 +1240,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_LDO3_VSEL_MASK + 1, .vsel_reg = WM8350_LDO3_CONTROL, .vsel_mask = WM8350_LDO3_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO3_ENA, .owner = THIS_MODULE, }, { @@ -1318,6 +1253,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .n_voltages = WM8350_LDO4_VSEL_MASK + 1, .vsel_reg = WM8350_LDO4_CONTROL, .vsel_mask = WM8350_LDO4_VSEL_MASK, + .enable_reg = WM8350_DCDC_LDO_REQUESTED, + .enable_mask = WM8350_LDO4_ENA, .owner = THIS_MODULE, }, { -- cgit v1.2.3 From 107a3967a814d99e700ff3788f6c66568ab914db Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 9 May 2012 22:22:30 +0100 Subject: regulator: wm8350: Convert DCDCs to set_voltage_sel() and linear voltages The WM8350 DCDCs have a simple linear mapping from selectors to voltages so can be converted very simply to use the new infrastructure for a nice reduction in code size. Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 64 +++++------------------------------- 1 file changed, 8 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 12ecaec770a1..a618284fc45e 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -359,60 +359,6 @@ int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, } EXPORT_SYMBOL_GPL(wm8350_isink_set_flash); -static int wm8350_dcdc_set_voltage(struct regulator_dev *rdev, int min_uV, - int max_uV, unsigned *selector) -{ - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int volt_reg, dcdc = rdev_get_id(rdev), mV, - min_mV = min_uV / 1000, max_mV = max_uV / 1000; - u16 val; - - if (min_mV < 850 || min_mV > 4025) - return -EINVAL; - if (max_mV < 850 || max_mV > 4025) - return -EINVAL; - - /* step size is 25mV */ - mV = (min_mV - 826) / 25; - if (wm8350_dcdc_val_to_mvolts(mV) > max_mV) - return -EINVAL; - BUG_ON(wm8350_dcdc_val_to_mvolts(mV) < min_mV); - - switch (dcdc) { - case WM8350_DCDC_1: - volt_reg = WM8350_DCDC1_CONTROL; - break; - case WM8350_DCDC_3: - volt_reg = WM8350_DCDC3_CONTROL; - break; - case WM8350_DCDC_4: - volt_reg = WM8350_DCDC4_CONTROL; - break; - case WM8350_DCDC_6: - volt_reg = WM8350_DCDC6_CONTROL; - break; - case WM8350_DCDC_2: - case WM8350_DCDC_5: - default: - return -EINVAL; - } - - *selector = mV; - - /* all DCDCs have same mV bits */ - val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; - wm8350_reg_write(wm8350, volt_reg, val | mV); - return 0; -} - -static int wm8350_dcdc_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - if (selector > WM8350_DCDC_MAX_VSEL) - return -EINVAL; - return wm8350_dcdc_val_to_mvolts(selector) * 1000; -} - static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); @@ -1087,9 +1033,9 @@ static unsigned int wm8350_dcdc_get_optimum_mode(struct regulator_dev *rdev, } static struct regulator_ops wm8350_dcdc_ops = { - .set_voltage = wm8350_dcdc_set_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .list_voltage = wm8350_dcdc_list_voltage, + .list_voltage = regulator_list_voltage_linear, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, @@ -1140,6 +1086,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC1, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, .vsel_reg = WM8350_DCDC1_CONTROL, .vsel_mask = WM8350_DC1_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, @@ -1163,6 +1111,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC3, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, .vsel_reg = WM8350_DCDC3_CONTROL, .vsel_mask = WM8350_DC3_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, @@ -1199,6 +1149,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC6, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, .vsel_reg = WM8350_DCDC6_CONTROL, .vsel_mask = WM8350_DC6_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, -- cgit v1.2.3 From 8029a00686e396919b9adb2a6df07d68ef96cf3d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 22 May 2012 12:26:42 +0800 Subject: regulator: palmas: Use regulator_[list|map]_voltage_linear() for palmas_ops_smps10 Signed-off-by: Axel Lin Acked-by: Graeme Gregory Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index c4435f608df7..b4e10b0b36d4 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -400,19 +400,14 @@ static struct regulator_ops palmas_ops_smps = { .map_voltage = palmas_map_voltage_smps, }; -static int palmas_list_voltage_smps10(struct regulator_dev *dev, - unsigned selector) -{ - return 3750000 + (selector * 1250000); -} - static struct regulator_ops palmas_ops_smps10 = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .list_voltage = palmas_list_voltage_smps10, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, }; static int palmas_is_enabled_ldo(struct regulator_dev *dev) @@ -675,6 +670,8 @@ static __devinit int palmas_probe(struct platform_device *pdev) pmic->desc[id].vsel_mask = SMPS10_VSEL; pmic->desc[id].enable_reg = PALMAS_SMPS10_STATUS; pmic->desc[id].enable_mask = SMPS10_BOOST_EN; + pmic->desc[id].min_uV = 3750000; + pmic->desc[id].uV_step = 1250000; } pmic->desc[id].type = REGULATOR_VOLTAGE; -- cgit v1.2.3 From 0713e6abf398327b6813398d3583e0907953e457 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 14 May 2012 11:06:44 +0800 Subject: regulator: anatop: Convert to regulator_list_voltage_linear() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 3660bace123c..d04cf2113245 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -94,21 +94,10 @@ static int anatop_get_voltage_sel(struct regulator_dev *reg) return val - anatop_reg->min_bit_val; } -static int anatop_list_voltage(struct regulator_dev *reg, unsigned selector) -{ - struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); - int uv; - - uv = anatop_reg->min_voltage + selector * 25000; - dev_dbg(®->dev, "vddio = %d, selector = %u\n", uv, selector); - - return uv; -} - static struct regulator_ops anatop_rops = { .set_voltage = anatop_set_voltage, .get_voltage_sel = anatop_get_voltage_sel, - .list_voltage = anatop_list_voltage, + .list_voltage = regulator_list_voltage_linear, }; static int __devinit anatop_regulator_probe(struct platform_device *pdev) @@ -176,6 +165,8 @@ static int __devinit anatop_regulator_probe(struct platform_device *pdev) rdesc->n_voltages = (sreg->max_voltage - sreg->min_voltage) / 25000 + 1; + rdesc->min_uV = sreg->min_voltage; + rdesc->uV_step = 25000; config.dev = &pdev->dev; config.init_data = initdata; -- cgit v1.2.3 From f2d103add158af4c95223a1b576cc0cc6a41a18b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 23 May 2012 22:40:58 +0800 Subject: regulator: wm8994: Convert wm8994_ldo1_ops to regulator_[list|map]_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8994-regulator.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 9a994316e63c..0e2028b74d1a 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -77,22 +77,14 @@ static int wm8994_ldo_enable_time(struct regulator_dev *rdev) return 3000; } -static int wm8994_ldo1_list_voltage(struct regulator_dev *rdev, - unsigned int selector) -{ - if (selector > WM8994_LDO1_MAX_SELECTOR) - return -EINVAL; - - return (selector * 100000) + 2400000; -} - static struct regulator_ops wm8994_ldo1_ops = { .enable = wm8994_ldo_enable, .disable = wm8994_ldo_disable, .is_enabled = wm8994_ldo_is_enabled, .enable_time = wm8994_ldo_enable_time, - .list_voltage = wm8994_ldo1_list_voltage, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, }; @@ -143,6 +135,8 @@ static const struct regulator_desc wm8994_ldo_desc[] = { .vsel_reg = WM8994_LDO_1, .vsel_mask = WM8994_LDO1_VSEL_MASK, .ops = &wm8994_ldo1_ops, + .min_uV = 2400000, + .uV_step = 100000, .owner = THIS_MODULE, }, { -- cgit v1.2.3 From c2543b5f6c3e13996776079cda1007ef6e7a0bbb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 31 May 2012 17:39:00 +0800 Subject: regulator: wm831x-ldo: Use regulator_map_voltage_linear for wm831x_alive_ldo_ops wm831x_alive_ldo_ops uses simple linear voltage maps. Thus use regulator_map_voltage_linear is more efficient than using the default regulator_map_voltage_iterate. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm831x-ldo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index a9a28d8ac185..f23d020e9503 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -690,6 +690,7 @@ static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev) static struct regulator_ops wm831x_alive_ldo_ops = { .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage = wm831x_alive_ldo_set_voltage, .set_suspend_voltage = wm831x_alive_ldo_set_suspend_voltage, -- cgit v1.2.3 From 27eeabb7a1000de974e45cd007b3f5324dcee490 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 31 May 2012 17:40:22 +0800 Subject: regulator: wm8400: Use regulator_map_voltage_linear for wm8400_dcdc_ops wm8400_dcdc_ops uses simple linear voltage maps. Thus use regulator_map_voltage_linear is more efficient than using the default regulator_map_voltage_iterate. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8400-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 69a2b7ce5e4a..f365795d51c2 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -152,6 +152,7 @@ static struct regulator_ops wm8400_dcdc_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_mode = wm8400_dcdc_get_mode, -- cgit v1.2.3 From 133d4016f1783b21e9458430fa7c0c610c010037 Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Fri, 1 Jun 2012 13:17:14 +0900 Subject: regulator: MAX77686: Add Maxim 77686 regulator driver Add driver for support max77686 regulator. MAX77686 provides LDOs[1~26] and BUCKs[1~9]. It support to set or get the volatege of regulator on max77686 chip with using regmap. Signed-off-by: Chiwoong Byun Signed-off-by: Jonghwa Lee Signed-off-by: Myungjoo Ham Signed-off-by: Kyungmin Park Reviewed-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/max77686.c | 337 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 drivers/regulator/max77686.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c86b8864e411..00ffe05d8026 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -195,6 +195,14 @@ config REGULATOR_MAX8998 via I2C bus. The provided regulator is suitable for S3C6410 and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. +config REGULATOR_MAX77686 + tristate "Maxim 77686 regulator" + depends on MFD_MAX77686 + help + This driver controls a Maxim 77686 regulator + via I2C bus. The provided regulator is suitable for + Exynos-4 chips to control VARM and VINT voltages. + config REGULATOR_PCAP tristate "Motorola PCAP2 regulator driver" depends on EZX_PCAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 977fd46909ab..d8544539efec 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o +obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c new file mode 100644 index 000000000000..70046f8032fd --- /dev/null +++ b/drivers/regulator/max77686.c @@ -0,0 +1,337 @@ +/* + * max77686.c - Regulator driver for the Maxim 77686 + * + * Copyright (C) 2012 Samsung Electronics + * Chiwoong Byun + * Jonghwa Lee + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max8997.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX77686_LDO_MINUV 800000 +#define MAX77686_LDO_UVSTEP 50000 +#define MAX77686_LDO_LOW_MINUV 800000 +#define MAX77686_LDO_LOW_UVSTEP 25000 +#define MAX77686_BUCK_MINUV 750000 +#define MAX77686_BUCK_UVSTEP 50000 +#define MAX77686_DVS_MINUV 600000 +#define MAX77686_DVS_UVSTEP 12500 + +#define MAX77686_OPMODE_SHIFT 6 +#define MAX77686_OPMODE_BUCK234_SHIFT 4 +#define MAX77686_OPMODE_MASK 0x3 + +#define MAX77686_VSEL_MASK 0x3F +#define MAX77686_DVS_VSEL_MASK 0xFF + +#define MAX77686_RAMP_RATE_MASK 0xC0 + +#define MAX77686_REGULATORS MAX77686_REG_MAX +#define MAX77686_LDOS 26 + +enum max77686_ramp_rate { + RAMP_RATE_13P75MV, + RAMP_RATE_27P5MV, + RAMP_RATE_55MV, + RAMP_RATE_NO_CTRL, /* 100mV/us */ +}; + +struct max77686_data { + struct device *dev; + struct max77686_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; + int ramp_delay; /* in mV/us */ +}; + +static int max77686_set_dvs_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + struct max77686_data *max77686 = rdev_get_drvdata(rdev); + int ramp_rate[] = {13, 27, 55, 100}; + return (DIV_ROUND_UP(rdev->desc->uV_step + * abs(new_selector - old_selector), + ramp_rate[max77686->ramp_delay])); +} + +static int max77686_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, unsigned int new_selector) +{ + /* Unconditionally 100 mV/us */ + return (DIV_ROUND_UP(rdev->desc->uV_step + * abs(new_selector - old_selector), 100)); +} + +static struct regulator_ops max77686_ops = { + .list_voltage = regulator_list_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = max77686_set_voltage_time_sel, +}; + +static struct regulator_ops max77686_buck_dvs_ops = { + .list_voltage = regulator_list_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_voltage_time_sel = max77686_set_dvs_voltage_time_sel, +}; + +#define regulator_desc_ldo(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_LDO_MINUV, \ + .uV_step = MAX77686_LDO_UVSTEP, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_SHIFT, \ +} +#define regulator_desc_ldo_low(num) { \ + .name = "LDO"#num, \ + .id = MAX77686_LDO##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_LDO_LOW_MINUV, \ + .uV_step = MAX77686_LDO_LOW_UVSTEP, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_SHIFT, \ +} +#define regulator_desc_buck(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_BUCK_MINUV, \ + .uV_step = MAX77686_BUCK_UVSTEP, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_BUCK5OUT + (num - 5) * 2, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_BUCK5CTRL + (num - 5) * 2, \ + .enable_mask = MAX77686_OPMODE_MASK, \ +} +#define regulator_desc_buck1(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_BUCK_MINUV, \ + .uV_step = MAX77686_BUCK_UVSTEP, \ + .n_voltages = MAX77686_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_BUCK1OUT, \ + .vsel_mask = MAX77686_VSEL_MASK, \ + .enable_reg = MAX77686_REG_BUCK1CTRL, \ + .enable_mask = MAX77686_OPMODE_MASK, \ +} +#define regulator_desc_buck_dvs(num) { \ + .name = "BUCK"#num, \ + .id = MAX77686_BUCK##num, \ + .ops = &max77686_buck_dvs_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = MAX77686_DVS_MINUV, \ + .uV_step = MAX77686_DVS_UVSTEP, \ + .n_voltages = MAX77686_DVS_VSEL_MASK + 1, \ + .vsel_reg = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10, \ + .vsel_mask = MAX77686_DVS_VSEL_MASK, \ + .enable_reg = MAX77686_REG_BUCK2CTRL1 + (num - 2) * 10, \ + .enable_mask = MAX77686_OPMODE_MASK \ + << MAX77686_OPMODE_BUCK234_SHIFT, \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_ldo_low(1), + regulator_desc_ldo_low(2), + regulator_desc_ldo(3), + regulator_desc_ldo(4), + regulator_desc_ldo(5), + regulator_desc_ldo_low(6), + regulator_desc_ldo_low(7), + regulator_desc_ldo_low(8), + regulator_desc_ldo(9), + regulator_desc_ldo(10), + regulator_desc_ldo(11), + regulator_desc_ldo(12), + regulator_desc_ldo(13), + regulator_desc_ldo(14), + regulator_desc_ldo_low(15), + regulator_desc_ldo(16), + regulator_desc_ldo(17), + regulator_desc_ldo(18), + regulator_desc_ldo(19), + regulator_desc_ldo(20), + regulator_desc_ldo(21), + regulator_desc_ldo(22), + regulator_desc_ldo(23), + regulator_desc_ldo(24), + regulator_desc_ldo(25), + regulator_desc_ldo(26), + regulator_desc_buck1(1), + regulator_desc_buck_dvs(2), + regulator_desc_buck_dvs(3), + regulator_desc_buck_dvs(4), + regulator_desc_buck(5), + regulator_desc_buck(6), + regulator_desc_buck(7), + regulator_desc_buck(8), + regulator_desc_buck(9), +}; + +static __devinit int max77686_pmic_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev); + struct regulator_dev **rdev; + struct max77686_data *max77686; + int i, size; + int ret = 0; + struct regulator_config config; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data), + GFP_KERNEL); + if (!max77686) + return -ENOMEM; + + size = sizeof(struct regulator_dev *) * MAX77686_REGULATORS; + max77686->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!max77686->rdev) + return -ENOMEM; + + rdev = max77686->rdev; + max77686->dev = &pdev->dev; + max77686->iodev = iodev; + if (pdata) + max77686->num_regulators = pdata->num_regulators; + platform_set_drvdata(pdev, max77686); + + max77686->ramp_delay = RAMP_RATE_NO_CTRL; /* Set 0x3 for RAMP */ + regmap_update_bits(max77686->iodev->regmap, + MAX77686_REG_BUCK2CTRL1, MAX77686_RAMP_RATE_MASK, + max77686->ramp_delay << 6); + regmap_update_bits(max77686->iodev->regmap, + MAX77686_REG_BUCK3CTRL1, MAX77686_RAMP_RATE_MASK, + max77686->ramp_delay << 6); + regmap_update_bits(max77686->iodev->regmap, + MAX77686_REG_BUCK4CTRL1, MAX77686_RAMP_RATE_MASK, + max77686->ramp_delay << 6); + + if (pdata->num_regulators == MAX77686_REGULATORS) { + for (i = 0; i < MAX77686_REGULATORS; i++) { + config.dev = max77686->dev; + config.regmap = iodev->regmap; + config.driver_data = max77686; + config.init_data = pdata->regulators[i].initdata; + + rdev[i] = regulator_register(®ulators[i], &config); + + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max77686->dev, + "regulator init failed for %d\n", i); + rdev[i] = NULL; + goto err; + } + } + } else { + dev_err(max77686->dev, + "Lack of initial data for regulator's initialiation\n"); + return -EINVAL; + } + return 0; +err: + for (i = 0; i < MAX77686_REGULATORS; i++) { + if (rdev[i]) + regulator_unregister(rdev[i]); + } + return ret; +} + +static int __devexit max77686_pmic_remove(struct platform_device *pdev) +{ + struct max77686_data *max77686 = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max77686->rdev; + int i; + + for (i = 0; i < MAX77686_REGULATORS; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + return 0; +} + +static const struct platform_device_id max77686_pmic_id[] = { + {"max77686-pmic", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77686_pmic_id); + +static struct platform_driver max77686_pmic_driver = { + .driver = { + .name = "max77686-pmic", + .owner = THIS_MODULE, + }, + .probe = max77686_pmic_probe, + .remove = __devexit_p(max77686_pmic_remove), + .id_table = max77686_pmic_id, +}; + +static int __init max77686_pmic_init(void) +{ + return platform_driver_register(&max77686_pmic_driver); +} +subsys_initcall(max77686_pmic_init); + +static void __exit max77686_pmic_cleanup(void) +{ + platform_driver_unregister(&max77686_pmic_driver); +} +module_exit(max77686_pmic_cleanup); + +MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver"); +MODULE_AUTHOR("Chiwoong Byun "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From abcfaf23c7d8494bd3b3266f7a78e9efe6206ca4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 1 Jun 2012 12:16:25 +0800 Subject: regulator: fixed: Use of_match_ptr() for of_match_table entry Use the new of_match_ptr() macro for the of_match_table pointer entry to avoid having to #define match NULL. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index f09fe7b20e82..8bda3654ae51 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -296,8 +296,6 @@ static const struct of_device_id fixed_of_match[] __devinitconst = { {}, }; MODULE_DEVICE_TABLE(of, fixed_of_match); -#else -#define fixed_of_match NULL #endif static struct platform_driver regulator_fixed_voltage_driver = { @@ -306,7 +304,7 @@ static struct platform_driver regulator_fixed_voltage_driver = { .driver = { .name = "reg-fixed-voltage", .owner = THIS_MODULE, - .of_match_table = fixed_of_match, + .of_match_table = of_match_ptr(fixed_of_match), }, }; -- cgit v1.2.3 From 7e715b954a78debf021c8ad33a2cb4f462c245e3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 30 May 2012 12:47:26 +0800 Subject: regulator: Change ab8500 match names to reflect Device Tree The 'name' field in 'struct of_regulator_match' expects to match with its corresponding regulator device node in the Device Tree. This patch renames each of the regulators in the ab8500 regulator driver so this is true. Signed-off-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index f15807449011..290c289a653d 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -771,17 +771,17 @@ static __devinit int ab8500_regulator_register(struct platform_device *pdev, } static struct of_regulator_match ab8500_regulator_matches[] = { - { .name = "LDO-AUX1", .driver_data = (void *) AB8500_LDO_AUX1, }, - { .name = "LDO-AUX2", .driver_data = (void *) AB8500_LDO_AUX2, }, - { .name = "LDO-AUX3", .driver_data = (void *) AB8500_LDO_AUX3, }, - { .name = "LDO-INTCORE", .driver_data = (void *) AB8500_LDO_INTCORE, }, - { .name = "LDO-TVOUT", .driver_data = (void *) AB8500_LDO_TVOUT, }, - { .name = "LDO-USB", .driver_data = (void *) AB8500_LDO_USB, }, - { .name = "LDO-AUDIO", .driver_data = (void *) AB8500_LDO_AUDIO, }, - { .name = "LDO-ANAMIC1", .driver_data = (void *) AB8500_LDO_ANAMIC1, }, - { .name = "LDO-ANAMIC2", .driver_data = (void *) AB8500_LDO_ANAMIC2, }, - { .name = "LDO-DMIC", .driver_data = (void *) AB8500_LDO_DMIC, }, - { .name = "LDO-ANA", .driver_data = (void *) AB8500_LDO_ANA, }, + { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB8500_LDO_AUX1, }, + { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB8500_LDO_AUX2, }, + { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB8500_LDO_AUX3, }, + { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB8500_LDO_INTCORE, }, + { .name = "ab8500_ldo_tvout", .driver_data = (void *) AB8500_LDO_TVOUT, }, + { .name = "ab8500_ldo_usb", .driver_data = (void *) AB8500_LDO_USB, }, + { .name = "ab8500_ldo_audio", .driver_data = (void *) AB8500_LDO_AUDIO, }, + { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB8500_LDO_ANAMIC1, }, + { .name = "ab8500_ldo_amamic2", .driver_data = (void *) AB8500_LDO_ANAMIC2, }, + { .name = "ab8500_ldo_dmic", .driver_data = (void *) AB8500_LDO_DMIC, }, + { .name = "ab8500_ldo_ana", .driver_data = (void *) AB8500_LDO_ANA, }, }; static __devinit int -- cgit v1.2.3 From 392b309644c7223b5320b939aedf09b5a4f5a325 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 30 May 2012 12:47:27 +0800 Subject: regulator: Change db8500-prcmu match names to reflect Device Tree The 'name' field in 'struct of_regulator_match' expects to match with its corresponding regulator device node in the Device Tree. This patch renames each of the regulators in the db8500-prcum regulator driver so this is true. Signed-off-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/db8500-prcmu.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 968f97f3cb3d..9dbb491b6efa 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -452,26 +452,26 @@ static __devinit int db8500_regulator_register(struct platform_device *pdev, } static struct of_regulator_match db8500_regulator_matches[] = { - { .name = "db8500-vape", .driver_data = (void *) DB8500_REGULATOR_VAPE, }, - { .name = "db8500-varm", .driver_data = (void *) DB8500_REGULATOR_VARM, }, - { .name = "db8500-vmodem", .driver_data = (void *) DB8500_REGULATOR_VMODEM, }, - { .name = "db8500-vpll", .driver_data = (void *) DB8500_REGULATOR_VPLL, }, - { .name = "db8500-vsmps1", .driver_data = (void *) DB8500_REGULATOR_VSMPS1, }, - { .name = "db8500-vsmps2", .driver_data = (void *) DB8500_REGULATOR_VSMPS2, }, - { .name = "db8500-vsmps3", .driver_data = (void *) DB8500_REGULATOR_VSMPS3, }, - { .name = "db8500-vrf1", .driver_data = (void *) DB8500_REGULATOR_VRF1, }, - { .name = "db8500-sva-mmdsp", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAMMDSP, }, - { .name = "db8500-sva-mmdsp-ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAMMDSPRET, }, - { .name = "db8500-sva-pipe", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAPIPE, }, - { .name = "db8500-sia-mmdsp", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAMMDSP, }, - { .name = "db8500-sia-mmdsp-ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAMMDSPRET, }, - { .name = "db8500-sia-pipe", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAPIPE, }, - { .name = "db8500-sga", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SGA, }, - { .name = "db8500-b2r2-mcde", .driver_data = (void *) DB8500_REGULATOR_SWITCH_B2R2_MCDE, }, - { .name = "db8500-esram12", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM12, }, - { .name = "db8500-esram12-ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM12RET, }, - { .name = "db8500-esram34", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34, }, - { .name = "db8500-esram34-ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34RET, }, + { .name = "db8500_vape", .driver_data = (void *) DB8500_REGULATOR_VAPE, }, + { .name = "db8500_varm", .driver_data = (void *) DB8500_REGULATOR_VARM, }, + { .name = "db8500_vmodem", .driver_data = (void *) DB8500_REGULATOR_VMODEM, }, + { .name = "db8500_vpll", .driver_data = (void *) DB8500_REGULATOR_VPLL, }, + { .name = "db8500_vsmps1", .driver_data = (void *) DB8500_REGULATOR_VSMPS1, }, + { .name = "db8500_vsmps2", .driver_data = (void *) DB8500_REGULATOR_VSMPS2, }, + { .name = "db8500_vsmps3", .driver_data = (void *) DB8500_REGULATOR_VSMPS3, }, + { .name = "db8500_vrf1", .driver_data = (void *) DB8500_REGULATOR_VRF1, }, + { .name = "db8500_sva_mmdsp", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAMMDSP, }, + { .name = "db8500_sva_mmdsp_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAMMDSPRET, }, + { .name = "db8500_sva_pipe", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SVAPIPE, }, + { .name = "db8500_sia_mmdsp", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAMMDSP, }, + { .name = "db8500_sia_mmdsp_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAMMDSPRET, }, + { .name = "db8500_sia_pipe", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SIAPIPE, }, + { .name = "db8500_sga", .driver_data = (void *) DB8500_REGULATOR_SWITCH_SGA, }, + { .name = "db8500_b2r2_mcde", .driver_data = (void *) DB8500_REGULATOR_SWITCH_B2R2_MCDE, }, + { .name = "db8500_esram12", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM12, }, + { .name = "db8500_esram12_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM12RET, }, + { .name = "db8500_esram34", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34, }, + { .name = "db8500_esram34_ret", .driver_data = (void *) DB8500_REGULATOR_SWITCH_ESRAM34RET, }, }; static __devinit int -- cgit v1.2.3 From fcbb71f6f8084b74fef54e40c9e515105ccc4e6e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 3 Jun 2012 23:12:16 +0800 Subject: regulator: wm8350: Use regulator_map_voltage_linear for wm8350_dcdc_ops wm8350_dcdc_ops uses simple linear voltage maps. Thus use regulator_map_voltage_linear is more efficient than using the default regulator_map_voltage_iterate. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index a618284fc45e..33c37f9c0e41 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1036,6 +1036,7 @@ static struct regulator_ops wm8350_dcdc_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, -- cgit v1.2.3 From 2e3f7f26e1323fb302921c4575a499710636d531 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 3 Jun 2012 22:46:14 +0800 Subject: regulator: max77686: Use regulator_map_voltage_linear for simple linear mappings Both max77686_ops and max77686_buck_dvs_ops use simple linear voltage maps. Thus use regulator_map_voltage_linear is more efficient than using the defult regulator_map_voltage_iterate. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 70046f8032fd..4e9f4f3f062b 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -90,6 +90,7 @@ static int max77686_set_voltage_time_sel(struct regulator_dev *rdev, static struct regulator_ops max77686_ops = { .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -100,6 +101,7 @@ static struct regulator_ops max77686_ops = { static struct regulator_ops max77686_buck_dvs_ops = { .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, -- cgit v1.2.3 From b924b2047045844644e04b3c6e8308b2114afe7e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 4 Jun 2012 00:56:15 -0700 Subject: ARM: OMAP: Make FS USB omap1 only As the FS USB code is not being actively used for omap2+ there's no point keeping it around for omap2+. Let's make the FS USB platform init code omap1 only so we can remove the last user of omap_read/write for omap2+, and simplify things for further USB, DMA, and device tree related work. While at it, also group the mach includes for the related drivers. Cc: linux-usb@vger.kernel.org Cc: Kyungmin Park Acked-by: Felipe Balbi Signed-off-by: Tony Lindgren --- drivers/usb/gadget/Kconfig | 2 +- drivers/usb/gadget/omap_udc.c | 3 ++- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ohci-omap.c | 7 ++++--- drivers/usb/otg/isp1301_omap.c | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bddc8fd9a7be..271ca161d7ef 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -185,7 +185,7 @@ config USB_FUSB300 config USB_OMAP tristate "OMAP USB Device Controller" - depends on ARCH_OMAP + depends on ARCH_OMAP1 select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG select USB_OTG_UTILS if ARCH_OMAP help diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 7ba32469c5bd..2acf06cd9225 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -44,7 +44,8 @@ #include #include -#include + +#include #include "omap_udc.h" diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 83e58df29fe3..dcfaaa91a3fb 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -308,7 +308,7 @@ config USB_OHCI_HCD config USB_OHCI_HCD_OMAP1 bool "OHCI support for OMAP1/2 chips" - depends on USB_OHCI_HCD && (ARCH_OMAP1 || ARCH_OMAP2) + depends on USB_OHCI_HCD && ARCH_OMAP1 default y ---help--- Enables support for the OHCI controller on OMAP1/2 chips. diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 9ce35d0d9d5d..b02c344e2cc9 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -20,14 +20,15 @@ #include #include -#include #include #include #include -#include #include -#include + +#include +#include +#include /* OMAP-1510 OHCI has its own MMU for DMA */ diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index 70cf5d7bca48..e0558dfcfafc 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -36,9 +36,9 @@ #include #include -#include #include +#include #ifndef DEBUG #undef VERBOSE -- cgit v1.2.3 From 87c9ea76a242c2f9063e2a8f3e90846c932c61a7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Sun, 3 Jun 2012 21:56:21 +0530 Subject: mtip32xx: Remove version.h header file inclusion version.h header file inclusion is no longer required. Signed-off-by: Sachin Kamat --- drivers/block/mtip32xx/mtip32xx.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index b2c88da26b2a..adb1aae3b752 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -26,7 +26,6 @@ #include #include #include -#include /* Offset of Subsystem Device ID in pci confoguration space */ #define PCI_SUBSYSTEM_DEVICEID 0x2E -- cgit v1.2.3 From fc492f9bd13cb4c7c1299f5d390a203c1ad9a85b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 9 May 2012 22:27:41 +0100 Subject: regulator: wm8350: Convert LDOs to set_voltage_sel() Since there are two linear ranges for the LDO voltages provide a voltage mapping function and then use regulator_set_voltage_sel_regmap(). Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 38 ++++++++---------------------------- 1 file changed, 8 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 33c37f9c0e41..ca678eea79de 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -671,13 +671,12 @@ static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) return 0; } -static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, - int max_uV, unsigned *selector) +static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) { - struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int volt_reg, ldo = rdev_get_id(rdev), mV, min_mV = min_uV / 1000, - max_mV = max_uV / 1000; - u16 val; + int mV; + int min_mV = min_uV / 1000; + int max_mV = max_uV / 1000; if (min_mV < 900 || min_mV > 3300) return -EINVAL; @@ -698,29 +697,7 @@ static int wm8350_ldo_set_voltage(struct regulator_dev *rdev, int min_uV, BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); } - switch (ldo) { - case WM8350_LDO_1: - volt_reg = WM8350_LDO1_CONTROL; - break; - case WM8350_LDO_2: - volt_reg = WM8350_LDO2_CONTROL; - break; - case WM8350_LDO_3: - volt_reg = WM8350_LDO3_CONTROL; - break; - case WM8350_LDO_4: - volt_reg = WM8350_LDO4_CONTROL; - break; - default: - return -EINVAL; - } - - *selector = mV; - - /* all LDOs have same mV bits */ - val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; - wm8350_reg_write(wm8350, volt_reg, val | mV); - return 0; + return mV; } static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, @@ -1058,7 +1035,8 @@ static struct regulator_ops wm8350_dcdc2_5_ops = { }; static struct regulator_ops wm8350_ldo_ops = { - .set_voltage = wm8350_ldo_set_voltage, + .map_voltage = wm8350_ldo_map_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = wm8350_ldo_list_voltage, .enable = regulator_enable_regmap, -- cgit v1.2.3 From 9152c36a3b37a95c1161508dc105719456d7f7d0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 4 Jun 2012 09:41:38 +0800 Subject: regulator: core: Use map_voltage_linear() if list_voltage_linear() is in use and nothing is set Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 8521e0d6b3bc..3cbe3129ed3b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2118,12 +2118,18 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV, &selector); } else if (rdev->desc->ops->set_voltage_sel) { - if (rdev->desc->ops->map_voltage) + if (rdev->desc->ops->map_voltage) { ret = rdev->desc->ops->map_voltage(rdev, min_uV, max_uV); - else - ret = regulator_map_voltage_iterate(rdev, min_uV, - max_uV); + } else { + if (rdev->desc->ops->list_voltage == + regulator_list_voltage_linear) + ret = regulator_map_voltage_linear(rdev, + min_uV, max_uV); + else + ret = regulator_map_voltage_iterate(rdev, + min_uV, max_uV); + } if (ret >= 0) { selector = ret; -- cgit v1.2.3 From 96d25221d28646fc2ce0a64a725c67c1ee2f2f0a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 4 Jun 2012 12:56:58 +0800 Subject: regulator: max1586: Convert max1586_v6_ops to regulator_list_voltage_table Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max1586.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index b9444ee08da9..f215195de917 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -47,6 +47,14 @@ struct max1586_data { struct regulator_dev *rdev[0]; }; +/* + * V6 voltage + * On I2C bus, sending a "x" byte to the max1586 means : + * set V6 to either 0V, 1.8V, 2.5V, 3V depending on (x & 0x3) + * As regulator framework doesn't accept voltages to be 0V, we use 1uV. + */ +static int v6_voltages_uv[] = { 1, 1800000, 2500000, 3000000 }; + /* * V3 voltage * On I2C bus, sending a "x" byte to the max1586 means : @@ -97,19 +105,6 @@ static int max1586_v3_list(struct regulator_dev *rdev, unsigned selector) return max1586_v3_calc_voltage(max1586, selector); } -/* - * V6 voltage - * On I2C bus, sending a "x" byte to the max1586 means : - * set V6 to either 0V, 1.8V, 2.5V, 3V depending on (x & 0x3) - * As regulator framework doesn't accept voltages to be 0V, we use 1uV. - */ -static int max1586_v6_calc_voltage(unsigned selector) -{ - static int voltages_uv[] = { 1, 1800000, 2500000, 3000000 }; - - return voltages_uv[selector]; -} - static int max1586_v6_set(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned int *selector) { @@ -130,23 +125,16 @@ static int max1586_v6_set(struct regulator_dev *rdev, int min_uV, int max_uV, else if (min_uV >= 3000000) *selector = 3; - if (max1586_v6_calc_voltage(*selector) > max_uV) + if (rdev->desc->volt_table[*selector] > max_uV) return -EINVAL; dev_dbg(&client->dev, "changing voltage v6 to %dmv\n", - max1586_v6_calc_voltage(*selector) / 1000); + rdev->desc->volt_table[*selector] / 1000); v6_prog = I2C_V6_SELECT | (u8) *selector; return i2c_smbus_write_byte(client, v6_prog); } -static int max1586_v6_list(struct regulator_dev *rdev, unsigned selector) -{ - if (selector > MAX1586_V6_MAX_VSEL) - return -EINVAL; - return max1586_v6_calc_voltage(selector); -} - /* * The Maxim 1586 controls V3 and V6 voltages, but offers no way of reading back * the set up value. @@ -158,7 +146,7 @@ static struct regulator_ops max1586_v3_ops = { static struct regulator_ops max1586_v6_ops = { .set_voltage = max1586_v6_set, - .list_voltage = max1586_v6_list, + .list_voltage = regulator_list_voltage_table, }; static const struct regulator_desc max1586_reg[] = { @@ -176,6 +164,7 @@ static const struct regulator_desc max1586_reg[] = { .ops = &max1586_v6_ops, .type = REGULATOR_VOLTAGE, .n_voltages = MAX1586_V6_MAX_VSEL + 1, + .volt_table = v6_voltages_uv, .owner = THIS_MODULE, }, }; -- cgit v1.2.3 From 0d032731178d78450296bdf0b27562476594c298 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 4 Jun 2012 12:58:24 +0800 Subject: regulator: max1586: Convert max1586_v6_ops to set_voltage_sel Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max1586.c | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index f215195de917..126c7d8f31c6 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -105,33 +105,16 @@ static int max1586_v3_list(struct regulator_dev *rdev, unsigned selector) return max1586_v3_calc_voltage(max1586, selector); } -static int max1586_v6_set(struct regulator_dev *rdev, int min_uV, int max_uV, - unsigned int *selector) +static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) { struct i2c_client *client = rdev_get_drvdata(rdev); u8 v6_prog; - if (min_uV < MAX1586_V6_MIN_UV || min_uV > MAX1586_V6_MAX_UV) - return -EINVAL; - if (max_uV < MAX1586_V6_MIN_UV || max_uV > MAX1586_V6_MAX_UV) - return -EINVAL; - - if (min_uV < 1800000) - *selector = 0; - else if (min_uV < 2500000) - *selector = 1; - else if (min_uV < 3000000) - *selector = 2; - else if (min_uV >= 3000000) - *selector = 3; - - if (rdev->desc->volt_table[*selector] > max_uV) - return -EINVAL; - dev_dbg(&client->dev, "changing voltage v6 to %dmv\n", - rdev->desc->volt_table[*selector] / 1000); + rdev->desc->volt_table[selector] / 1000); - v6_prog = I2C_V6_SELECT | (u8) *selector; + v6_prog = I2C_V6_SELECT | (u8) selector; return i2c_smbus_write_byte(client, v6_prog); } @@ -145,7 +128,7 @@ static struct regulator_ops max1586_v3_ops = { }; static struct regulator_ops max1586_v6_ops = { - .set_voltage = max1586_v6_set, + .set_voltage_sel = max1586_v6_set_voltage_sel, .list_voltage = regulator_list_voltage_table, }; -- cgit v1.2.3 From 74adfee53b8f05b4a9db0848d19399db2cef6cbf Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 4 Jun 2012 10:19:18 +0800 Subject: regulator: max77686: Fix checkpatch warnings Fix below checkpatch warnings: $ scripts/checkpatch.pl -f drivers/regulator/max77686.c ERROR: return is not a function, parentheses are not required + return (DIV_ROUND_UP(rdev->desc->uV_step WARNING: line over 80 characters + .ops = &max77686_buck_dvs_ops, \ total: 1 errors, 1 warnings, 339 lines checked Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 4e9f4f3f062b..b76a0389d0b8 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -75,17 +75,18 @@ static int max77686_set_dvs_voltage_time_sel(struct regulator_dev *rdev, { struct max77686_data *max77686 = rdev_get_drvdata(rdev); int ramp_rate[] = {13, 27, 55, 100}; - return (DIV_ROUND_UP(rdev->desc->uV_step - * abs(new_selector - old_selector), - ramp_rate[max77686->ramp_delay])); + + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + ramp_rate[max77686->ramp_delay]); } static int max77686_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { /* Unconditionally 100 mV/us */ - return (DIV_ROUND_UP(rdev->desc->uV_step - * abs(new_selector - old_selector), 100)); + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), 100); } static struct regulator_ops max77686_ops = { @@ -171,7 +172,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { #define regulator_desc_buck_dvs(num) { \ .name = "BUCK"#num, \ .id = MAX77686_BUCK##num, \ - .ops = &max77686_buck_dvs_ops, \ + .ops = &max77686_buck_dvs_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ .min_uV = MAX77686_DVS_MINUV, \ -- cgit v1.2.3 From 29a6b6c060445eb46528785d51a2d8b0e6d898c4 Mon Sep 17 00:00:00 2001 From: Raffaele Recalcati Date: Sun, 3 Jun 2012 10:43:43 +0000 Subject: net/ethernet: ks8851_mll mac address configuration support added Signed-off-by: Raffaele Recalcati Signed-off-by: David S. Miller --- drivers/net/ethernet/micrel/ks8851_mll.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index 5ffde23ac8fb..70bd329882c6 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -35,7 +35,7 @@ #include #include #include -#include +#include #define DRV_NAME "ks8851_mll" @@ -1515,6 +1515,7 @@ static int __devinit ks8851_probe(struct platform_device *pdev) struct net_device *netdev; struct ks_net *ks; u16 id, data; + struct ks8851_mll_platform_data *pdata; io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0); io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -1596,17 +1597,27 @@ static int __devinit ks8851_probe(struct platform_device *pdev) ks_disable_qmu(ks); ks_setup(ks); ks_setup_int(ks); - memcpy(netdev->dev_addr, ks->mac_addr, 6); data = ks_rdreg16(ks, KS_OBCR); ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA); - /** - * If you want to use the default MAC addr, - * comment out the 2 functions below. - */ + /* overwriting the default MAC address */ + pdata = pdev->dev.platform_data; + if (!pdata) { + netdev_err(netdev, "No platform data\n"); + err = -ENODEV; + goto err_register; + } + memcpy(ks->mac_addr, pdata->mac_addr, 6); + if (!is_valid_ether_addr(ks->mac_addr)) { + /* Use random MAC address if none passed */ + random_ether_addr(ks->mac_addr); + netdev_info(netdev, "Using random mac address\n"); + } + netdev_info(netdev, "Mac address is: %pM\n", ks->mac_addr); + + memcpy(netdev->dev_addr, ks->mac_addr, 6); - random_ether_addr(netdev->dev_addr); ks_set_mac(ks, netdev->dev_addr); id = ks_rdreg16(ks, KS_CIDER); -- cgit v1.2.3 From 13497f58670c4e7a30170738f07b17d90011c785 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Mon, 4 Jun 2012 06:36:22 +0000 Subject: stmmac: remove two useless initialisations This patch removes two useless initialisations in the stmmac_rx and stmmac_tx functions. In the former, the count variable was reset twice and in the stmmac_tx we only need to increment the dirty pointer w/o setting the entry variable. v2: review the subject and comment that was not clear in my first version. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 70966330f44e..0caae72cda89 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -677,7 +677,7 @@ static void stmmac_tx(struct stmmac_priv *priv) priv->hw->desc->release_tx_desc(p); - entry = (++priv->dirty_tx) % txsize; + priv->dirty_tx++; } if (unlikely(netif_queue_stopped(priv->dev) && stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) { @@ -1307,7 +1307,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) display_ring(priv->dma_rx, rxsize); } #endif - count = 0; while (!priv->hw->desc->get_rx_owner(p)) { int status; -- cgit v1.2.3 From fe7c580073280c15bb4eb4f82bf20dddc1a68383 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 15 May 2012 15:57:06 +0200 Subject: NFC: Add target mode protocols to the polling loop startup routine Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 39 +++++++++++++++++++++++++++++---------- drivers/nfc/pn544_hci.c | 10 ++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 19110f0eb15f..38a523c62132 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1078,27 +1078,23 @@ stop_poll: return 0; } -static int pn533_start_poll(struct nfc_dev *nfc_dev, u32 protocols) +static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) +{ + return 0; +} + +static int pn533_start_im_poll(struct nfc_dev *nfc_dev, u32 protocols) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_poll_modulations *start_mod; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s - protocols=0x%x", __func__, - protocols); - if (dev->poll_mod_count) { nfc_dev_err(&dev->interface->dev, "Polling operation already" " active"); return -EBUSY; } - if (dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, "Cannot poll with a target" - " already activated"); - return -EBUSY; - } - pn533_poll_create_mod_list(dev, protocols); if (!dev->poll_mod_count) { @@ -1135,6 +1131,29 @@ error: return rc; } +static int pn533_start_poll(struct nfc_dev *nfc_dev, + u32 im_protocols, u32 tm_protocols) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + + nfc_dev_dbg(&dev->interface->dev, + "%s: im protocols 0x%x tm protocols 0x%x", + __func__, im_protocols, tm_protocols); + + if (dev->tgt_active_prot) { + nfc_dev_err(&dev->interface->dev, + "Cannot poll with a target already activated"); + return -EBUSY; + } + + if (!tm_protocols) + return pn533_start_im_poll(nfc_dev, im_protocols); + else if (!im_protocols) + return pn533_init_target(nfc_dev, tm_protocols); + else + return -EINVAL; +} + static void pn533_stop_poll(struct nfc_dev *nfc_dev) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index 281f18c2fb82..457eac35dc74 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -576,7 +576,8 @@ static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) return pn544_hci_i2c_write(client, skb->data, skb->len); } -static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols) +static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, + u32 im_protocols, u32 tm_protocols) { struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); u8 phases = 0; @@ -584,7 +585,8 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols) u8 duration[2]; u8 activated; - pr_info(DRIVER_DESC ": %s protocols = %d\n", __func__, protocols); + pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n", + __func__, im_protocols, tm_protocols); r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE, NFC_HCI_EVT_END_OPERATION, NULL, 0); @@ -604,10 +606,10 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, u32 protocols) if (r < 0) return r; - if (protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | + if (im_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_JEWEL_MASK)) phases |= 1; /* Type A */ - if (protocols & NFC_PROTO_FELICA_MASK) { + if (im_protocols & NFC_PROTO_FELICA_MASK) { phases |= (1 << 2); /* Type F 212 */ phases |= (1 << 3); /* Type F 424 */ } -- cgit v1.2.3 From ad3823cef650bdc1ca9e7bf1a01b87ad3c0425de Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 30 May 2012 23:54:55 +0200 Subject: NFC: Implement pn533 target mode polling loop We only want to support p2p target mode for now, no host card emulation. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 38a523c62132..605a08a62e45 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -74,6 +74,8 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_IN_RELEASE 0x52 #define PN533_CMD_IN_JUMP_FOR_DEP 0x56 +#define PN533_CMD_TG_INIT_AS_TARGET 0x8c + #define PN533_CMD_RESPONSE(cmd) (cmd + 1) /* PN533 Return codes */ @@ -253,6 +255,25 @@ struct pn533_cmd_jump_dep_response { u8 gt[]; } __packed; + +/* PN533_TG_INIT_AS_TARGET */ +#define PN533_INIT_TARGET_PASSIVE 0x1 +#define PN533_INIT_TARGET_DEP 0x2 + +struct pn533_cmd_init_target { + u8 mode; + u8 mifare[6]; + u8 felica[18]; + u8 nfcid3[10]; + u8 gb_len; + u8 gb[]; +} __packed; + +struct pn533_cmd_init_target_response { + u8 mode; + u8 cmd[]; +} __packed; + struct pn533 { struct usb_device *udev; struct usb_interface *interface; @@ -1078,11 +1099,88 @@ stop_poll: return 0; } -static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) +static int pn533_init_target_frame(struct pn533_frame *frame, + u8 *gb, size_t gb_len) { + struct pn533_cmd_init_target *cmd; + size_t cmd_len; + + cmd_len = sizeof(struct pn533_cmd_init_target) + gb_len + 1; + cmd = kzalloc(cmd_len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + pn533_tx_frame_init(frame, PN533_CMD_TG_INIT_AS_TARGET); + + /* DEP support only */ + cmd->mode |= PN533_INIT_TARGET_DEP; + get_random_bytes(cmd->nfcid3, 10); + cmd->gb_len = gb_len; + memcpy(cmd->gb, gb, gb_len); + /* Len Tk */ + cmd->gb[gb_len] = 0; + + memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), cmd, cmd_len); + frame->datalen += cmd_len; + + pn533_tx_frame_finish(frame); + return 0; } +static int pn533_init_target_complete(struct pn533 *dev, void *arg, + u8 *params, int params_len) +{ + struct pn533_cmd_init_target_response *resp; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + if (params_len < 0) { + nfc_dev_err(&dev->interface->dev, + "Error %d when starting as a target", + params_len); + + return params_len; + } + + resp = (struct pn533_cmd_init_target_response *) params; + + nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x\n", resp->mode); + + return 0; +} + +static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + u8 *gb; + size_t gb_len; + int rc; + + pn533_poll_reset_mod_list(dev); + + gb = nfc_get_local_general_bytes(nfc_dev, &gb_len); + if (gb == NULL) + return -ENOMEM; + + rc = pn533_init_target_frame(dev->out_frame, gb, gb_len); + if (rc < 0) + return rc; + + rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, + dev->in_maxlen, + pn533_init_target_complete, + NULL, GFP_KERNEL); + + if (rc) + nfc_dev_err(&dev->interface->dev, + "Error %d when trying to initiate as a target", rc); + + dev->poll_mod_count++; + + return rc; +} + static int pn533_start_im_poll(struct nfc_dev *nfc_dev, u32 protocols) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); @@ -1146,12 +1244,13 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev, return -EBUSY; } - if (!tm_protocols) + if (im_protocols) return pn533_start_im_poll(nfc_dev, im_protocols); - else if (!im_protocols) + + if (tm_protocols) return pn533_init_target(nfc_dev, tm_protocols); - else - return -EINVAL; + + return -EINVAL; } static void pn533_stop_poll(struct nfc_dev *nfc_dev) -- cgit v1.2.3 From fc40a8c1a06ab7db45da790693dd9802612a055c Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 1 Jun 2012 13:21:13 +0200 Subject: NFC: Add target mode activation netlink event Userspace gets a netlink event upon target mode activation. The LLCP layer is also signaled when we get an ATR_REQ in order to get the remote general bytes. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 605a08a62e45..c6b9bc5ac6e6 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -260,6 +260,10 @@ struct pn533_cmd_jump_dep_response { #define PN533_INIT_TARGET_PASSIVE 0x1 #define PN533_INIT_TARGET_DEP 0x2 +#define PN533_INIT_TARGET_RESP_FRAME_MASK 0x3 +#define PN533_INIT_TARGET_RESP_ACTIVE 0x1 +#define PN533_INIT_TARGET_RESP_DEP 0x4 + struct pn533_cmd_init_target { u8 mode; u8 mifare[6]; @@ -1128,10 +1132,13 @@ static int pn533_init_target_frame(struct pn533_frame *frame, return 0; } +#define ATR_REQ_GB_OFFSET 17 static int pn533_init_target_complete(struct pn533 *dev, void *arg, u8 *params, int params_len) { struct pn533_cmd_init_target_response *resp; + u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb; + size_t gb_len; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1143,11 +1150,27 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg, return params_len; } + if (params_len < ATR_REQ_GB_OFFSET + 1) + return -EINVAL; + resp = (struct pn533_cmd_init_target_response *) params; - nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x\n", resp->mode); + nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x param len %d\n", + resp->mode, params_len); - return 0; + frame = resp->mode & PN533_INIT_TARGET_RESP_FRAME_MASK; + if (frame == PN533_INIT_TARGET_RESP_ACTIVE) + comm_mode = NFC_COMM_ACTIVE; + + /* Again, only DEP */ + if ((resp->mode & PN533_INIT_TARGET_RESP_DEP) == 0) + return -EOPNOTSUPP; + + gb = resp->cmd + ATR_REQ_GB_OFFSET; + gb_len = params_len - (ATR_REQ_GB_OFFSET + 1); + + return nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, + comm_mode, gb, gb_len); } static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) -- cgit v1.2.3 From be9ae4ce4ee66e211815122ab4f41913efed4fec Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 16 May 2012 15:55:48 +0200 Subject: NFC: Introduce target mode tx ops And rename the initiator mode data exchange ops for consistency sake. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index c6b9bc5ac6e6..fd94c6f5d6a8 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1691,9 +1691,9 @@ error: return 0; } -static int pn533_data_exchange(struct nfc_dev *nfc_dev, - struct nfc_target *target, struct sk_buff *skb, - data_exchange_cb_t cb, void *cb_context) +static int pn533_transceive(struct nfc_dev *nfc_dev, + struct nfc_target *target, struct sk_buff *skb, + data_exchange_cb_t cb, void *cb_context) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_frame *out_frame, *in_frame; @@ -1853,7 +1853,7 @@ struct nfc_ops pn533_nfc_ops = { .stop_poll = pn533_stop_poll, .activate_target = pn533_activate_target, .deactivate_target = pn533_deactivate_target, - .data_exchange = pn533_data_exchange, + .im_transceive = pn533_transceive, }; static int pn533_probe(struct usb_interface *interface, -- cgit v1.2.3 From 103b34cf5fe2bb72c38afa8523dd206e6ef3b5fe Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 00:07:51 +0200 Subject: NFC: Implement the pn533 target mode data fetching routine This one needs to be called as soon as we are activated as a target, for the pn533 to receive the first SYMM and keep the LLCP link alive. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index fd94c6f5d6a8..6a506e267d20 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -75,6 +75,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_IN_JUMP_FOR_DEP 0x56 #define PN533_CMD_TG_INIT_AS_TARGET 0x8c +#define PN533_CMD_TG_GET_DATA 0x86 #define PN533_CMD_RESPONSE(cmd) (cmd + 1) @@ -83,6 +84,9 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_MI_MASK 0x40 #define PN533_CMD_RET_SUCCESS 0x00 +/* PN533 status codes */ +#define PN533_STATUS_TARGET_RELEASED 0x29 + struct pn533; typedef int (*pn533_cmd_complete_t) (struct pn533 *dev, void *arg, @@ -296,6 +300,7 @@ struct pn533 { struct workqueue_struct *wq; struct work_struct cmd_work; struct work_struct mi_work; + struct work_struct tg_work; struct pn533_frame *wq_in_frame; int wq_in_error; @@ -1132,6 +1137,68 @@ static int pn533_init_target_frame(struct pn533_frame *frame, return 0; } +#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3) +#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 +static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, + u8 *params, int params_len) +{ + struct sk_buff *skb_resp = arg; + struct pn533_frame *in_frame = (struct pn533_frame *) skb_resp->data; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + if (params_len < 0) { + nfc_dev_err(&dev->interface->dev, + "Error %d when starting as a target", + params_len); + + return params_len; + } + + if (params_len > 0 && params[0] != 0) { + nfc_tm_deactivated(dev->nfc_dev); + + kfree_skb(skb_resp); + return 0; + } + + skb_put(skb_resp, PN533_FRAME_SIZE(in_frame)); + skb_pull(skb_resp, PN533_CMD_DATAEXCH_HEAD_LEN); + skb_trim(skb_resp, skb_resp->len - PN533_FRAME_TAIL_SIZE); + + return nfc_tm_data_received(dev->nfc_dev, skb_resp); +} + +static void pn533_wq_tg_get_data(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, tg_work); + struct pn533_frame *in_frame; + struct sk_buff *skb_resp; + size_t skb_resp_len; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + skb_resp_len = PN533_CMD_DATAEXCH_HEAD_LEN + + PN533_CMD_DATAEXCH_DATA_MAXLEN + + PN533_FRAME_TAIL_SIZE; + + skb_resp = nfc_alloc_recv_skb(skb_resp_len, GFP_KERNEL); + if (!skb_resp) + return; + + in_frame = (struct pn533_frame *)skb_resp->data; + + pn533_tx_frame_init(dev->out_frame, PN533_CMD_TG_GET_DATA); + pn533_tx_frame_finish(dev->out_frame); + + pn533_send_cmd_frame_async(dev, dev->out_frame, in_frame, + skb_resp_len, + pn533_tm_get_data_complete, + skb_resp, GFP_KERNEL); + + return; +} + #define ATR_REQ_GB_OFFSET 17 static int pn533_init_target_complete(struct pn533 *dev, void *arg, u8 *params, int params_len) @@ -1139,6 +1206,7 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg, struct pn533_cmd_init_target_response *resp; u8 frame, comm_mode = NFC_COMM_PASSIVE, *gb; size_t gb_len; + int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1169,8 +1237,17 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg, gb = resp->cmd + ATR_REQ_GB_OFFSET; gb_len = params_len - (ATR_REQ_GB_OFFSET + 1); - return nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, - comm_mode, gb, gb_len); + rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, + comm_mode, gb, gb_len); + if (rc < 0) { + nfc_dev_err(&dev->interface->dev, + "Error when signaling target activation"); + return rc; + } + + queue_work(dev->wq, &dev->tg_work); + + return 0; } static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) @@ -1553,9 +1630,6 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev) return 0; } -#define PN533_CMD_DATAEXCH_HEAD_LEN (sizeof(struct pn533_frame) + 3) -#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 - static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb) { int payload_len = skb->len; @@ -1920,6 +1994,7 @@ static int pn533_probe(struct usb_interface *interface, INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete); INIT_WORK(&dev->mi_work, pn533_wq_mi_recv); + INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); dev->wq = alloc_workqueue("pn533", WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, 1); -- cgit v1.2.3 From dadb06f270ad7cd9572b82995f6261f8ca4620ac Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 00:09:11 +0200 Subject: NFC: Implement the pn533 target mode Tx op Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 6a506e267d20..db4078765ac9 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -76,6 +76,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_TG_INIT_AS_TARGET 0x8c #define PN533_CMD_TG_GET_DATA 0x86 +#define PN533_CMD_TG_SET_DATA 0x8e #define PN533_CMD_RESPONSE(cmd) (cmd + 1) @@ -1630,7 +1631,8 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev) return 0; } -static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb) +static int pn533_build_tx_frame(struct pn533 *dev, struct sk_buff *skb, + bool target) { int payload_len = skb->len; struct pn533_frame *out_frame; @@ -1647,14 +1649,20 @@ static int pn533_data_exchange_tx_frame(struct pn533 *dev, struct sk_buff *skb) return -ENOSYS; } - skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN); - out_frame = (struct pn533_frame *) skb->data; + if (target == true) { + skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN); + out_frame = (struct pn533_frame *) skb->data; - pn533_tx_frame_init(out_frame, PN533_CMD_IN_DATA_EXCHANGE); + pn533_tx_frame_init(out_frame, PN533_CMD_IN_DATA_EXCHANGE); + tg = 1; + memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), &tg, sizeof(u8)); + out_frame->datalen += sizeof(u8); + } else { + skb_push(skb, PN533_CMD_DATAEXCH_HEAD_LEN - 1); + out_frame = (struct pn533_frame *) skb->data; + pn533_tx_frame_init(out_frame, PN533_CMD_TG_SET_DATA); + } - tg = 1; - memcpy(PN533_FRAME_CMD_PARAMS_PTR(out_frame), &tg, sizeof(u8)); - out_frame->datalen += sizeof(u8); /* The data is already in the out_frame, just update the datalen */ out_frame->datalen += payload_len; @@ -1785,7 +1793,7 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, goto error; } - rc = pn533_data_exchange_tx_frame(dev, skb); + rc = pn533_build_tx_frame(dev, skb, true); if (rc) goto error; @@ -1833,6 +1841,61 @@ error: return rc; } +static int pn533_tm_send_complete(struct pn533 *dev, void *arg, + u8 *params, int params_len) +{ + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + if (params_len < 0) { + nfc_dev_err(&dev->interface->dev, + "Error %d when sending data", + params_len); + + return params_len; + } + + if (params_len > 0 && params[0] != 0) { + nfc_tm_deactivated(dev->nfc_dev); + + return 0; + } + + queue_work(dev->wq, &dev->tg_work); + + return 0; +} + +static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct pn533_frame *out_frame; + int rc; + + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + + rc = pn533_build_tx_frame(dev, skb, false); + if (rc) + goto error; + + out_frame = (struct pn533_frame *) skb->data; + + rc = pn533_send_cmd_frame_async(dev, out_frame, dev->in_frame, + dev->in_maxlen, pn533_tm_send_complete, + NULL, GFP_KERNEL); + if (rc) { + nfc_dev_err(&dev->interface->dev, + "Error %d when trying to send data", rc); + goto error; + } + + return 0; + +error: + kfree_skb(skb); + + return rc; +} + static void pn533_wq_mi_recv(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, mi_work); @@ -1853,7 +1916,7 @@ static void pn533_wq_mi_recv(struct work_struct *work) skb_reserve(skb_cmd, PN533_CMD_DATAEXCH_HEAD_LEN); - rc = pn533_data_exchange_tx_frame(dev, skb_cmd); + rc = pn533_build_tx_frame(dev, skb_cmd, true); if (rc) goto error_frame; @@ -1928,6 +1991,7 @@ struct nfc_ops pn533_nfc_ops = { .activate_target = pn533_activate_target, .deactivate_target = pn533_deactivate_target, .im_transceive = pn533_transceive, + .tm_send = pn533_tm_send, }; static int pn533_probe(struct usb_interface *interface, -- cgit v1.2.3 From 7c2a04a933d51b45af7660bd1790339eb3c33308 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Mon, 21 May 2012 16:20:01 +0200 Subject: NFC: Reset poll mod list when stopping pn533 poll Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index db4078765ac9..6b8a65ca0d71 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1371,6 +1371,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev) /* prevent pn533_start_poll_complete to issue a new poll meanwhile */ usb_kill_urb(dev->in_urb); + + pn533_poll_reset_mod_list(dev); } static int pn533_activate_target_nfcdep(struct pn533 *dev) -- cgit v1.2.3 From 9f2f8ba172d9a7f7f600ced18b05f6fd52b83396 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 29 May 2012 21:28:58 +0200 Subject: NFC: Unregister device if pn533 initial configuration fails Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 6b8a65ca0d71..2a7e4e8e4f11 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -2112,13 +2112,17 @@ static int pn533_probe(struct usb_interface *interface, if (rc) { nfc_dev_err(&dev->interface->dev, "Error on setting MAX_RETRIES" " config"); - goto free_nfc_dev; + goto unregister_nfc_dev; } return 0; +unregister_nfc_dev: + nfc_unregister_device(dev->nfc_dev); + free_nfc_dev: nfc_free_device(dev->nfc_dev); + destroy_wq: destroy_workqueue(dev->wq); error: -- cgit v1.2.3 From 34a85bfcfb84fe673833290ee071de0a10dd1cac Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 29 May 2012 21:34:08 +0200 Subject: NFC: Configure pn533 RF timings Those define the ATR_RES response timeout after which the pn533 considers the target to be mute. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 2a7e4e8e4f11..d9763440b8e9 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -104,8 +104,14 @@ struct pn533_fw_version { }; /* PN533_CMD_RF_CONFIGURATION */ +#define PN533_CFGITEM_TIMING 0x02 #define PN533_CFGITEM_MAX_RETRIES 0x05 +#define PN533_CONFIG_TIMING_102 0xb +#define PN533_CONFIG_TIMING_204 0xc +#define PN533_CONFIG_TIMING_409 0xd +#define PN533_CONFIG_TIMING_819 0xe + #define PN533_CONFIG_MAX_RETRIES_NO_RETRY 0x00 #define PN533_CONFIG_MAX_RETRIES_ENDLESS 0xFF @@ -115,6 +121,12 @@ struct pn533_config_max_retries { u8 mx_rty_passive_act; } __packed; +struct pn533_config_timing { + u8 rfu; + u8 atr_res_timeout; + u8 dep_timeout; +} __packed; + /* PN533_CMD_IN_LIST_PASSIVE_TARGET */ /* felica commands opcode */ @@ -2004,6 +2016,7 @@ static int pn533_probe(struct usb_interface *interface, struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; struct pn533_config_max_retries max_retries; + struct pn533_config_timing timing; int in_endpoint = 0; int out_endpoint = 0; int rc = -ENOMEM; @@ -2115,6 +2128,18 @@ static int pn533_probe(struct usb_interface *interface, goto unregister_nfc_dev; } + timing.rfu = PN533_CONFIG_TIMING_102; + timing.atr_res_timeout = PN533_CONFIG_TIMING_204; + timing.dep_timeout = PN533_CONFIG_TIMING_409; + + rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING, + (u8 *) &timing, sizeof(timing)); + if (rc) { + nfc_dev_err(&dev->interface->dev, + "Error on setting RF timings"); + goto unregister_nfc_dev; + } + return 0; unregister_nfc_dev: -- cgit v1.2.3 From d7f3345d3af568394080890ca3136c4ea1021cba Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 29 May 2012 21:45:21 +0200 Subject: NFC: Add passive initiator data for pn533 When trying to enable a DEP link as a passive initiator, pn533 needs to provide the polling request command payload. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index d9763440b8e9..806ab3da6414 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -256,7 +256,7 @@ struct pn533_cmd_jump_dep { u8 active; u8 baud; u8 next; - u8 gt[]; + u8 data[]; } __packed; struct pn533_cmd_jump_dep_response { @@ -1582,12 +1582,14 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, return 0; } +#define PASSIVE_DATA_LEN 5 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, u8 comm_mode, u8* gb, size_t gb_len) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); struct pn533_cmd_jump_dep *cmd; - u8 cmd_len; + u8 cmd_len, *data_ptr; + u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1605,6 +1607,9 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, } cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len; + if (comm_mode == NFC_COMM_PASSIVE) + cmd_len += PASSIVE_DATA_LEN; + cmd = kzalloc(cmd_len, GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -1612,10 +1617,18 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, pn533_tx_frame_init(dev->out_frame, PN533_CMD_IN_JUMP_FOR_DEP); cmd->active = !comm_mode; - cmd->baud = 0; + cmd->next = 0; + cmd->baud = 2; + data_ptr = cmd->data; + if (comm_mode == NFC_COMM_PASSIVE && cmd->baud > 0) { + memcpy(data_ptr, passive_data, PASSIVE_DATA_LEN); + cmd->next |= 1; + data_ptr += PASSIVE_DATA_LEN; + } + if (gb != NULL && gb_len > 0) { - cmd->next = 4; /* We have some Gi */ - memcpy(cmd->gt, gb, gb_len); + cmd->next |= 4; /* We have some Gi */ + memcpy(data_ptr, gb, gb_len); } else { cmd->next = 0; } -- cgit v1.2.3 From 51d9e803b906ea8ef995980d5367ab66ff79305a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 30 May 2012 01:48:46 +0200 Subject: NFC: Add type A and type F parameters for pn533 target mode Without those settings several devices will not activate pn533 as a target. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 806ab3da6414..6e4b228c7e6f 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1126,6 +1126,13 @@ static int pn533_init_target_frame(struct pn533_frame *frame, { struct pn533_cmd_init_target *cmd; size_t cmd_len; + u8 felica_params[18] = {0x1, 0xfe, /* DEP */ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, /* random */ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xff, 0xff}; /* System code */ + u8 mifare_params[6] = {0x1, 0x1, /* SENS_RES */ + 0x0, 0x0, 0x0, + 0x40}; /* SEL_RES for DEP */ cmd_len = sizeof(struct pn533_cmd_init_target) + gb_len + 1; cmd = kzalloc(cmd_len, GFP_KERNEL); @@ -1136,17 +1143,33 @@ static int pn533_init_target_frame(struct pn533_frame *frame, /* DEP support only */ cmd->mode |= PN533_INIT_TARGET_DEP; - get_random_bytes(cmd->nfcid3, 10); + + /* Felica params */ + memcpy(cmd->felica, felica_params, 18); + get_random_bytes(cmd->felica + 2, 6); + + /* NFCID3 */ + memset(cmd->nfcid3, 0, 10); + memcpy(cmd->nfcid3, cmd->felica, 8); + + /* MIFARE params */ + memcpy(cmd->mifare, mifare_params, 6); + + /* General bytes */ cmd->gb_len = gb_len; memcpy(cmd->gb, gb, gb_len); + /* Len Tk */ cmd->gb[gb_len] = 0; memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), cmd, cmd_len); + frame->datalen += cmd_len; pn533_tx_frame_finish(frame); + kfree(cmd); + return 0; } -- cgit v1.2.3 From 6fbbdc16be3881aabaa4096c3466b9bbd361bd1f Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 30 May 2012 17:20:25 +0200 Subject: NFC: Implement pn533 polling loop After going through all the modulations, the pn533 driver spends 2 seconds listening for targets. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 287 +++++++++++++++++++++++++++++----------------------- 1 file changed, 161 insertions(+), 126 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 6e4b228c7e6f..c9ba96c320fe 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -45,6 +45,9 @@ static const struct usb_device_id pn533_table[] = { }; MODULE_DEVICE_TABLE(usb, pn533_table); +/* How much time we spend listening for initiators */ +#define PN533_LISTEN_TIME 2 + /* frame definitions */ #define PN533_FRAME_TAIL_SIZE 2 #define PN533_FRAME_SIZE(f) (sizeof(struct pn533_frame) + f->datalen + \ @@ -163,6 +166,7 @@ enum { PN533_POLL_MOD_424KBPS_FELICA, PN533_POLL_MOD_106KBPS_JEWEL, PN533_POLL_MOD_847KBPS_B, + PN533_LISTEN_MOD, __PN533_POLL_MOD_AFTER_LAST, }; @@ -230,6 +234,9 @@ const struct pn533_poll_modulations poll_mod[] = { }, .len = 3, }, + [PN533_LISTEN_MOD] = { + .len = 0, + }, }; /* PN533_CMD_IN_ATR */ @@ -312,10 +319,13 @@ struct pn533 { struct workqueue_struct *wq; struct work_struct cmd_work; + struct work_struct poll_work; struct work_struct mi_work; struct work_struct tg_work; + struct timer_list listen_timer; struct pn533_frame *wq_in_frame; int wq_in_error; + int cancel_listen; pn533_cmd_complete_t cmd_complete; void *cmd_complete_arg; @@ -326,6 +336,10 @@ struct pn533 { u8 poll_mod_count; u8 poll_mod_curr; u32 poll_protocols; + u32 listen_protocols; + + u8 *gb; + size_t gb_len; u8 tgt_available_prots; u8 tgt_active_prot; @@ -1006,6 +1020,11 @@ static int pn533_target_found(struct pn533 *dev, return 0; } +static inline void pn533_poll_next_mod(struct pn533 *dev) +{ + dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count; +} + static void pn533_poll_reset_mod_list(struct pn533 *dev) { dev->poll_mod_count = 0; @@ -1018,107 +1037,52 @@ static void pn533_poll_add_mod(struct pn533 *dev, u8 mod_index) dev->poll_mod_count++; } -static void pn533_poll_create_mod_list(struct pn533 *dev, u32 protocols) +static void pn533_poll_create_mod_list(struct pn533 *dev, + u32 im_protocols, u32 tm_protocols) { pn533_poll_reset_mod_list(dev); - if (protocols & NFC_PROTO_MIFARE_MASK - || protocols & NFC_PROTO_ISO14443_MASK - || protocols & NFC_PROTO_NFC_DEP_MASK) + if (im_protocols & NFC_PROTO_MIFARE_MASK + || im_protocols & NFC_PROTO_ISO14443_MASK + || im_protocols & NFC_PROTO_NFC_DEP_MASK) pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_A); - if (protocols & NFC_PROTO_FELICA_MASK - || protocols & NFC_PROTO_NFC_DEP_MASK) { + if (im_protocols & NFC_PROTO_FELICA_MASK + || im_protocols & NFC_PROTO_NFC_DEP_MASK) { pn533_poll_add_mod(dev, PN533_POLL_MOD_212KBPS_FELICA); pn533_poll_add_mod(dev, PN533_POLL_MOD_424KBPS_FELICA); } - if (protocols & NFC_PROTO_JEWEL_MASK) + if (im_protocols & NFC_PROTO_JEWEL_MASK) pn533_poll_add_mod(dev, PN533_POLL_MOD_106KBPS_JEWEL); - if (protocols & NFC_PROTO_ISO14443_MASK) + if (im_protocols & NFC_PROTO_ISO14443_MASK) pn533_poll_add_mod(dev, PN533_POLL_MOD_847KBPS_B); -} - -static void pn533_start_poll_frame(struct pn533_frame *frame, - struct pn533_poll_modulations *mod) -{ - - pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET); - memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len); - frame->datalen += mod->len; - - pn533_tx_frame_finish(frame); + if (tm_protocols) + pn533_poll_add_mod(dev, PN533_LISTEN_MOD); } static int pn533_start_poll_complete(struct pn533 *dev, void *arg, - u8 *params, int params_len) + u8 *params, int params_len) { struct pn533_poll_response *resp; - struct pn533_poll_modulations *next_mod; int rc; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (params_len == -ENOENT) { - nfc_dev_dbg(&dev->interface->dev, "Polling operation has been" - " stopped"); - goto stop_poll; - } - - if (params_len < 0) { - nfc_dev_err(&dev->interface->dev, "Error %d when running poll", - params_len); - goto stop_poll; - } - resp = (struct pn533_poll_response *) params; if (resp->nbtg) { rc = pn533_target_found(dev, resp, params_len); /* We must stop the poll after a valid target found */ - if (rc == 0) - goto stop_poll; - - if (rc != -EAGAIN) - nfc_dev_err(&dev->interface->dev, "The target found is" - " not valid - continuing to poll"); - } - - dev->poll_mod_curr = (dev->poll_mod_curr + 1) % dev->poll_mod_count; - - next_mod = dev->poll_mod_active[dev->poll_mod_curr]; - - nfc_dev_dbg(&dev->interface->dev, "Polling next modulation (0x%x)", - dev->poll_mod_curr); - - pn533_start_poll_frame(dev->out_frame, next_mod); - - /* Don't need to down the semaphore again */ - rc = __pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen, pn533_start_poll_complete, - NULL, GFP_ATOMIC); - - if (rc == -EPERM) { - nfc_dev_dbg(&dev->interface->dev, "Cannot poll next modulation" - " because poll has been stopped"); - goto stop_poll; - } - - if (rc) { - nfc_dev_err(&dev->interface->dev, "Error %d when trying to poll" - " next modulation", rc); - goto stop_poll; + if (rc == 0) { + pn533_poll_reset_mod_list(dev); + return 0; + } } - /* Inform caller function to do not up the semaphore */ - return -EINPROGRESS; - -stop_poll: - pn533_poll_reset_mod_list(dev); - dev->poll_protocols = 0; - return 0; + return -EAGAIN; } static int pn533_init_target_frame(struct pn533_frame *frame, @@ -1286,83 +1250,136 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg, return 0; } -static int pn533_init_target(struct nfc_dev *nfc_dev, u32 protocols) +static void pn533_listen_mode_timer(unsigned long data) { - struct pn533 *dev = nfc_get_drvdata(nfc_dev); - u8 *gb; - size_t gb_len; + struct pn533 *dev = (struct pn533 *) data; + + nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout"); + + /* An ack will cancel the last issued command (poll) */ + pn533_send_ack(dev, GFP_ATOMIC); + + dev->cancel_listen = 1; + + up(&dev->cmd_lock); + + pn533_poll_next_mod(dev); + + queue_work(dev->wq, &dev->poll_work); +} + +static int pn533_poll_complete(struct pn533 *dev, void *arg, + u8 *params, int params_len) +{ + struct pn533_poll_modulations *cur_mod; int rc; - pn533_poll_reset_mod_list(dev); + nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - gb = nfc_get_local_general_bytes(nfc_dev, &gb_len); - if (gb == NULL) - return -ENOMEM; + if (params_len == -ENOENT) { + if (dev->poll_mod_count != 0) + return 0; - rc = pn533_init_target_frame(dev->out_frame, gb, gb_len); - if (rc < 0) - return rc; + nfc_dev_err(&dev->interface->dev, + "Polling operation has been stopped"); - rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen, - pn533_init_target_complete, - NULL, GFP_KERNEL); + goto stop_poll; + } - if (rc) + if (params_len < 0) { nfc_dev_err(&dev->interface->dev, - "Error %d when trying to initiate as a target", rc); + "Error %d when running poll", params_len); - dev->poll_mod_count++; + goto stop_poll; + } - return rc; + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + + if (cur_mod->len == 0) { + del_timer(&dev->listen_timer); + + return pn533_init_target_complete(dev, arg, params, params_len); + } else { + rc = pn533_start_poll_complete(dev, arg, params, params_len); + if (!rc) + return rc; + } + + pn533_poll_next_mod(dev); + + queue_work(dev->wq, &dev->poll_work); + + return 0; + +stop_poll: + pn533_poll_reset_mod_list(dev); + dev->poll_protocols = 0; + return 0; } -static int pn533_start_im_poll(struct nfc_dev *nfc_dev, u32 protocols) +static void pn533_build_poll_frame(struct pn533 *dev, + struct pn533_frame *frame, + struct pn533_poll_modulations *mod) { - struct pn533 *dev = nfc_get_drvdata(nfc_dev); - struct pn533_poll_modulations *start_mod; - int rc; + nfc_dev_dbg(&dev->interface->dev, "mod len %d\n", mod->len); - if (dev->poll_mod_count) { - nfc_dev_err(&dev->interface->dev, "Polling operation already" - " active"); - return -EBUSY; - } + if (mod->len == 0) { + /* Listen mode */ + pn533_init_target_frame(frame, dev->gb, dev->gb_len); + } else { + /* Polling mode */ + pn533_tx_frame_init(frame, PN533_CMD_IN_LIST_PASSIVE_TARGET); - pn533_poll_create_mod_list(dev, protocols); + memcpy(PN533_FRAME_CMD_PARAMS_PTR(frame), &mod->data, mod->len); + frame->datalen += mod->len; - if (!dev->poll_mod_count) { - nfc_dev_err(&dev->interface->dev, "No valid protocols" - " specified"); - rc = -EINVAL; - goto error; + pn533_tx_frame_finish(frame); } +} - nfc_dev_dbg(&dev->interface->dev, "It will poll %d modulations types", - dev->poll_mod_count); +static int pn533_send_poll_frame(struct pn533 *dev) +{ + struct pn533_poll_modulations *cur_mod; + int rc; - dev->poll_mod_curr = 0; - start_mod = dev->poll_mod_active[dev->poll_mod_curr]; + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; - pn533_start_poll_frame(dev->out_frame, start_mod); + pn533_build_poll_frame(dev, dev->out_frame, cur_mod); rc = pn533_send_cmd_frame_async(dev, dev->out_frame, dev->in_frame, - dev->in_maxlen, pn533_start_poll_complete, + dev->in_maxlen, pn533_poll_complete, NULL, GFP_KERNEL); + if (rc) + nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc); - if (rc) { - nfc_dev_err(&dev->interface->dev, "Error %d when trying to" - " start poll", rc); - goto error; + return rc; +} + +static void pn533_wq_poll(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, poll_work); + struct pn533_poll_modulations *cur_mod; + int rc; + + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + + nfc_dev_dbg(&dev->interface->dev, + "%s cancel_listen %d modulation len %d", + __func__, dev->cancel_listen, cur_mod->len); + + if (dev->cancel_listen == 1) { + dev->cancel_listen = 0; + usb_kill_urb(dev->in_urb); } - dev->poll_protocols = protocols; + rc = pn533_send_poll_frame(dev); + if (rc) + return; - return 0; + if (cur_mod->len == 0 && dev->poll_mod_count > 1) + mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ); -error: - pn533_poll_reset_mod_list(dev); - return rc; + return; } static int pn533_start_poll(struct nfc_dev *nfc_dev, @@ -1380,13 +1397,18 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev, return -EBUSY; } - if (im_protocols) - return pn533_start_im_poll(nfc_dev, im_protocols); + if (tm_protocols) { + dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len); + if (dev->gb == NULL) + tm_protocols = 0; + } - if (tm_protocols) - return pn533_init_target(nfc_dev, tm_protocols); + dev->poll_mod_curr = 0; + pn533_poll_create_mod_list(dev, im_protocols, tm_protocols); + dev->poll_protocols = im_protocols; + dev->listen_protocols = tm_protocols; - return -EINVAL; + return pn533_send_poll_frame(dev); } static void pn533_stop_poll(struct nfc_dev *nfc_dev) @@ -1395,6 +1417,8 @@ static void pn533_stop_poll(struct nfc_dev *nfc_dev) nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + del_timer(&dev->listen_timer); + if (!dev->poll_mod_count) { nfc_dev_dbg(&dev->interface->dev, "Polling operation was not" " running"); @@ -1676,6 +1700,10 @@ out: static int pn533_dep_link_down(struct nfc_dev *nfc_dev) { + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + + pn533_poll_reset_mod_list(dev); + pn533_deactivate_target(nfc_dev, 0); return 0; @@ -2110,12 +2138,17 @@ static int pn533_probe(struct usb_interface *interface, INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete); INIT_WORK(&dev->mi_work, pn533_wq_mi_recv); INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); + INIT_WORK(&dev->poll_work, pn533_wq_poll); dev->wq = alloc_workqueue("pn533", WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, 1); if (dev->wq == NULL) goto error; + init_timer(&dev->listen_timer); + dev->listen_timer.data = (unsigned long) dev; + dev->listen_timer.function = pn533_listen_mode_timer; + skb_queue_head_init(&dev->resp_q); usb_set_intfdata(interface, dev); @@ -2212,6 +2245,8 @@ static void pn533_disconnect(struct usb_interface *interface) skb_queue_purge(&dev->resp_q); + del_timer(&dev->listen_timer); + kfree(dev->in_frame); usb_free_urb(dev->in_urb); kfree(dev->out_frame); -- cgit v1.2.3 From 41a8ec496de8f4ef161fa0293de8cd9aa5773325 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 17:44:44 +0200 Subject: NFC: Set the proper baud rate when trying to activate pn533 targets We get the right baud rate from the last polled modulation. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index c9ba96c320fe..b97e3c81ec0b 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -1629,6 +1629,20 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, return 0; } +static int pn533_mod_to_baud(struct pn533 *dev) +{ + switch (dev->poll_mod_curr) { + case PN533_POLL_MOD_106KBPS_A: + return 0; + case PN533_POLL_MOD_212KBPS_FELICA: + return 1; + case PN533_POLL_MOD_424KBPS_FELICA: + return 2; + default: + return -EINVAL; + } +} + #define PASSIVE_DATA_LEN 5 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, u8 comm_mode, u8* gb, size_t gb_len) @@ -1637,7 +1651,7 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, struct pn533_cmd_jump_dep *cmd; u8 cmd_len, *data_ptr; u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; - int rc; + int rc, baud; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); @@ -1653,6 +1667,13 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, return -EBUSY; } + baud = pn533_mod_to_baud(dev); + if (baud < 0) { + nfc_dev_err(&dev->interface->dev, + "Invalid curr modulation %d", dev->poll_mod_curr); + return baud; + } + cmd_len = sizeof(struct pn533_cmd_jump_dep) + gb_len; if (comm_mode == NFC_COMM_PASSIVE) cmd_len += PASSIVE_DATA_LEN; @@ -1665,7 +1686,7 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, cmd->active = !comm_mode; cmd->next = 0; - cmd->baud = 2; + cmd->baud = baud; data_ptr = cmd->data; if (comm_mode == NFC_COMM_PASSIVE && cmd->baud > 0) { memcpy(data_ptr, passive_data, PASSIVE_DATA_LEN); -- cgit v1.2.3 From 0201ed0349a71cec12ced7e142e6df78504f38fb Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 17:56:46 +0200 Subject: NFC: Convert pn533 from semaphore to mutex Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index b97e3c81ec0b..58c1641cf658 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -329,7 +329,7 @@ struct pn533 { pn533_cmd_complete_t cmd_complete; void *cmd_complete_arg; - struct semaphore cmd_lock; + struct mutex cmd_lock; u8 cmd; struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1]; @@ -462,7 +462,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work) PN533_FRAME_CMD_PARAMS_LEN(in_frame)); if (rc != -EINPROGRESS) - up(&dev->cmd_lock); + mutex_unlock(&dev->cmd_lock); } static void pn533_recv_response(struct urb *urb) @@ -640,7 +640,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (down_trylock(&dev->cmd_lock)) + if (!mutex_trylock(&dev->cmd_lock)) return -EBUSY; rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, @@ -650,7 +650,7 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, return 0; error: - up(&dev->cmd_lock); + mutex_unlock(&dev->cmd_lock); return rc; } @@ -1261,7 +1261,7 @@ static void pn533_listen_mode_timer(unsigned long data) dev->cancel_listen = 1; - up(&dev->cmd_lock); + mutex_unlock(&dev->cmd_lock); pn533_poll_next_mod(dev); @@ -2054,7 +2054,7 @@ error_cmd: kfree(arg); - up(&dev->cmd_lock); + mutex_unlock(&dev->cmd_lock); } static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, @@ -2114,7 +2114,7 @@ static int pn533_probe(struct usb_interface *interface, dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; - sema_init(&dev->cmd_lock, 1); + mutex_init(&dev->cmd_lock); iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { -- cgit v1.2.3 From 51ad304cc8db5589d1e7b0a9df3858381753d788 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 31 May 2012 20:01:32 +0200 Subject: NFC: Monitor pn533 target mode When receiving a DEP link down event, we should cancel all pending URBs if we're activated as a target or if we're an initiator. Signed-off-by: Samuel Ortiz --- drivers/nfc/pn533.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 58c1641cf658..9ac829e22e73 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -343,6 +343,7 @@ struct pn533 { u8 tgt_available_prots; u8 tgt_active_prot; + u8 tgt_mode; }; struct pn533_frame { @@ -1158,6 +1159,8 @@ static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, if (params_len > 0 && params[0] != 0) { nfc_tm_deactivated(dev->nfc_dev); + dev->tgt_mode = 0; + kfree_skb(skb_resp); return 0; } @@ -1245,6 +1248,8 @@ static int pn533_init_target_complete(struct pn533 *dev, void *arg, return rc; } + dev->tgt_mode = 1; + queue_work(dev->wq, &dev->tg_work); return 0; @@ -1397,6 +1402,12 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev, return -EBUSY; } + if (dev->tgt_mode) { + nfc_dev_err(&dev->interface->dev, + "Cannot poll while already being activated"); + return -EBUSY; + } + if (tm_protocols) { dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len); if (dev->gb == NULL) @@ -1725,7 +1736,15 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev) pn533_poll_reset_mod_list(dev); - pn533_deactivate_target(nfc_dev, 0); + if (dev->tgt_mode || dev->tgt_active_prot) { + pn533_send_ack(dev, GFP_KERNEL); + usb_kill_urb(dev->in_urb); + } + + dev->tgt_active_prot = 0; + dev->tgt_mode = 0; + + skb_queue_purge(&dev->resp_q); return 0; } @@ -1956,6 +1975,8 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg, if (params_len > 0 && params[0] != 0) { nfc_tm_deactivated(dev->nfc_dev); + dev->tgt_mode = 0; + return 0; } -- cgit v1.2.3 From a6c511c636848f871f5b7aef38e25e5b894b3b48 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Wed, 23 May 2012 12:35:46 +0200 Subject: Bluetooth: Rename HCI_QUIRK_NO_RESET to HCI_QUIRK_RESET_ON_CLOSE HCI_QUIRK_NO_RESET name is misleading - purpose of this quirk is to reset device on close instead of init, not to not reset at all. Rename it to HCI_QUIRK_RESET_ON_CLOSE to avoid confusion. Signed-off-by: Szymon Janc Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bpa10x.c | 2 +- drivers/bluetooth/btusb.c | 6 +++--- drivers/bluetooth/hci_ldisc.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 609861a53c28..29caaed2d715 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -470,7 +470,7 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * hdev->flush = bpa10x_flush; hdev->send = bpa10x_send_frame; - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); err = hci_register_dev(hdev); if (err < 0) { diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index c9463af8e564..3a6cdc9b75a3 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1026,7 +1026,7 @@ static int btusb_probe(struct usb_interface *intf, data->isoc = usb_ifnum_to_if(data->udev, 1); if (!reset) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (force_scofix || id->driver_info & BTUSB_WRONG_SCO_MTU) { if (!disable_scofix) @@ -1038,7 +1038,7 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); } if (id->driver_info & BTUSB_CSR) { @@ -1046,7 +1046,7 @@ static int btusb_probe(struct usb_interface *intf, /* Old firmware would otherwise execute USB reset */ if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); } if (id->driver_info & BTUSB_SNIFFER) { diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index e564579a6115..2f9b796e106e 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -394,7 +394,7 @@ static int hci_uart_register_dev(struct hci_uart *hu) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) - set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags)) hdev->dev_type = HCI_AMP; -- cgit v1.2.3 From fc5fef615a963c8b13abf0bbc2a8e8d7c3fd1ffb Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 23 May 2012 04:04:19 -0300 Subject: Bluetooth: Remove 'register' usage from the subsystem Let the compiler chooses what is best. Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/bluecard_cs.c | 10 +++++----- drivers/bluetooth/bt3c_cs.c | 4 ++-- drivers/bluetooth/btuart_cs.c | 4 ++-- drivers/bluetooth/dtl1_cs.c | 4 ++-- drivers/bluetooth/hci_bcsp.c | 2 +- drivers/bluetooth/hci_h4.c | 2 +- drivers/bluetooth/hci_ll.c | 6 +++--- 7 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 1fcd92380356..585c88e01893 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -231,12 +231,12 @@ static void bluecard_write_wakeup(bluecard_info_t *info) } do { - register unsigned int iobase = info->p_dev->resource[0]->start; - register unsigned int offset; - register unsigned char command; - register unsigned long ready_bit; + unsigned int iobase = info->p_dev->resource[0]->start; + unsigned int offset; + unsigned char command; + unsigned long ready_bit; register struct sk_buff *skb; - register int len; + int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 308c8599ab55..b2b0fbbb43b5 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -186,9 +186,9 @@ static void bt3c_write_wakeup(bt3c_info_t *info) return; do { - register unsigned int iobase = info->p_dev->resource[0]->start; + unsigned int iobase = info->p_dev->resource[0]->start; register struct sk_buff *skb; - register int len; + int len; if (!pcmcia_dev_present(info->p_dev)) break; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index c4fc2f3fc32c..65b8d996840c 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -140,9 +140,9 @@ static void btuart_write_wakeup(btuart_info_t *info) } do { - register unsigned int iobase = info->p_dev->resource[0]->start; + unsigned int iobase = info->p_dev->resource[0]->start; register struct sk_buff *skb; - register int len; + int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 6e8d96189684..b1b37ccd3cd4 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -144,9 +144,9 @@ static void dtl1_write_wakeup(dtl1_info_t *info) } do { - register unsigned int iobase = info->p_dev->resource[0]->start; + unsigned int iobase = info->p_dev->resource[0]->start; register struct sk_buff *skb; - register int len; + int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index 661a8dc4d2f8..57e502e06080 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -552,7 +552,7 @@ static u16 bscp_get_crc(struct bcsp_struct *bcsp) static int bcsp_recv(struct hci_uart *hu, void *data, int count) { struct bcsp_struct *bcsp = hu->priv; - register unsigned char *ptr; + unsigned char *ptr; BT_DBG("hu %p count %d rx_state %d rx_count %ld", hu, count, bcsp->rx_state, bcsp->rx_count); diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 748329468d26..c60623f206d4 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -126,7 +126,7 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) static inline int h4_check_data_len(struct h4_struct *h4, int len) { - register int room = skb_tailroom(h4->rx_skb); + int room = skb_tailroom(h4->rx_skb); BT_DBG("len %d room %d", len, room); diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index b874c0efde24..ff6d589c34a5 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -348,7 +348,7 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) static inline int ll_check_data_len(struct ll_struct *ll, int len) { - register int room = skb_tailroom(ll->rx_skb); + int room = skb_tailroom(ll->rx_skb); BT_DBG("len %d room %d", len, room); @@ -374,11 +374,11 @@ static inline int ll_check_data_len(struct ll_struct *ll, int len) static int ll_recv(struct hci_uart *hu, void *data, int count) { struct ll_struct *ll = hu->priv; - register char *ptr; + char *ptr; struct hci_event_hdr *eh; struct hci_acl_hdr *ah; struct hci_sco_hdr *sh; - register int len, type, dlen; + int len, type, dlen; BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count); -- cgit v1.2.3 From 8c520a59927a5600973782505dbb750d985057c4 Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Wed, 23 May 2012 04:04:22 -0300 Subject: Bluetooth: Remove unnecessary headers include Most of the include were unnecessary or already included by some other header. Replace module.h by export.h where possible. Signed-off-by: Gustavo Padovan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3a6cdc9b75a3..a45e717f5f84 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -21,15 +21,7 @@ * */ -#include #include -#include -#include -#include -#include -#include -#include - #include #include -- cgit v1.2.3 From 37ed561e823f5f91ca87522d61a57974be583c0d Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Wed, 23 May 2012 18:50:04 -0700 Subject: Bluetooth: btmrvl: add SD8787 Bluetooth AMP device ID SD8787 SDIO function 3 (device ID 0x911B) is for Bluetooth AMP. Signed-off-by: Bing Zhao Signed-off-by: Gustavo Padovan --- drivers/bluetooth/btmrvl_sdio.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index a853244e7fd7..2867499f7256 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -110,6 +110,9 @@ static const struct sdio_device_id btmrvl_sdio_ids[] = { /* Marvell SD8787 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A), .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, + /* Marvell SD8787 Bluetooth AMP device */ + { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B), + .driver_data = (unsigned long) &btmrvl_sdio_sd8787 }, /* Marvell SD8797 Bluetooth device */ { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A), .driver_data = (unsigned long) &btmrvl_sdio_sd8797 }, -- cgit v1.2.3 From 9f132652d94c96476b0b0a8caf0c10e96ab10fa8 Mon Sep 17 00:00:00 2001 From: Pavel Vasilyev Date: Tue, 5 Jun 2012 00:02:05 -0400 Subject: ACPI sysfs.c strlen fix Current code is ignoring the last character of "enable" and "disable" in comparisons. https://bugzilla.kernel.org/show_bug.cgi?id=33732 Signed-off-by: Len Brown --- drivers/acpi/sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 9f66181c814e..240a24400976 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -173,7 +173,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp) { int result = 0; - if (!strncmp(val, "enable", strlen("enable") - 1)) { + if (!strncmp(val, "enable", strlen("enable"))) { result = acpi_debug_trace(trace_method_name, trace_debug_level, trace_debug_layer, 0); if (result) @@ -181,7 +181,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp) goto exit; } - if (!strncmp(val, "disable", strlen("disable") - 1)) { + if (!strncmp(val, "disable", strlen("disable"))) { int name = 0; result = acpi_debug_trace((char *)&name, trace_debug_level, trace_debug_layer, 0); -- cgit v1.2.3 From 7412ff139d73f5561492478e89a22aede7252b7b Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Mon, 4 Jun 2012 12:43:03 -0700 Subject: mtip32xx: Remove 'registers' and 'flags' from sysfs This patch removes entries 'registers' and 'flags' from sysfs. Updated ABI file to reflect this change. Reported-by: Greg Kroah-Hartman Signed-off-by: Asai Thambi S P Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 92 +-------------------------------------- 1 file changed, 1 insertion(+), 91 deletions(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 264bc77dcb91..b6e95b911b8c 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -2546,7 +2546,7 @@ static struct scatterlist *mtip_hw_get_scatterlist(struct driver_data *dd, } /* - * Sysfs register/status dump. + * Sysfs status dump. * * @dev Pointer to the device structure, passed by the kernrel. * @attr Pointer to the device_attribute structure passed by the kernel. @@ -2555,71 +2555,6 @@ static struct scatterlist *mtip_hw_get_scatterlist(struct driver_data *dd, * return value * The size, in bytes, of the data copied into buf. */ -static ssize_t mtip_hw_show_registers(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u32 group_allocated; - struct driver_data *dd = dev_to_disk(dev)->private_data; - int size = 0; - int n; - - size += sprintf(&buf[size], "Hardware\n--------\n"); - size += sprintf(&buf[size], "S ACTive : [ 0x"); - - for (n = dd->slot_groups-1; n >= 0; n--) - size += sprintf(&buf[size], "%08X ", - readl(dd->port->s_active[n])); - - size += sprintf(&buf[size], "]\n"); - size += sprintf(&buf[size], "Command Issue : [ 0x"); - - for (n = dd->slot_groups-1; n >= 0; n--) - size += sprintf(&buf[size], "%08X ", - readl(dd->port->cmd_issue[n])); - - size += sprintf(&buf[size], "]\n"); - size += sprintf(&buf[size], "Completed : [ 0x"); - - for (n = dd->slot_groups-1; n >= 0; n--) - size += sprintf(&buf[size], "%08X ", - readl(dd->port->completed[n])); - - size += sprintf(&buf[size], "]\n"); - size += sprintf(&buf[size], "PORT IRQ STAT : [ 0x%08X ]\n", - readl(dd->port->mmio + PORT_IRQ_STAT)); - size += sprintf(&buf[size], "HOST IRQ STAT : [ 0x%08X ]\n", - readl(dd->mmio + HOST_IRQ_STAT)); - size += sprintf(&buf[size], "\n"); - - size += sprintf(&buf[size], "Local\n-----\n"); - size += sprintf(&buf[size], "Allocated : [ 0x"); - - for (n = dd->slot_groups-1; n >= 0; n--) { - if (sizeof(long) > sizeof(u32)) - group_allocated = - dd->port->allocated[n/2] >> (32*(n&1)); - else - group_allocated = dd->port->allocated[n]; - size += sprintf(&buf[size], "%08X ", group_allocated); - } - size += sprintf(&buf[size], "]\n"); - - size += sprintf(&buf[size], "Commands in Q: [ 0x"); - - for (n = dd->slot_groups-1; n >= 0; n--) { - if (sizeof(long) > sizeof(u32)) - group_allocated = - dd->port->cmds_to_issue[n/2] >> (32*(n&1)); - else - group_allocated = dd->port->cmds_to_issue[n]; - size += sprintf(&buf[size], "%08X ", group_allocated); - } - size += sprintf(&buf[size], "]\n"); - - return size; -} - static ssize_t mtip_hw_show_status(struct device *dev, struct device_attribute *attr, char *buf) @@ -2637,24 +2572,7 @@ static ssize_t mtip_hw_show_status(struct device *dev, return size; } -static ssize_t mtip_hw_show_flags(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct driver_data *dd = dev_to_disk(dev)->private_data; - int size = 0; - - size += sprintf(&buf[size], "Flag in port struct : [ %08lX ]\n", - dd->port->flags); - size += sprintf(&buf[size], "Flag in dd struct : [ %08lX ]\n", - dd->dd_flag); - - return size; -} - -static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL); static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL); -static DEVICE_ATTR(flags, S_IRUGO, mtip_hw_show_flags, NULL); /* * Create the sysfs related attributes. @@ -2671,15 +2589,9 @@ static int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj) if (!kobj || !dd) return -EINVAL; - if (sysfs_create_file(kobj, &dev_attr_registers.attr)) - dev_warn(&dd->pdev->dev, - "Error creating 'registers' sysfs entry\n"); if (sysfs_create_file(kobj, &dev_attr_status.attr)) dev_warn(&dd->pdev->dev, "Error creating 'status' sysfs entry\n"); - if (sysfs_create_file(kobj, &dev_attr_flags.attr)) - dev_warn(&dd->pdev->dev, - "Error creating 'flags' sysfs entry\n"); return 0; } @@ -2698,9 +2610,7 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj) if (!kobj || !dd) return -EINVAL; - sysfs_remove_file(kobj, &dev_attr_registers.attr); sysfs_remove_file(kobj, &dev_attr_status.attr); - sysfs_remove_file(kobj, &dev_attr_flags.attr); return 0; } -- cgit v1.2.3 From 7b421d24eac79800ee68905f732300a291f72f00 Mon Sep 17 00:00:00 2001 From: Asai Thambi S P Date: Mon, 4 Jun 2012 12:44:02 -0700 Subject: mtip32xx: Create debugfs entries for troubleshooting On module load, creates a debugfs parent 'rssd' in debugfs root. Then for each device, create a new node with corresponding disk name. Under the new node, two entries 'registers' and 'flags' are created. NOTE: These entries were removed from sysfs in the previous patch Signed-off-by: Asai Thambi S P Signed-off-by: Jens Axboe --- drivers/block/mtip32xx/mtip32xx.c | 162 +++++++++++++++++++++++++++++++++++++- drivers/block/mtip32xx/mtip32xx.h | 4 + 2 files changed, 165 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index b6e95b911b8c..a8fddeb3d638 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -37,6 +37,7 @@ #include #include <../drivers/ata/ahci.h> #include +#include #include "mtip32xx.h" #define HW_CMD_SLOT_SZ (MTIP_MAX_COMMAND_SLOTS * 32) @@ -85,6 +86,7 @@ static int instance; * allocated in mtip_init(). */ static int mtip_major; +static struct dentry *dfs_parent; static DEFINE_SPINLOCK(rssd_index_lock); static DEFINE_IDA(rssd_index_ida); @@ -2574,6 +2576,120 @@ static ssize_t mtip_hw_show_status(struct device *dev, static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL); +static ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf, + size_t len, loff_t *offset) +{ + struct driver_data *dd = (struct driver_data *)f->private_data; + char buf[MTIP_DFS_MAX_BUF_SIZE]; + u32 group_allocated; + int size = *offset; + int n; + + if (!len || size) + return 0; + + if (size < 0) + return -EINVAL; + + size += sprintf(&buf[size], "H/ S ACTive : [ 0x"); + + for (n = dd->slot_groups-1; n >= 0; n--) + size += sprintf(&buf[size], "%08X ", + readl(dd->port->s_active[n])); + + size += sprintf(&buf[size], "]\n"); + size += sprintf(&buf[size], "H/ Command Issue : [ 0x"); + + for (n = dd->slot_groups-1; n >= 0; n--) + size += sprintf(&buf[size], "%08X ", + readl(dd->port->cmd_issue[n])); + + size += sprintf(&buf[size], "]\n"); + size += sprintf(&buf[size], "H/ Completed : [ 0x"); + + for (n = dd->slot_groups-1; n >= 0; n--) + size += sprintf(&buf[size], "%08X ", + readl(dd->port->completed[n])); + + size += sprintf(&buf[size], "]\n"); + size += sprintf(&buf[size], "H/ PORT IRQ STAT : [ 0x%08X ]\n", + readl(dd->port->mmio + PORT_IRQ_STAT)); + size += sprintf(&buf[size], "H/ HOST IRQ STAT : [ 0x%08X ]\n", + readl(dd->mmio + HOST_IRQ_STAT)); + size += sprintf(&buf[size], "\n"); + + size += sprintf(&buf[size], "L/ Allocated : [ 0x"); + + for (n = dd->slot_groups-1; n >= 0; n--) { + if (sizeof(long) > sizeof(u32)) + group_allocated = + dd->port->allocated[n/2] >> (32*(n&1)); + else + group_allocated = dd->port->allocated[n]; + size += sprintf(&buf[size], "%08X ", group_allocated); + } + size += sprintf(&buf[size], "]\n"); + + size += sprintf(&buf[size], "L/ Commands in Q : [ 0x"); + + for (n = dd->slot_groups-1; n >= 0; n--) { + if (sizeof(long) > sizeof(u32)) + group_allocated = + dd->port->cmds_to_issue[n/2] >> (32*(n&1)); + else + group_allocated = dd->port->cmds_to_issue[n]; + size += sprintf(&buf[size], "%08X ", group_allocated); + } + size += sprintf(&buf[size], "]\n"); + + *offset = size <= len ? size : len; + size = copy_to_user(ubuf, buf, *offset); + if (size) + return -EFAULT; + + return *offset; +} + +static ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf, + size_t len, loff_t *offset) +{ + struct driver_data *dd = (struct driver_data *)f->private_data; + char buf[MTIP_DFS_MAX_BUF_SIZE]; + int size = *offset; + + if (!len || size) + return 0; + + if (size < 0) + return -EINVAL; + + size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n", + dd->port->flags); + size += sprintf(&buf[size], "Flag-dd : [ %08lX ]\n", + dd->dd_flag); + + *offset = size <= len ? size : len; + size = copy_to_user(ubuf, buf, *offset); + if (size) + return -EFAULT; + + return *offset; +} + +static const struct file_operations mtip_regs_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = mtip_hw_read_registers, + .llseek = no_llseek, +}; + +static const struct file_operations mtip_flags_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = mtip_hw_read_flags, + .llseek = no_llseek, +}; + /* * Create the sysfs related attributes. * @@ -2615,6 +2731,34 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj) return 0; } +static int mtip_hw_debugfs_init(struct driver_data *dd) +{ + if (!dfs_parent) + return -1; + + dd->dfs_node = debugfs_create_dir(dd->disk->disk_name, dfs_parent); + if (IS_ERR_OR_NULL(dd->dfs_node)) { + dev_warn(&dd->pdev->dev, + "Error creating node %s under debugfs\n", + dd->disk->disk_name); + dd->dfs_node = NULL; + return -1; + } + + debugfs_create_file("flags", S_IRUGO, dd->dfs_node, dd, + &mtip_flags_fops); + debugfs_create_file("registers", S_IRUGO, dd->dfs_node, dd, + &mtip_regs_fops); + + return 0; +} + +static void mtip_hw_debugfs_exit(struct driver_data *dd) +{ + debugfs_remove_recursive(dd->dfs_node); +} + + /* * Perform any init/resume time hardware setup * @@ -3640,6 +3784,7 @@ skip_create_disk: mtip_hw_sysfs_init(dd, kobj); kobject_put(kobj); } + mtip_hw_debugfs_init(dd); if (dd->mtip_svc_handler) { set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); @@ -3665,6 +3810,8 @@ start_service_thread: return rv; kthread_run_error: + mtip_hw_debugfs_exit(dd); + /* Delete our gendisk. This also removes the device from /dev */ del_gendisk(dd->disk); @@ -3715,6 +3862,7 @@ static int mtip_block_remove(struct driver_data *dd) kobject_put(kobj); } } + mtip_hw_debugfs_exit(dd); /* * Delete our gendisk structure. This also removes the device @@ -4062,10 +4210,20 @@ static int __init mtip_init(void) } mtip_major = error; + if (!dfs_parent) { + dfs_parent = debugfs_create_dir("rssd", NULL); + if (IS_ERR_OR_NULL(dfs_parent)) { + printk(KERN_WARNING "Error creating debugfs parent\n"); + dfs_parent = NULL; + } + } + /* Register our PCI operations. */ error = pci_register_driver(&mtip_pci_driver); - if (error) + if (error) { + debugfs_remove(dfs_parent); unregister_blkdev(mtip_major, MTIP_DRV_NAME); + } return error; } @@ -4082,6 +4240,8 @@ static int __init mtip_init(void) */ static void __exit mtip_exit(void) { + debugfs_remove_recursive(dfs_parent); + /* Release the allocated major block device number. */ unregister_blkdev(mtip_major, MTIP_DRV_NAME); diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h index adb1aae3b752..f51fc23d17bb 100644 --- a/drivers/block/mtip32xx/mtip32xx.h +++ b/drivers/block/mtip32xx/mtip32xx.h @@ -110,6 +110,8 @@ #define dbg_printk(format, arg...) #endif +#define MTIP_DFS_MAX_BUF_SIZE 1024 + #define __force_bit2int (unsigned int __force) enum { @@ -446,6 +448,8 @@ struct driver_data { unsigned long dd_flag; /* NOTE: use atomic bit operations on this */ struct task_struct *mtip_svc_handler; /* task_struct of svc thd */ + + struct dentry *dfs_node; }; #endif -- cgit v1.2.3 From cc502bb741e1797290381da14dddb695ec1adbaf Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sun, 3 Jun 2012 18:39:13 +0200 Subject: renesas_usbhs: cleanup quoted includes A few quoted includes start with a superfluous "./". Clean up those quoted includes. Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/usb/renesas_usbhs/common.c | 2 +- drivers/usb/renesas_usbhs/common.h | 4 ++-- drivers/usb/renesas_usbhs/fifo.c | 4 ++-- drivers/usb/renesas_usbhs/mod.c | 4 ++-- drivers/usb/renesas_usbhs/mod.h | 2 +- drivers/usb/renesas_usbhs/pipe.c | 4 ++-- drivers/usb/renesas_usbhs/pipe.h | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index a165490bae48..8c9bb1ad3069 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -19,7 +19,7 @@ #include #include #include -#include "./common.h" +#include "common.h" /* * image of renesas_usbhs diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 3f3ccd358753..dddf40a59ded 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -22,8 +22,8 @@ struct usbhs_priv; -#include "./mod.h" -#include "./pipe.h" +#include "mod.h" +#include "pipe.h" /* * diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 6ec7f838d7fa..08313574aac8 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -17,8 +17,8 @@ #include #include #include -#include "./common.h" -#include "./pipe.h" +#include "common.h" +#include "pipe.h" #define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo)) #define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo)) diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 0871e816df45..82a628f96c03 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -16,8 +16,8 @@ */ #include -#include "./common.h" -#include "./mod.h" +#include "common.h" +#include "mod.h" #define usbhs_priv_to_modinfo(priv) (&priv->mod_info) #define usbhs_mod_info_call(priv, func, param...) \ diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 6c6875533f01..1ef5bf604070 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -19,7 +19,7 @@ #include #include -#include "./common.h" +#include "common.h" /* * struct diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index feb06d6d2814..122526cfd32b 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -16,8 +16,8 @@ */ #include #include -#include "./common.h" -#include "./pipe.h" +#include "common.h" +#include "pipe.h" /* * macros diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index fa18b7dc2b2a..08786c06dcf1 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -17,8 +17,8 @@ #ifndef RENESAS_USB_PIPE_H #define RENESAS_USB_PIPE_H -#include "./common.h" -#include "./fifo.h" +#include "common.h" +#include "fifo.h" /* * struct -- cgit v1.2.3 From c224071e28e2bbd213cce2e6e9dde26e78542ca8 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sun, 3 Jun 2012 18:39:42 +0200 Subject: parisc: cleanup quoted include A quoted include starts with a superfluous "./". Clean up that quoted include. Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/parisc/iosapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 1f9e9fefb8e7..69ff60832b09 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -146,7 +146,7 @@ #endif #include -#include "./iosapic_private.h" +#include "iosapic_private.h" #define MODULE_NAME "iosapic" -- cgit v1.2.3 From e39fe251e03b6df83e740e2f598c04f382b4d3c7 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Mon, 4 Jun 2012 20:45:40 +0200 Subject: HID: wiimote-ext: comment spelling fix Signed-off-by: Giuseppe Bilotta Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c index aa958706c0e5..0a1805c9b0e5 100644 --- a/drivers/hid/hid-wiimote-ext.c +++ b/drivers/hid/hid-wiimote-ext.c @@ -77,7 +77,7 @@ static __u16 wiiext_keymap[] = { BTN_TR, /* WIIEXT_KEY_RT */ }; -/* diable all extensions */ +/* disable all extensions */ static void ext_disable(struct wiimote_ext *ext) { unsigned long flags; -- cgit v1.2.3 From f03c4a57cf0cbb4f961bca287f8bdfd3d6eb7e1a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 5 Jun 2012 08:39:52 +0800 Subject: regulator: tps6507x: Remove unused min_uV and max_uV from struct tps_info The min_uV and max_uV are not used in this driver. For table based voltage mappings, we can get min_uV and max_uV by info->table[0] and info->table[table_len -1]. Thus remove min_uV and max_uV from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6507x-regulator.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index c771e1077cc1..eed6678c2906 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -91,8 +91,6 @@ static const unsigned int LDO2_VSEL_table[] = { struct tps_info { const char *name; - unsigned min_uV; - unsigned max_uV; u8 table_len; const unsigned int *table; @@ -103,36 +101,26 @@ struct tps_info { static struct tps_info tps6507x_pmic_regs[] = { { .name = "VDCDC1", - .min_uV = 725000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), .table = VDCDCx_VSEL_table, }, { .name = "VDCDC2", - .min_uV = 725000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), .table = VDCDCx_VSEL_table, }, { .name = "VDCDC3", - .min_uV = 725000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(VDCDCx_VSEL_table), .table = VDCDCx_VSEL_table, }, { .name = "LDO1", - .min_uV = 1000000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(LDO1_VSEL_table), .table = LDO1_VSEL_table, }, { .name = "LDO2", - .min_uV = 725000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(LDO2_VSEL_table), .table = LDO2_VSEL_table, }, -- cgit v1.2.3 From 93b07e7bcd50bb73d3d60043438fa68bd5a0988b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 5 Jun 2012 08:58:13 +0800 Subject: regulator: tps6507x: Avoid duplicating the same mapping table for LDO2 and VDCDCx The voltage mapping table for LDO2 is exactly the same as VDCDCx. Thus we can use one mapping table for both LDO2 and VDCDCx instead of duplicating the mapping table. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6507x-regulator.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index eed6678c2906..07d01ccdf308 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -70,24 +70,8 @@ static const unsigned int LDO1_VSEL_table[] = { 2800000, 3000000, 3100000, 3300000, }; -static const unsigned int LDO2_VSEL_table[] = { - 725000, 750000, 775000, 800000, - 825000, 850000, 875000, 900000, - 925000, 950000, 975000, 1000000, - 1025000, 1050000, 1075000, 1100000, - 1125000, 1150000, 1175000, 1200000, - 1225000, 1250000, 1275000, 1300000, - 1325000, 1350000, 1375000, 1400000, - 1425000, 1450000, 1475000, 1500000, - 1550000, 1600000, 1650000, 1700000, - 1750000, 1800000, 1850000, 1900000, - 1950000, 2000000, 2050000, 2100000, - 2150000, 2200000, 2250000, 2300000, - 2350000, 2400000, 2450000, 2500000, - 2550000, 2600000, 2650000, 2700000, - 2750000, 2800000, 2850000, 2900000, - 3000000, 3100000, 3200000, 3300000, -}; +/* The voltage mapping table for LDO2 is the same as VDCDCx */ +#define LDO2_VSEL_table VDCDCx_VSEL_table struct tps_info { const char *name; -- cgit v1.2.3 From ed35ba9af4f40056a222dd7b67f39f1f6172185a Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 4 Jun 2012 19:29:41 +0200 Subject: s390/cio/chp: cleanup attribute usage Let the driver core handle device attribute creation and removal. This will simplify the code and eliminates races between attribute availability and userspace notification via uevents. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/chp.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index e792436c9270..9b054806f7b1 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -362,10 +362,13 @@ static struct attribute *chp_attrs[] = { &dev_attr_shared.attr, NULL, }; - static struct attribute_group chp_attr_group = { .attrs = chp_attrs, }; +static const struct attribute_group *chp_attr_groups[] = { + &chp_attr_group, + NULL, +}; static void chp_release(struct device *dev) { @@ -397,6 +400,7 @@ int chp_new(struct chp_id chpid) chp->chpid = chpid; chp->state = 1; chp->dev.parent = &channel_subsystems[chpid.cssid]->device; + chp->dev.groups = chp_attr_groups; chp->dev.release = chp_release; mutex_init(&chp->lock); @@ -426,16 +430,10 @@ int chp_new(struct chp_id chpid) put_device(&chp->dev); goto out; } - ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); - if (ret) { - device_unregister(&chp->dev); - goto out; - } mutex_lock(&channel_subsystems[chpid.cssid]->mutex); if (channel_subsystems[chpid.cssid]->cm_enabled) { ret = chp_add_cmg_attr(chp); if (ret) { - sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); device_unregister(&chp->dev); mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); goto out; -- cgit v1.2.3 From 76e0377b2450af577077c7e4e00ae6da738d1884 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 4 Jun 2012 19:32:13 +0200 Subject: s390/vmlogrdr: cleanup device attribute usage Let the driver core handle device attribute creation and removal. This will simplify the code and eliminates races between attribute availability and userspace notification via uevents. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmlogrdr.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 524d988d89dd..e9d70aedbaa2 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -668,6 +668,13 @@ static struct attribute *vmlogrdr_attrs[] = { &dev_attr_recording.attr, NULL, }; +static struct attribute_group vmlogrdr_attr_group = { + .attrs = vmlogrdr_attrs, +}; +static const struct attribute_group *vmlogrdr_attr_groups[] = { + &vmlogrdr_attr_group, + NULL, +}; static int vmlogrdr_pm_prepare(struct device *dev) { @@ -692,10 +699,6 @@ static const struct dev_pm_ops vmlogrdr_pm_ops = { .prepare = vmlogrdr_pm_prepare, }; -static struct attribute_group vmlogrdr_attr_group = { - .attrs = vmlogrdr_attrs, -}; - static struct class *vmlogrdr_class; static struct device_driver vmlogrdr_driver = { .name = "vmlogrdr", @@ -762,6 +765,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) dev->bus = &iucv_bus; dev->parent = iucv_root; dev->driver = &vmlogrdr_driver; + dev->groups = vmlogrdr_attr_groups; dev_set_drvdata(dev, priv); /* * The release function could be called after the @@ -779,11 +783,6 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) return ret; } - ret = sysfs_create_group(&dev->kobj, &vmlogrdr_attr_group); - if (ret) { - device_unregister(dev); - return ret; - } priv->class_device = device_create(vmlogrdr_class, dev, MKDEV(vmlogrdr_major, priv->minor_num), @@ -791,7 +790,6 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) if (IS_ERR(priv->class_device)) { ret = PTR_ERR(priv->class_device); priv->class_device=NULL; - sysfs_remove_group(&dev->kobj, &vmlogrdr_attr_group); device_unregister(dev); return ret; } @@ -804,7 +802,6 @@ static int vmlogrdr_unregister_device(struct vmlogrdr_priv_t *priv) { device_destroy(vmlogrdr_class, MKDEV(vmlogrdr_major, priv->minor_num)); if (priv->device != NULL) { - sysfs_remove_group(&priv->device->kobj, &vmlogrdr_attr_group); device_unregister(priv->device); priv->device=NULL; } -- cgit v1.2.3 From 72f6e3a8bc956fddf6e004ae9b804977d1458e77 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Mon, 4 Jun 2012 19:33:09 +0200 Subject: s390/vmlogrdr: cleanup driver attribute usage Let the driver core handle driver attribute creation and removal. This will simplify the code and eliminates races between attribute availability and userspace notification via uevents. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/char/vmlogrdr.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index e9d70aedbaa2..5138fc93dc00 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -656,10 +656,19 @@ static ssize_t vmlogrdr_recording_status_show(struct device_driver *driver, len = strlen(buf); return len; } - - static DRIVER_ATTR(recording_status, 0444, vmlogrdr_recording_status_show, NULL); +static struct attribute *vmlogrdr_drv_attrs[] = { + &driver_attr_recording_status.attr, + NULL, +}; +static struct attribute_group vmlogrdr_drv_attr_group = { + .attrs = vmlogrdr_drv_attrs, +}; +static const struct attribute_group *vmlogrdr_drv_attr_groups[] = { + &vmlogrdr_drv_attr_group, + NULL, +}; static struct attribute *vmlogrdr_attrs[] = { &dev_attr_autopurge.attr, @@ -704,9 +713,9 @@ static struct device_driver vmlogrdr_driver = { .name = "vmlogrdr", .bus = &iucv_bus, .pm = &vmlogrdr_pm_ops, + .groups = vmlogrdr_drv_attr_groups, }; - static int vmlogrdr_register_driver(void) { int ret; @@ -720,21 +729,14 @@ static int vmlogrdr_register_driver(void) if (ret) goto out_iucv; - ret = driver_create_file(&vmlogrdr_driver, - &driver_attr_recording_status); - if (ret) - goto out_driver; - vmlogrdr_class = class_create(THIS_MODULE, "vmlogrdr"); if (IS_ERR(vmlogrdr_class)) { ret = PTR_ERR(vmlogrdr_class); vmlogrdr_class = NULL; - goto out_attr; + goto out_driver; } return 0; -out_attr: - driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status); out_driver: driver_unregister(&vmlogrdr_driver); out_iucv: @@ -748,7 +750,6 @@ static void vmlogrdr_unregister_driver(void) { class_destroy(vmlogrdr_class); vmlogrdr_class = NULL; - driver_remove_file(&vmlogrdr_driver, &driver_attr_recording_status); driver_unregister(&vmlogrdr_driver); iucv_unregister(&vmlogrdr_iucv_handler, 1); } -- cgit v1.2.3 From 9a1a69901dae144ae160af5ac38848866d7c83b1 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:06 +0300 Subject: wl18xx: add new module Add the wl18xx module and the probe functions. Use wlcore for the main parts (not functional at this point due to differences in the wl18xx initialization). Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/Kconfig | 1 + drivers/net/wireless/ti/Makefile | 1 + drivers/net/wireless/ti/wl18xx/Kconfig | 6 +++ drivers/net/wireless/ti/wl18xx/Makefile | 3 ++ drivers/net/wireless/ti/wl18xx/main.c | 73 +++++++++++++++++++++++++++++++++ 5 files changed, 84 insertions(+) create mode 100644 drivers/net/wireless/ti/wl18xx/Kconfig create mode 100644 drivers/net/wireless/ti/wl18xx/Makefile create mode 100644 drivers/net/wireless/ti/wl18xx/main.c (limited to 'drivers') diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig index 1a72932e2213..be800119d0a3 100644 --- a/drivers/net/wireless/ti/Kconfig +++ b/drivers/net/wireless/ti/Kconfig @@ -8,6 +8,7 @@ menuconfig WL_TI if WL_TI source "drivers/net/wireless/ti/wl1251/Kconfig" source "drivers/net/wireless/ti/wl12xx/Kconfig" +source "drivers/net/wireless/ti/wl18xx/Kconfig" # keep last for automatic dependencies source "drivers/net/wireless/ti/wlcore/Kconfig" diff --git a/drivers/net/wireless/ti/Makefile b/drivers/net/wireless/ti/Makefile index 0a565622d4a4..4d6823983c04 100644 --- a/drivers/net/wireless/ti/Makefile +++ b/drivers/net/wireless/ti/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_WLCORE) += wlcore/ obj-$(CONFIG_WL12XX) += wl12xx/ obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wlcore/ obj-$(CONFIG_WL1251) += wl1251/ +obj-$(CONFIG_WL18XX) += wl18xx/ diff --git a/drivers/net/wireless/ti/wl18xx/Kconfig b/drivers/net/wireless/ti/wl18xx/Kconfig new file mode 100644 index 000000000000..1451d1f377c9 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/Kconfig @@ -0,0 +1,6 @@ +config WL18XX + tristate "TI wl18xx support" + select WLCORE + ---help--- + This module adds support for wireless adapters based on TI + WiLink 8 chipsets. diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile new file mode 100644 index 000000000000..d187037bef70 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -0,0 +1,3 @@ +wl18xx-objs = main.o + +obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c new file mode 100644 index 000000000000..cb835d914bab --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -0,0 +1,73 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include + +#include "../wlcore/wlcore.h" +#include "../wlcore/debug.h" + +int __devinit wl18xx_probe(struct platform_device *pdev) +{ + struct wl1271 *wl; + struct ieee80211_hw *hw; + + hw = wlcore_alloc_hw(0); + if (IS_ERR(hw)) { + wl1271_error("can't allocate hw"); + return PTR_ERR(hw); + } + + wl = hw->priv; + + return wlcore_probe(wl, pdev); +} + +static const struct platform_device_id wl18xx_id_table[] __devinitconst = { + { "wl18xx", 0 }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(platform, wl18xx_id_table); + +static struct platform_driver wl18xx_driver = { + .probe = wl18xx_probe, + .remove = __devexit_p(wlcore_remove), + .id_table = wl18xx_id_table, + .driver = { + .name = "wl18xx_driver", + .owner = THIS_MODULE, + } +}; + +static int __init wl18xx_init(void) +{ + return platform_driver_register(&wl18xx_driver); +} +module_init(wl18xx_init); + +static void __exit wl18xx_exit(void) +{ + platform_driver_unregister(&wl18xx_driver); +} +module_exit(wl18xx_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Luciano Coelho "); -- cgit v1.2.3 From 197c62832b1a347f933fea286fc70266026ef5fb Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:07 +0300 Subject: wlcore_sdio/wl18xx: use SDIO revision number to identify wl18xx chips At least in PG1, the wl18xx chips use the same SDIO vendor/device ID, so it's not possible to figure out which driver is to be used. As a workaround, we can check the SDIO revision number, because wl18xx uses 3.00 and wl12xx does not. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/sdio.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 0a72347cfc4c..1082e394e8f7 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, struct resource res[1]; mmc_pm_flag_t mmcflags; int ret = -ENOMEM; + const char *chip_family; /* We are only able to handle the wlan function */ if (func->num != 0x02) @@ -236,7 +238,18 @@ static int __devinit wl1271_probe(struct sdio_func *func, /* Tell PM core that we don't need the card to be powered now */ pm_runtime_put_noidle(&func->dev); - glue->core = platform_device_alloc("wl12xx", -1); + /* + * Due to a hardware bug, we can't differentiate wl18xx from + * wl12xx, because both report the same device ID. The only + * way to differentiate is by checking the SDIO revision, + * which is 3.00 on the wl18xx chips. + */ + if (func->card->cccr.sdio_vsn == SDIO_SDIO_REV_3_00) + chip_family = "wl18xx"; + else + chip_family = "wl12xx"; + + glue->core = platform_device_alloc(chip_family, -1); if (!glue->core) { dev_err(glue->dev, "can't allocate platform_device"); ret = -ENOMEM; -- cgit v1.2.3 From 554c36b7373ffcbe9c752ad779402f8cf3a44b48 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:08 +0300 Subject: wl18xx: add empty operations struct We don't have any chip-specific operations yet, but now wlcore has defined an operations structure and requires the pointer to be set. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index cb835d914bab..3fa9298e0b42 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -25,6 +25,9 @@ #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" +static struct wlcore_ops wl18xx_ops = { +}; + int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -37,6 +40,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) } wl = hw->priv; + wl->ops = &wl18xx_ops; return wlcore_probe(wl, pdev); } -- cgit v1.2.3 From 82b890cd4ea90e127feca3c27a7625e304e7e193 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:09 +0300 Subject: wl18xx: add partition table Define and add the partition table in the wlcore struct. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 3fa9298e0b42..f3a164a9d294 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -28,6 +28,40 @@ static struct wlcore_ops wl18xx_ops = { }; +static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { + [PART_TOP_PRCM_ELP_SOC] = { + .mem = { .start = 0x00A02000, .size = 0x00010000 }, + .reg = { .start = 0x00807000, .size = 0x00005000 }, + .mem2 = { .start = 0x00800000, .size = 0x0000B000 }, + .mem3 = { .start = 0x00000000, .size = 0x00000000 }, + }, + [PART_DOWN] = { + .mem = { .start = 0x00000000, .size = 0x00014000 }, + .reg = { .start = 0x00810000, .size = 0x0000BFFF }, + .mem2 = { .start = 0x00000000, .size = 0x00000000 }, + .mem3 = { .start = 0x00000000, .size = 0x00000000 }, + }, + [PART_BOOT] = { + .mem = { .start = 0x00700000, .size = 0x0000030c }, + .reg = { .start = 0x00802000, .size = 0x00014578 }, + .mem2 = { .start = 0x00B00404, .size = 0x00001000 }, + .mem3 = { .start = 0x00C00000, .size = 0x00000400 }, + }, + [PART_WORK] = { + .mem = { .start = 0x00800000, .size = 0x000050FC }, + .reg = { .start = 0x00B00404, .size = 0x00001000 }, + .mem2 = { .start = 0x00C00000, .size = 0x00000400 }, + .mem3 = { .start = 0x00000000, .size = 0x00000000 }, + }, + [PART_PHY_INIT] = { + /* TODO: use the phy_conf struct size here */ + .mem = { .start = 0x80926000, .size = 252 }, + .reg = { .start = 0x00000000, .size = 0x00000000 }, + .mem2 = { .start = 0x00000000, .size = 0x00000000 }, + .mem3 = { .start = 0x00000000, .size = 0x00000000 }, + }, +}; + int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -41,6 +75,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl = hw->priv; wl->ops = &wl18xx_ops; + wl->ptable = wl18xx_ptable; return wlcore_probe(wl, pdev); } -- cgit v1.2.3 From 5d4a9fa692ce0fc9fff0a35e53b3a9be44b48da5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:10 +0300 Subject: wl18xx: add register table Add the register table with the appropriate values for wl18xx. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 23 +++++++ drivers/net/wireless/ti/wl18xx/reg.h | 112 ++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 drivers/net/wireless/ti/wl18xx/reg.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f3a164a9d294..88fd93485175 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -25,6 +25,8 @@ #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" +#include "reg.h" + static struct wlcore_ops wl18xx_ops = { }; @@ -62,6 +64,26 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { }, }; +static const int wl18xx_rtable[REG_TABLE_LEN] = { + [REG_ECPU_CONTROL] = WL18XX_REG_ECPU_CONTROL, + [REG_INTERRUPT_NO_CLEAR] = WL18XX_REG_INTERRUPT_NO_CLEAR, + [REG_INTERRUPT_ACK] = WL18XX_REG_INTERRUPT_ACK, + [REG_COMMAND_MAILBOX_PTR] = WL18XX_REG_COMMAND_MAILBOX_PTR, + [REG_EVENT_MAILBOX_PTR] = WL18XX_REG_EVENT_MAILBOX_PTR, + [REG_INTERRUPT_TRIG] = WL18XX_REG_INTERRUPT_TRIG_H, + [REG_INTERRUPT_MASK] = WL18XX_REG_INTERRUPT_MASK, + [REG_PC_ON_RECOVERY] = 0, /* TODO: where is the PC? */ + [REG_CHIP_ID_B] = WL18XX_REG_CHIP_ID_B, + [REG_CMD_MBOX_ADDRESS] = WL18XX_CMD_MBOX_ADDRESS, + + /* data access memory addresses, used with partition translation */ + [REG_SLV_MEM_DATA] = WL18XX_SLV_MEM_DATA, + [REG_SLV_REG_DATA] = WL18XX_SLV_REG_DATA, + + /* raw data access memory addresses */ + [REG_RAW_FW_STATUS_ADDR] = WL18XX_FW_STATUS_ADDR, +}; + int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -76,6 +98,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl = hw->priv; wl->ops = &wl18xx_ops; wl->ptable = wl18xx_ptable; + wl->rtable = wl18xx_rtable; return wlcore_probe(wl, pdev); } diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h new file mode 100644 index 000000000000..9af0c83727d6 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -0,0 +1,112 @@ +/* + * This file is part of wlcore + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __REG_H__ +#define __REG_H__ + +#define WL18XX_REGISTERS_BASE 0x00800000 +#define WL18XX_CODE_BASE 0x00000000 +#define WL18XX_DATA_BASE 0x00400000 +#define WL18XX_DOUBLE_BUFFER_BASE 0x00600000 +#define WL18XX_MCU_KEY_SEARCH_BASE 0x00700000 +#define WL18XX_PHY_BASE 0x00900000 +#define WL18XX_TOP_OCP_BASE 0x00A00000 +#define WL18XX_PACKET_RAM_BASE 0x00B00000 +#define WL18XX_HOST_BASE 0x00C00000 + +#define WL18XX_REGISTERS_DOWN_SIZE 0x0000B000 + +#define WL18XX_REG_BOOT_PART_START 0x00802000 +#define WL18XX_REG_BOOT_PART_SIZE 0x00014578 + +#define WL18XX_PHY_INIT_MEM_ADDR 0x80926000 + +#define WL18XX_SDIO_WSPI_BASE (WL18XX_REGISTERS_BASE) +#define WL18XX_REG_CONFIG_BASE (WL18XX_REGISTERS_BASE + 0x02000) +#define WL18XX_WGCM_REGS_BASE (WL18XX_REGISTERS_BASE + 0x03000) +#define WL18XX_ENC_BASE (WL18XX_REGISTERS_BASE + 0x04000) +#define WL18XX_INTERRUPT_BASE (WL18XX_REGISTERS_BASE + 0x05000) +#define WL18XX_UART_BASE (WL18XX_REGISTERS_BASE + 0x06000) +#define WL18XX_WELP_BASE (WL18XX_REGISTERS_BASE + 0x07000) +#define WL18XX_TCP_CKSM_BASE (WL18XX_REGISTERS_BASE + 0x08000) +#define WL18XX_FIFO_BASE (WL18XX_REGISTERS_BASE + 0x09000) +#define WL18XX_OCP_BRIDGE_BASE (WL18XX_REGISTERS_BASE + 0x0A000) +#define WL18XX_PMAC_RX_BASE (WL18XX_REGISTERS_BASE + 0x14800) +#define WL18XX_PMAC_ACM_BASE (WL18XX_REGISTERS_BASE + 0x14C00) +#define WL18XX_PMAC_TX_BASE (WL18XX_REGISTERS_BASE + 0x15000) +#define WL18XX_PMAC_CSR_BASE (WL18XX_REGISTERS_BASE + 0x15400) + +#define WL18XX_REG_ECPU_CONTROL (WL18XX_REGISTERS_BASE + 0x02004) +#define WL18XX_REG_INTERRUPT_NO_CLEAR (WL18XX_REGISTERS_BASE + 0x050E8) +#define WL18XX_REG_INTERRUPT_ACK (WL18XX_REGISTERS_BASE + 0x050F0) +#define WL18XX_REG_INTERRUPT_TRIG (WL18XX_REGISTERS_BASE + 0x5074) +#define WL18XX_REG_INTERRUPT_TRIG_H (WL18XX_REGISTERS_BASE + 0x5078) +#define WL18XX_REG_INTERRUPT_MASK (WL18XX_REGISTERS_BASE + 0x0050DC) + +#define WL18XX_REG_CHIP_ID_B (WL18XX_REGISTERS_BASE + 0x01542C) + +#define WL18XX_SLV_MEM_DATA (WL18XX_HOST_BASE + 0x0018) +#define WL18XX_SLV_REG_DATA (WL18XX_HOST_BASE + 0x0008) + +/* Scratch Pad registers*/ +#define WL18XX_SCR_PAD0 (WL18XX_REGISTERS_BASE + 0x0154EC) +#define WL18XX_SCR_PAD1 (WL18XX_REGISTERS_BASE + 0x0154F0) +#define WL18XX_SCR_PAD2 (WL18XX_REGISTERS_BASE + 0x0154F4) +#define WL18XX_SCR_PAD3 (WL18XX_REGISTERS_BASE + 0x0154F8) +#define WL18XX_SCR_PAD4 (WL18XX_REGISTERS_BASE + 0x0154FC) +#define WL18XX_SCR_PAD4_SET (WL18XX_REGISTERS_BASE + 0x015504) +#define WL18XX_SCR_PAD4_CLR (WL18XX_REGISTERS_BASE + 0x015500) +#define WL18XX_SCR_PAD5 (WL18XX_REGISTERS_BASE + 0x015508) +#define WL18XX_SCR_PAD5_SET (WL18XX_REGISTERS_BASE + 0x015510) +#define WL18XX_SCR_PAD5_CLR (WL18XX_REGISTERS_BASE + 0x01550C) +#define WL18XX_SCR_PAD6 (WL18XX_REGISTERS_BASE + 0x015514) +#define WL18XX_SCR_PAD7 (WL18XX_REGISTERS_BASE + 0x015518) +#define WL18XX_SCR_PAD8 (WL18XX_REGISTERS_BASE + 0x01551C) +#define WL18XX_SCR_PAD9 (WL18XX_REGISTERS_BASE + 0x015520) + +/* Spare registers*/ +#define WL18XX_SPARE_A1 (WL18XX_REGISTERS_BASE + 0x002194) +#define WL18XX_SPARE_A2 (WL18XX_REGISTERS_BASE + 0x002198) +#define WL18XX_SPARE_A3 (WL18XX_REGISTERS_BASE + 0x00219C) +#define WL18XX_SPARE_A4 (WL18XX_REGISTERS_BASE + 0x0021A0) +#define WL18XX_SPARE_A5 (WL18XX_REGISTERS_BASE + 0x0021A4) +#define WL18XX_SPARE_A6 (WL18XX_REGISTERS_BASE + 0x0021A8) +#define WL18XX_SPARE_A7 (WL18XX_REGISTERS_BASE + 0x0021AC) +#define WL18XX_SPARE_A8 (WL18XX_REGISTERS_BASE + 0x0021B0) +#define WL18XX_SPARE_B1 (WL18XX_REGISTERS_BASE + 0x015524) +#define WL18XX_SPARE_B2 (WL18XX_REGISTERS_BASE + 0x015528) +#define WL18XX_SPARE_B3 (WL18XX_REGISTERS_BASE + 0x01552C) +#define WL18XX_SPARE_B4 (WL18XX_REGISTERS_BASE + 0x015530) +#define WL18XX_SPARE_B5 (WL18XX_REGISTERS_BASE + 0x015534) +#define WL18XX_SPARE_B6 (WL18XX_REGISTERS_BASE + 0x015538) +#define WL18XX_SPARE_B7 (WL18XX_REGISTERS_BASE + 0x01553C) +#define WL18XX_SPARE_B8 (WL18XX_REGISTERS_BASE + 0x015540) + +#define WL18XX_REG_COMMAND_MAILBOX_PTR (WL18XX_SCR_PAD0) +#define WL18XX_REG_EVENT_MAILBOX_PTR (WL18XX_SCR_PAD1) + +#define WL18XX_CMD_MBOX_ADDRESS 0xB007B4 + +#define WL18XX_FW_STATUS_ADDR 0x50F8 + +#define CHIP_ID_185x_PG10 (0x06030101) + +#endif /* __REG_H__ */ -- cgit v1.2.3 From 0cd6543f6852d646985786a429027bd0461e1cb4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:11 +0300 Subject: wl18xx: add identify chip operation Add identify_chip operation to detect the chip ID for wl185x and set the correct firmware name. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 88fd93485175..e517afd98f41 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -27,9 +27,6 @@ #include "reg.h" -static struct wlcore_ops wl18xx_ops = { -}; - static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { [PART_TOP_PRCM_ELP_SOC] = { .mem = { .start = 0x00A02000, .size = 0x00010000 }, @@ -84,6 +81,36 @@ static const int wl18xx_rtable[REG_TABLE_LEN] = { [REG_RAW_FW_STATUS_ADDR] = WL18XX_FW_STATUS_ADDR, }; +/* TODO: maybe move to a new header file? */ +#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin" + +static int wl18xx_identify_chip(struct wl1271 *wl) +{ + int ret = 0; + + switch (wl->chip.id) { + case CHIP_ID_185x_PG10: + wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)", + wl->chip.id); + wl->sr_fw_name = WL18XX_FW_NAME; + wl->quirks |= WLCORE_QUIRK_NO_ELP; + + /* TODO: need to blocksize alignment for RX/TX separately? */ + break; + default: + wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); + ret = -ENODEV; + goto out; + } + +out: + return ret; +} + +static struct wlcore_ops wl18xx_ops = { + .identify_chip = wl18xx_identify_chip, +}; + int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -133,3 +160,4 @@ module_exit(wl18xx_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); +MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From 46a1d51261bc4d9cd35b4e41a9b623687c0b4b8c Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:12 +0300 Subject: wl18xx: add some boot operations and hw-specific configurations Implement the boot operation. Add a wl18xx-specific configuration structure (namely to configure the mac and phy parameters). The default hw configuration matches the DVP board. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/conf.h | 57 +++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 180 +++++++++++++++++++++++++++++++++- drivers/net/wireless/ti/wl18xx/reg.h | 75 ++++++++++++++ 3 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ti/wl18xx/conf.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h new file mode 100644 index 000000000000..7cd7bf16879e --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -0,0 +1,57 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_CONF_H__ +#define __WL18XX_CONF_H__ + +struct wl18xx_conf_phy { + u8 phy_standalone; + u8 rdl; + u8 enable_clpc; + u8 enable_tx_low_pwr_on_siso_rdl; + u8 auto_detect; + u8 dedicated_fem; + u8 low_band_component; + u8 low_band_component_type; + u8 high_band_component; + u8 high_band_component_type; + u8 number_of_assembled_ant2_4; + u8 number_of_assembled_ant5; + u8 external_pa_dc2dc; + u8 tcxo_ldo_voltage; + u8 xtal_itrim_val; + u8 srf_state; + u8 io_configuration; + u8 sdio_configuration; + u8 settings; + u8 rx_profile; + u8 primary_clock_setting_time; + u8 clock_valid_on_wake_up; + u8 secondary_clock_setting_time; +}; + +struct wl18xx_conf { + /* TODO: move the wlcore conf here? */ + + struct wl18xx_conf_phy phy; +}; + +#endif /* __WL18XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index e517afd98f41..12632d0373cc 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -24,8 +24,40 @@ #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" +#include "../wlcore/io.h" +#include "../wlcore/acx.h" +#include "../wlcore/boot.h" #include "reg.h" +#include "conf.h" + +static struct wl18xx_conf wl18xx_default_conf = { + .phy = { + .phy_standalone = 0x00, + .primary_clock_setting_time = 0x05, + .clock_valid_on_wake_up = 0x00, + .secondary_clock_setting_time = 0x05, + .rdl = 0x01, + .auto_detect = 0x00, + .dedicated_fem = FEM_NONE, + .low_band_component = COMPONENT_2_WAY_SWITCH, + .low_band_component_type = 0x05, + .high_band_component = COMPONENT_2_WAY_SWITCH, + .high_band_component_type = 0x09, + .number_of_assembled_ant2_4 = 0x01, + .number_of_assembled_ant5 = 0x01, + .external_pa_dc2dc = 0x00, + .tcxo_ldo_voltage = 0x00, + .xtal_itrim_val = 0x04, + .srf_state = 0x00, + .io_configuration = 0x01, + .sdio_configuration = 0x00, + .settings = 0x00, + .enable_clpc = 0x00, + .enable_tx_low_pwr_on_siso_rdl = 0x00, + .rx_profile = 0x00, + }, +}; static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { [PART_TOP_PRCM_ELP_SOC] = { @@ -107,8 +139,154 @@ out: return ret; } +static void wl18xx_set_clk(struct wl1271 *wl) +{ + /* + * TODO: this is hardcoded just for DVP/EVB, fix according to + * new unified_drv. + */ + wl1271_write32(wl, WL18XX_SCR_PAD2, 0xB3); + + wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + wl1271_write32(wl, 0x00A02360, 0xD0078); + wl1271_write32(wl, 0x00A0236c, 0x12); + wl1271_write32(wl, 0x00A02390, 0x20118); +} + +static void wl18xx_boot_soft_reset(struct wl1271 *wl) +{ + /* disable Rx/Tx */ + wl1271_write32(wl, WL18XX_ENABLE, 0x0); + + /* disable auto calibration on start*/ + wl1271_write32(wl, WL18XX_SPARE_A2, 0xffff); +} + +static int wl18xx_pre_boot(struct wl1271 *wl) +{ + /* TODO: add hw_pg_ver reading */ + + wl18xx_set_clk(wl); + + /* Continue the ELP wake up sequence */ + wl1271_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + udelay(500); + + wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + + /* Disable interrupts */ + wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + + wl18xx_boot_soft_reset(wl); + + return 0; +} + +static void wl18xx_pre_upload(struct wl1271 *wl) +{ + u32 tmp; + + wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + + /* TODO: check if this is all needed */ + wl1271_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND); + + tmp = wlcore_read_reg(wl, REG_CHIP_ID_B); + + wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); + + tmp = wl1271_read32(wl, WL18XX_SCR_PAD2); +} + +static void wl18xx_set_mac_and_phy(struct wl1271 *wl) +{ + struct wl18xx_mac_and_phy_params params; + + memset(¶ms, 0, sizeof(params)); + + params.phy_standalone = wl18xx_default_conf.phy.phy_standalone; + params.rdl = wl18xx_default_conf.phy.rdl; + params.enable_clpc = wl18xx_default_conf.phy.enable_clpc; + params.enable_tx_low_pwr_on_siso_rdl = + wl18xx_default_conf.phy.enable_tx_low_pwr_on_siso_rdl; + params.auto_detect = wl18xx_default_conf.phy.auto_detect; + params.dedicated_fem = wl18xx_default_conf.phy.dedicated_fem; + params.low_band_component = wl18xx_default_conf.phy.low_band_component; + params.low_band_component_type = + wl18xx_default_conf.phy.low_band_component_type; + params.high_band_component = + wl18xx_default_conf.phy.high_band_component; + params.high_band_component_type = + wl18xx_default_conf.phy.high_band_component_type; + params.number_of_assembled_ant2_4 = + wl18xx_default_conf.phy.number_of_assembled_ant2_4; + params.number_of_assembled_ant5 = + wl18xx_default_conf.phy.number_of_assembled_ant5; + params.external_pa_dc2dc = wl18xx_default_conf.phy.external_pa_dc2dc; + params.tcxo_ldo_voltage = wl18xx_default_conf.phy.tcxo_ldo_voltage; + params.xtal_itrim_val = wl18xx_default_conf.phy.xtal_itrim_val; + params.srf_state = wl18xx_default_conf.phy.srf_state; + params.io_configuration = wl18xx_default_conf.phy.io_configuration; + params.sdio_configuration = wl18xx_default_conf.phy.sdio_configuration; + params.settings = wl18xx_default_conf.phy.settings; + params.rx_profile = wl18xx_default_conf.phy.rx_profile; + params.primary_clock_setting_time = + wl18xx_default_conf.phy.primary_clock_setting_time; + params.clock_valid_on_wake_up = + wl18xx_default_conf.phy.clock_valid_on_wake_up; + params.secondary_clock_setting_time = + wl18xx_default_conf.phy.secondary_clock_setting_time; + + /* TODO: hardcoded for now */ + params.board_type = BOARD_TYPE_DVP_EVB_18XX; + + wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); + wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)¶ms, + sizeof(params), false); +} + +static void wl18xx_enable_interrupts(struct wl1271 *wl) +{ + wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); + + wlcore_enable_interrupts(wl); + wlcore_write_reg(wl, REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); +} + +static int wl18xx_boot(struct wl1271 *wl) +{ + int ret; + + ret = wl18xx_pre_boot(wl); + if (ret < 0) + goto out; + + ret = wlcore_boot_upload_nvs(wl); + if (ret < 0) + goto out; + + wl18xx_pre_upload(wl); + + ret = wlcore_boot_upload_firmware(wl); + if (ret < 0) + goto out; + + wl18xx_set_mac_and_phy(wl); + + ret = wlcore_boot_run_firmware(wl); + if (ret < 0) + goto out; + + wl18xx_enable_interrupts(wl); + +out: + return ret; +} + static struct wlcore_ops wl18xx_ops = { - .identify_chip = wl18xx_identify_chip, + .identify_chip = wl18xx_identify_chip, + .boot = wl18xx_boot, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 9af0c83727d6..2916d6939d44 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -102,6 +102,10 @@ #define WL18XX_REG_COMMAND_MAILBOX_PTR (WL18XX_SCR_PAD0) #define WL18XX_REG_EVENT_MAILBOX_PTR (WL18XX_SCR_PAD1) +#define WL18XX_EEPROMLESS_IND (WL18XX_SCR_PAD4) + +#define WL18XX_WELP_ARM_COMMAND (WL18XX_REGISTERS_BASE + 0x7100) +#define WL18XX_ENABLE (WL18XX_REGISTERS_BASE + 0x01543C) #define WL18XX_CMD_MBOX_ADDRESS 0xB007B4 @@ -109,4 +113,75 @@ #define CHIP_ID_185x_PG10 (0x06030101) +/* TODO: maybe move elsewhere? */ +#define NUM_OF_CHANNELS_11_ABG 150 +#define NUM_OF_CHANNELS_11_P 7 +#define WL18XX_NUM_OF_SUB_BANDS 9 +#define SRF_TABLE_LEN 16 +#define PIN_MUXING_SIZE 2 + +enum { + COMPONENT_NO_SWITCH = 0x0, + COMPONENT_2_WAY_SWITCH = 0x1, + COMPONENT_3_WAY_SWITCH = 0x2, + COMPONENT_MATCHING = 0x3, +}; + +enum { + FEM_NONE = 0x0, + FEM_VENDOR_1 = 0x1, + FEM_VENDOR_2 = 0x2, + FEM_VENDOR_3 = 0x3, +}; + +enum { + BOARD_TYPE_FPGA_18XX = 0, + BOARD_TYPE_HDK_18XX = 1, + BOARD_TYPE_DVP_EVB_18XX = 2, +}; + +struct wl18xx_mac_and_phy_params { + u8 phy_standalone; + u8 rdl; + u8 enable_clpc; + u8 enable_tx_low_pwr_on_siso_rdl; + u8 auto_detect; + u8 dedicated_fem; + + u8 low_band_component; + + /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ + u8 low_band_component_type; + + u8 high_band_component; + + /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ + u8 high_band_component_type; + u8 number_of_assembled_ant2_4; + u8 number_of_assembled_ant5; + u8 pin_muxing_platform_options[PIN_MUXING_SIZE]; + u8 external_pa_dc2dc; + u8 tcxo_ldo_voltage; + u8 xtal_itrim_val; + u8 srf_state; + u8 srf1[SRF_TABLE_LEN]; + u8 srf2[SRF_TABLE_LEN]; + u8 srf3[SRF_TABLE_LEN]; + u8 io_configuration; + u8 sdio_configuration; + u8 settings; + u8 rx_profile; + u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG]; + u8 pwr_limit_reference_11_abg; + u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P]; + u8 pwr_limit_reference_11p; + u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; + u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; + u8 primary_clock_setting_time; + u8 clock_valid_on_wake_up; + u8 secondary_clock_setting_time; + u8 board_type; + u8 padding[1]; +} __packed; + #endif /* __REG_H__ */ -- cgit v1.2.3 From 274c66cdcd89654de276be871627181b192a6e30 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:13 +0300 Subject: wl18xx: add trigger command and ack event operations Add the operations that allow wlcore to trigger commands to the firmware and acknowledge when an event has been fully received. Allocate a private buffer to hold the maximum sized cmd. Send the entire length of the buffer each time a command is sent to signal EOT. Remove the previous EOT mechanism. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 20 ++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/reg.h | 16 ++++++++++++++++ drivers/net/wireless/ti/wl18xx/wl18xx.h | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 drivers/net/wireless/ti/wl18xx/wl18xx.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 12632d0373cc..6a487c83752d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -30,6 +30,7 @@ #include "reg.h" #include "conf.h" +#include "wl18xx.h" static struct wl18xx_conf wl18xx_default_conf = { .phy = { @@ -284,9 +285,28 @@ out: return ret; } +static void wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, + void *buf, size_t len) +{ + struct wl18xx_priv *priv = wl->priv; + + memcpy(priv->cmd_buf, buf, len); + memset(priv->cmd_buf + len, 0, WL18XX_CMD_MAX_SIZE - len); + + wl1271_write(wl, cmd_box_addr, priv->cmd_buf, WL18XX_CMD_MAX_SIZE, + false); +} + +static void wl18xx_ack_event(struct wl1271 *wl) +{ + wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL18XX_INTR_TRIG_EVENT_ACK); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, + .trigger_cmd = wl18xx_trigger_cmd, + .ack_event = wl18xx_ack_event, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 2916d6939d44..53987997f78f 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -113,6 +113,22 @@ #define CHIP_ID_185x_PG10 (0x06030101) +/* + * Host Command Interrupt. Setting this bit masks + * the interrupt that the host issues to inform + * the FW that it has sent a command + * to the Wlan hardware Command Mailbox. + */ +#define WL18XX_INTR_TRIG_CMD BIT(28) + +/* + * Host Event Acknowlegde Interrupt. The host + * sets this bit to acknowledge that it received + * the unsolicited information from the event + * mailbox. + */ +#define WL18XX_INTR_TRIG_EVENT_ACK BIT(29) + /* TODO: maybe move elsewhere? */ #define NUM_OF_CHANNELS_11_ABG 150 #define NUM_OF_CHANNELS_11_P 7 diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h new file mode 100644 index 000000000000..58709bb88b7b --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -0,0 +1,33 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_PRIV_H__ +#define __WL18XX_PRIV_H__ + + +#define WL18XX_CMD_MAX_SIZE 740 + +struct wl18xx_priv { + /* buffer for sending commands to FW */ + u8 cmd_buf[WL18XX_CMD_MAX_SIZE]; +}; + +#endif /* __WL18XX_PRIV_H__ */ -- cgit v1.2.3 From 9d1c973a7b569652cea0ea00dc7ac409b08a8381 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:14 +0300 Subject: wl18xx: create per-chip-family private storage Make use of the wlcore provided private storage in the 18xx low-level driver. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 6a487c83752d..b298e0121377 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -313,8 +313,9 @@ int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; struct ieee80211_hw *hw; + struct wl18xx_priv *priv; - hw = wlcore_alloc_hw(0); + hw = wlcore_alloc_hw(sizeof(*priv)); if (IS_ERR(hw)) { wl1271_error("can't allocate hw"); return PTR_ERR(hw); -- cgit v1.2.3 From cb7b5d86c8283482bbf26398e83d2cbc5e36e193 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:15 +0300 Subject: wl18xx: set the number of Tx descriptors Initialize the number of Tx-descriptors for the 18xx family. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index b298e0121377..68f5e09be508 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -325,6 +325,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->ops = &wl18xx_ops; wl->ptable = wl18xx_ptable; wl->rtable = wl18xx_rtable; + wl->num_tx_desc = 32; return wlcore_probe(wl, pdev); } -- cgit v1.2.3 From 1349c4212aacde0d26b20e9ff2ba7501d6e33d3c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:16 +0300 Subject: wl18xx: set normal/GEM Tx spare block counts Initialize the Tx spare block counts for all operating modes in the 18xx card. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 68f5e09be508..70683adc9cf3 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -32,6 +32,9 @@ #include "conf.h" #include "wl18xx.h" +#define WL18XX_TX_HW_BLOCK_SPARE 1 +#define WL18XX_TX_HW_GEM_BLOCK_SPARE 2 + static struct wl18xx_conf wl18xx_default_conf = { .phy = { .phy_standalone = 0x00, @@ -326,6 +329,8 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->ptable = wl18xx_ptable; wl->rtable = wl18xx_rtable; wl->num_tx_desc = 32; + wl->normal_tx_spare = WL18XX_TX_HW_BLOCK_SPARE; + wl->gem_tx_spare = WL18XX_TX_HW_GEM_BLOCK_SPARE; return wlcore_probe(wl, pdev); } -- cgit v1.2.3 From 624845b31cae0bf6671c3d0cbbec8214cf7d8584 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:17 +0300 Subject: wl18xx: implement hw op for calculating hw block count per packet Implement the calc_tx_blocks op for the 18xx family. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 70683adc9cf3..055b194827b3 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -34,6 +34,7 @@ #define WL18XX_TX_HW_BLOCK_SPARE 1 #define WL18XX_TX_HW_GEM_BLOCK_SPARE 2 +#define WL18XX_TX_HW_BLOCK_SIZE 268 static struct wl18xx_conf wl18xx_default_conf = { .phy = { @@ -305,11 +306,18 @@ static void wl18xx_ack_event(struct wl1271 *wl) wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL18XX_INTR_TRIG_EVENT_ACK); } +static u32 wl18xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) +{ + u32 blk_size = WL18XX_TX_HW_BLOCK_SIZE; + return (len + blk_size - 1) / blk_size + spare_blks; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, .trigger_cmd = wl18xx_trigger_cmd, .ack_event = wl18xx_ack_event, + .calc_tx_blocks = wl18xx_calc_tx_blocks, }; int __devinit wl18xx_probe(struct platform_device *pdev) -- cgit v1.2.3 From fb0f2e4ac0635549ce035c461d5050643ea5684c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:18 +0300 Subject: wl18xx: implement hw op for setting blocks in hw_tx_desc Add the 18xx variant to the HW Tx descriptor union and set the 18xx specific values during Tx. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 10 ++++++++++ drivers/net/wireless/ti/wlcore/tx.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 055b194827b3..c85492ead0f9 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -26,6 +26,7 @@ #include "../wlcore/debug.h" #include "../wlcore/io.h" #include "../wlcore/acx.h" +#include "../wlcore/tx.h" #include "../wlcore/boot.h" #include "reg.h" @@ -312,12 +313,21 @@ static u32 wl18xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) return (len + blk_size - 1) / blk_size + spare_blks; } +static void +wl18xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, + u32 blks, u32 spare_blks) +{ + desc->wl18xx_mem.total_mem_blocks = blks; + desc->wl18xx_mem.reserved = 0; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, .trigger_cmd = wl18xx_trigger_cmd, .ack_event = wl18xx_ack_event, .calc_tx_blocks = wl18xx_calc_tx_blocks, + .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 2fd6e5dc6f75..e440c313537d 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -85,6 +85,19 @@ struct wl128x_tx_mem { u8 extra_bytes; } __packed; +struct wl18xx_tx_mem { + /* + * Total number of memory blocks allocated by the host for + * this packet. + */ + u8 total_mem_blocks; + + /* + * always zero + */ + u8 reserved; +} __packed; + /* * On wl128x based devices, when TX packets are aggregated, each packet * size must be aligned to the SDIO block size. The maximum block size @@ -100,6 +113,7 @@ struct wl1271_tx_hw_descr { union { struct wl127x_tx_mem wl127x_mem; struct wl128x_tx_mem wl128x_mem; + struct wl18xx_tx_mem wl18xx_mem; } __packed; /* Device time (in us) when the packet arrived to the driver */ __le32 start_time; -- cgit v1.2.3 From d2361c5140694c39add831693abcf82ad87fe31e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:19 +0300 Subject: wl18xx: implement hw op for setting frame length in tx_hw_desc Set the frame length during Tx in a way compatible with the 18xx FW. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c85492ead0f9..0a5422c88183 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -321,6 +321,19 @@ wl18xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, desc->wl18xx_mem.reserved = 0; } +static void +wl18xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, + struct sk_buff *skb) +{ + desc->length = cpu_to_le16(skb->len); + + wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d " + "len: %d life: %d mem: %d", desc->hlid, + le16_to_cpu(desc->length), + le16_to_cpu(desc->life_time), + desc->wl18xx_mem.total_mem_blocks); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -328,6 +341,7 @@ static struct wlcore_ops wl18xx_ops = { .ack_event = wl18xx_ack_event, .calc_tx_blocks = wl18xx_calc_tx_blocks, .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, + .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, }; int __devinit wl18xx_probe(struct platform_device *pdev) -- cgit v1.2.3 From f648eab75815086328ea92f31b10df16cf090075 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:20 +0300 Subject: wl18xx: define HW-rate translation elements/tables Define HW-rate conversion tables for the 18xx chip. Initialize the appropriate wlcore elements with these tables and values to allow conversion of HW-rates. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 115 ++++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/conf.h | 10 ++- 2 files changed, 124 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 0a5422c88183..7dcb8327b17f 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -37,6 +37,118 @@ #define WL18XX_TX_HW_GEM_BLOCK_SPARE 2 #define WL18XX_TX_HW_BLOCK_SIZE 268 +static const u8 wl18xx_rate_to_idx_2ghz[] = { + /* MCS rates are used only with 11n */ + 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ + 14, /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */ + 13, /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */ + 12, /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */ + 11, /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */ + 10, /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */ + 9, /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */ + 8, /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */ + 7, /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */ + 6, /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */ + 5, /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */ + 4, /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */ + 3, /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */ + 2, /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */ + 1, /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */ + 0, /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */ + + 11, /* WL18XX_CONF_HW_RXTX_RATE_54 */ + 10, /* WL18XX_CONF_HW_RXTX_RATE_48 */ + 9, /* WL18XX_CONF_HW_RXTX_RATE_36 */ + 8, /* WL18XX_CONF_HW_RXTX_RATE_24 */ + + /* TI-specific rate */ + CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22 */ + + 7, /* WL18XX_CONF_HW_RXTX_RATE_18 */ + 6, /* WL18XX_CONF_HW_RXTX_RATE_12 */ + 3, /* WL18XX_CONF_HW_RXTX_RATE_11 */ + 5, /* WL18XX_CONF_HW_RXTX_RATE_9 */ + 4, /* WL18XX_CONF_HW_RXTX_RATE_6 */ + 2, /* WL18XX_CONF_HW_RXTX_RATE_5_5 */ + 1, /* WL18XX_CONF_HW_RXTX_RATE_2 */ + 0 /* WL18XX_CONF_HW_RXTX_RATE_1 */ +}; + +static const u8 wl18xx_rate_to_idx_5ghz[] = { + /* MCS rates are used only with 11n */ + 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ + 14, /* WL18XX_CONF_HW_RXTX_RATE_MCS14 */ + 13, /* WL18XX_CONF_HW_RXTX_RATE_MCS13 */ + 12, /* WL18XX_CONF_HW_RXTX_RATE_MCS12 */ + 11, /* WL18XX_CONF_HW_RXTX_RATE_MCS11 */ + 10, /* WL18XX_CONF_HW_RXTX_RATE_MCS10 */ + 9, /* WL18XX_CONF_HW_RXTX_RATE_MCS9 */ + 8, /* WL18XX_CONF_HW_RXTX_RATE_MCS8 */ + 7, /* WL18XX_CONF_HW_RXTX_RATE_MCS7 */ + 6, /* WL18XX_CONF_HW_RXTX_RATE_MCS6 */ + 5, /* WL18XX_CONF_HW_RXTX_RATE_MCS5 */ + 4, /* WL18XX_CONF_HW_RXTX_RATE_MCS4 */ + 3, /* WL18XX_CONF_HW_RXTX_RATE_MCS3 */ + 2, /* WL18XX_CONF_HW_RXTX_RATE_MCS2 */ + 1, /* WL18XX_CONF_HW_RXTX_RATE_MCS1 */ + 0, /* WL18XX_CONF_HW_RXTX_RATE_MCS0 */ + + 7, /* WL18XX_CONF_HW_RXTX_RATE_54 */ + 6, /* WL18XX_CONF_HW_RXTX_RATE_48 */ + 5, /* WL18XX_CONF_HW_RXTX_RATE_36 */ + 4, /* WL18XX_CONF_HW_RXTX_RATE_24 */ + + /* TI-specific rate */ + CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_22 */ + + 3, /* WL18XX_CONF_HW_RXTX_RATE_18 */ + 2, /* WL18XX_CONF_HW_RXTX_RATE_12 */ + CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_11 */ + 1, /* WL18XX_CONF_HW_RXTX_RATE_9 */ + 0, /* WL18XX_CONF_HW_RXTX_RATE_6 */ + CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_5_5 */ + CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_2 */ + CONF_HW_RXTX_RATE_UNSUPPORTED, /* WL18XX_CONF_HW_RXTX_RATE_1 */ +}; + +static const u8 *wl18xx_band_rate_to_idx[] = { + [IEEE80211_BAND_2GHZ] = wl18xx_rate_to_idx_2ghz, + [IEEE80211_BAND_5GHZ] = wl18xx_rate_to_idx_5ghz +}; + +enum wl18xx_hw_rates { + WL18XX_CONF_HW_RXTX_RATE_MCS15 = 0, + WL18XX_CONF_HW_RXTX_RATE_MCS14, + WL18XX_CONF_HW_RXTX_RATE_MCS13, + WL18XX_CONF_HW_RXTX_RATE_MCS12, + WL18XX_CONF_HW_RXTX_RATE_MCS11, + WL18XX_CONF_HW_RXTX_RATE_MCS10, + WL18XX_CONF_HW_RXTX_RATE_MCS9, + WL18XX_CONF_HW_RXTX_RATE_MCS8, + WL18XX_CONF_HW_RXTX_RATE_MCS7, + WL18XX_CONF_HW_RXTX_RATE_MCS6, + WL18XX_CONF_HW_RXTX_RATE_MCS5, + WL18XX_CONF_HW_RXTX_RATE_MCS4, + WL18XX_CONF_HW_RXTX_RATE_MCS3, + WL18XX_CONF_HW_RXTX_RATE_MCS2, + WL18XX_CONF_HW_RXTX_RATE_MCS1, + WL18XX_CONF_HW_RXTX_RATE_MCS0, + WL18XX_CONF_HW_RXTX_RATE_54, + WL18XX_CONF_HW_RXTX_RATE_48, + WL18XX_CONF_HW_RXTX_RATE_36, + WL18XX_CONF_HW_RXTX_RATE_24, + WL18XX_CONF_HW_RXTX_RATE_22, + WL18XX_CONF_HW_RXTX_RATE_18, + WL18XX_CONF_HW_RXTX_RATE_12, + WL18XX_CONF_HW_RXTX_RATE_11, + WL18XX_CONF_HW_RXTX_RATE_9, + WL18XX_CONF_HW_RXTX_RATE_6, + WL18XX_CONF_HW_RXTX_RATE_5_5, + WL18XX_CONF_HW_RXTX_RATE_2, + WL18XX_CONF_HW_RXTX_RATE_1, + WL18XX_CONF_HW_RXTX_RATE_MAX, +}; + static struct wl18xx_conf wl18xx_default_conf = { .phy = { .phy_standalone = 0x00, @@ -363,6 +475,9 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->num_tx_desc = 32; wl->normal_tx_spare = WL18XX_TX_HW_BLOCK_SPARE; wl->gem_tx_spare = WL18XX_TX_HW_GEM_BLOCK_SPARE; + wl->band_rate_to_idx = wl18xx_band_rate_to_idx; + wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; + wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; return wlcore_probe(wl, pdev); } diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index fef0db4213bc..0798c1e88814 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -45,7 +45,15 @@ enum { CONF_HW_BIT_RATE_MCS_4 = BIT(17), CONF_HW_BIT_RATE_MCS_5 = BIT(18), CONF_HW_BIT_RATE_MCS_6 = BIT(19), - CONF_HW_BIT_RATE_MCS_7 = BIT(20) + CONF_HW_BIT_RATE_MCS_7 = BIT(20), + CONF_HW_BIT_RATE_MCS_8 = BIT(21), + CONF_HW_BIT_RATE_MCS_9 = BIT(22), + CONF_HW_BIT_RATE_MCS_10 = BIT(23), + CONF_HW_BIT_RATE_MCS_11 = BIT(24), + CONF_HW_BIT_RATE_MCS_12 = BIT(25), + CONF_HW_BIT_RATE_MCS_13 = BIT(26), + CONF_HW_BIT_RATE_MCS_14 = BIT(27), + CONF_HW_BIT_RATE_MCS_15 = BIT(28), }; enum { -- cgit v1.2.3 From 1fab39dc4a5fbd241787eb964d911d0aecbbcb84 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:21 +0300 Subject: wl18xx: add fw_status private data The wl18xx chip passes extra information in the firmware status to the driver. Add a private data section to handle that. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 2 +- drivers/net/wireless/ti/wl18xx/wl18xx.h | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 7dcb8327b17f..4a5ef6e53954 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -478,7 +478,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; - + wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); return wlcore_probe(wl, pdev); } diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 58709bb88b7b..68ef5ba2f607 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -30,4 +30,22 @@ struct wl18xx_priv { u8 cmd_buf[WL18XX_CMD_MAX_SIZE]; }; +#define WL18XX_FW_MAX_TX_STATUS_DESC 33 + +struct wl18xx_fw_status_priv { + /* + * Index in released_tx_desc for first byte that holds + * released tx host desc + */ + u8 fw_release_idx; + + /* + * Array of host Tx descriptors, where fw_release_idx + * indicated the first released idx. + */ + u8 released_tx_desc[WL18XX_FW_MAX_TX_STATUS_DESC]; + + u8 padding[2]; +}; + #endif /* __WL18XX_PRIV_H__ */ -- cgit v1.2.3 From 7cfefd1f2acbd5ff2a1a8846de058064bd63550a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:22 +0300 Subject: wl18xx: set Rx block-size alignment quirk The 18xx FW sends Rx blocks with aligned length. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 4a5ef6e53954..a074d811fef0 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -243,7 +243,8 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)", wl->chip.id); wl->sr_fw_name = WL18XX_FW_NAME; - wl->quirks |= WLCORE_QUIRK_NO_ELP; + wl->quirks |= WLCORE_QUIRK_NO_ELP | + WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; /* TODO: need to blocksize alignment for RX/TX separately? */ break; -- cgit v1.2.3 From 9c809f888370d87129d17028d515bb025fe94175 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:23 +0300 Subject: wl18xx: implement hw op for getting rx buffer data alignment Implement the HW op for getting alignment state in wl18xx. The FW aligns the Rx Ethernet payload data. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 13 +++++++++++++ drivers/net/wireless/ti/wlcore/rx.h | 3 +++ 2 files changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a074d811fef0..fef872608e00 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -27,6 +27,8 @@ #include "../wlcore/io.h" #include "../wlcore/acx.h" #include "../wlcore/tx.h" +#include "../wlcore/rx.h" +#include "../wlcore/io.h" #include "../wlcore/boot.h" #include "reg.h" @@ -447,6 +449,16 @@ wl18xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, desc->wl18xx_mem.total_mem_blocks); } +static enum wl_rx_buf_align +wl18xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) +{ + if (rx_desc & RX_BUF_PADDED_PAYLOAD) + return WLCORE_RX_BUF_PADDED; + + return WLCORE_RX_BUF_ALIGNED; +} + + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -455,6 +467,7 @@ static struct wlcore_ops wl18xx_ops = { .calc_tx_blocks = wl18xx_calc_tx_blocks, .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, + .get_rx_buf_align = wl18xx_get_rx_buf_align, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index e9a162a864ca..277936e14137 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -102,6 +102,9 @@ /* If set, the start of IP payload is not 4 bytes aligned */ #define RX_BUF_UNALIGNED_PAYLOAD BIT(20) +/* If set, the buffer was padded by the FW to be 4 bytes aligned */ +#define RX_BUF_PADDED_PAYLOAD BIT(30) + /* Describes the alignment state of a Rx buffer */ enum wl_rx_buf_align { WLCORE_RX_BUF_ALIGNED, -- cgit v1.2.3 From 30e2dd798dbd5929f981ec0c77ab8567e8859ad9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:24 +0300 Subject: wl18xx: implement hw op for getting rx packet data length Implement the 18xx-specific way for getting the length of a Rx packet. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index fef872608e00..40ed53c02866 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -458,6 +458,17 @@ wl18xx_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) return WLCORE_RX_BUF_ALIGNED; } +static u32 wl18xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, + u32 data_len) +{ + struct wl1271_rx_descriptor *desc = rx_data; + + /* invalid packet */ + if (data_len < sizeof(*desc)) + return 0; + + return data_len - sizeof(*desc); +} static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, @@ -468,6 +479,7 @@ static struct wlcore_ops wl18xx_ops = { .set_tx_desc_blocks = wl18xx_set_tx_desc_blocks, .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, .get_rx_buf_align = wl18xx_get_rx_buf_align, + .get_rx_packet_len = wl18xx_get_rx_packet_len, }; int __devinit wl18xx_probe(struct platform_device *pdev) -- cgit v1.2.3 From 872b345fbaef290f890d0bbd34b78ab50269980f Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:25 +0300 Subject: wl18xx: implement immediate Tx completion Implement immediate Tx completion for the 18xx family. Move 18xx specific Tx code to new tx.c/h files and create helper header files for definitions. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/main.c | 11 ++- drivers/net/wireless/ti/wl18xx/tx.c | 126 ++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/tx.h | 36 +++++++++ drivers/net/wireless/ti/wl18xx/wl18xx.h | 3 + drivers/net/wireless/ti/wlcore/tx.c | 4 +- drivers/net/wireless/ti/wlcore/tx.h | 1 + 7 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 drivers/net/wireless/ti/wl18xx/tx.c create mode 100644 drivers/net/wireless/ti/wl18xx/tx.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index d187037bef70..dd3b4c13fa3c 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o +wl18xx-objs = main.o tx.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 40ed53c02866..ae71131a4bba 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -33,11 +33,9 @@ #include "reg.h" #include "conf.h" +#include "tx.h" #include "wl18xx.h" -#define WL18XX_TX_HW_BLOCK_SPARE 1 -#define WL18XX_TX_HW_GEM_BLOCK_SPARE 2 -#define WL18XX_TX_HW_BLOCK_SIZE 268 static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -470,6 +468,11 @@ static u32 wl18xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, return data_len - sizeof(*desc); } +static void wl18xx_tx_immediate_completion(struct wl1271 *wl) +{ + wl18xx_tx_immediate_complete(wl); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -480,6 +483,8 @@ static struct wlcore_ops wl18xx_ops = { .set_tx_desc_data_len = wl18xx_set_tx_desc_data_len, .get_rx_buf_align = wl18xx_get_rx_buf_align, .get_rx_packet_len = wl18xx_get_rx_packet_len, + .tx_immediate_compl = wl18xx_tx_immediate_completion, + .tx_delayed_compl = NULL, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c new file mode 100644 index 000000000000..4e12f56b394f --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -0,0 +1,126 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "../wlcore/wlcore.h" +#include "../wlcore/cmd.h" +#include "../wlcore/debug.h" +#include "../wlcore/acx.h" +#include "../wlcore/tx.h" + +#include "wl18xx.h" +#include "tx.h" + +static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) +{ + struct ieee80211_tx_info *info; + struct sk_buff *skb; + int id = tx_stat_byte & WL18XX_TX_STATUS_DESC_ID_MASK; + bool tx_success; + + /* check for id legality */ + if (unlikely(id >= wl->num_tx_desc || wl->tx_frames[id] == NULL)) { + wl1271_warning("illegal id in tx completion: %d", id); + return; + } + + /* a zero bit indicates Tx success */ + tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX)); + + + skb = wl->tx_frames[id]; + info = IEEE80211_SKB_CB(skb); + + if (wl12xx_is_dummy_packet(wl, skb)) { + wl1271_free_tx_id(wl, id); + return; + } + + /* update the TX status info */ + if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + /* no real data about Tx completion */ + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + info->status.rates[0].flags = 0; + info->status.ack_signal = -1; + + if (!tx_success) + wl->stats.retry_count++; + + /* + * TODO: update sequence number for encryption? seems to be + * unsupported for now. needed for recovery with encryption. + */ + + /* remove private header from packet */ + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + + /* remove TKIP header space if present */ + if (info->control.hw_key && + info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); + skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); + } + + wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p success %d", + id, skb, tx_success); + + /* return the packet to the stack */ + skb_queue_tail(&wl->deferred_tx_queue, skb); + queue_work(wl->freezable_wq, &wl->netstack_work); + wl1271_free_tx_id(wl, id); +} + +void wl18xx_tx_immediate_complete(struct wl1271 *wl) +{ + struct wl18xx_fw_status_priv *status_priv = + (struct wl18xx_fw_status_priv *)wl->fw_status->priv; + struct wl18xx_priv *priv = wl->priv; + u8 i; + + /* nothing to do here */ + if (priv->last_fw_rls_idx == status_priv->fw_release_idx) + return; + + /* freed Tx descriptors */ + wl1271_debug(DEBUG_TX, "last released desc = %d, current idx = %d", + priv->last_fw_rls_idx, status_priv->fw_release_idx); + + if (status_priv->fw_release_idx >= WL18XX_FW_MAX_TX_STATUS_DESC) { + wl1271_error("invalid desc release index %d", + status_priv->fw_release_idx); + WARN_ON(1); + return; + } + + for (i = priv->last_fw_rls_idx; + i != status_priv->fw_release_idx; + i = (i + 1) % WL18XX_FW_MAX_TX_STATUS_DESC) { + wl18xx_tx_complete_packet(wl, + status_priv->released_tx_desc[i]); + + wl->tx_results_count++; + } + + priv->last_fw_rls_idx = status_priv->fw_release_idx; +} diff --git a/drivers/net/wireless/ti/wl18xx/tx.h b/drivers/net/wireless/ti/wl18xx/tx.h new file mode 100644 index 000000000000..a91eb10a42f2 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/tx.h @@ -0,0 +1,36 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_TX_H__ +#define __WL18XX_TX_H__ + +#include "../wlcore/wlcore.h" + +#define WL18XX_TX_HW_BLOCK_SPARE 1 +#define WL18XX_TX_HW_GEM_BLOCK_SPARE 2 +#define WL18XX_TX_HW_BLOCK_SIZE 268 + +#define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F +#define WL18XX_TX_STATUS_STAT_BIT_IDX 7 + +void wl18xx_tx_immediate_complete(struct wl1271 *wl); + +#endif /* __WL12XX_TX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 68ef5ba2f607..d24f9c0fd277 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -28,6 +28,9 @@ struct wl18xx_priv { /* buffer for sending commands to FW */ u8 cmd_buf[WL18XX_CMD_MAX_SIZE]; + + /* Index of last released Tx desc in FW */ + u8 last_fw_rls_idx; }; #define WL18XX_FW_MAX_TX_STATUS_DESC 33 diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 6893bc207994..b4e02ef22fc5 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -72,7 +72,7 @@ static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) return id; } -static void wl1271_free_tx_id(struct wl1271 *wl, int id) +void wl1271_free_tx_id(struct wl1271 *wl, int id) { if (__test_and_clear_bit(id, wl->tx_frames_map)) { if (unlikely(wl->tx_frames_cnt == wl->num_tx_desc)) @@ -82,6 +82,7 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id) wl->tx_frames_cnt--; } } +EXPORT_SYMBOL(wl1271_free_tx_id); static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, struct sk_buff *skb) @@ -127,6 +128,7 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) { return wl->dummy_packet == skb; } +EXPORT_SYMBOL(wl12xx_is_dummy_packet); u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb) diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index e440c313537d..220727421fa2 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -237,6 +237,7 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb); void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length); +void wl1271_free_tx_id(struct wl1271 *wl, int id); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); -- cgit v1.2.3 From b8422dcb865befc5d2d7c21e8427eedf32558fea Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:26 +0300 Subject: wl18xx: add hw_init operation Add wl18xx-specific HW initialization operation and create acx.[ch] files to support that. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/acx.c | 57 +++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/acx.h | 52 ++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 32 ++++++++++++++++++ drivers/net/wireless/ti/wlcore/acx.h | 2 ++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ti/wl18xx/acx.c create mode 100644 drivers/net/wireless/ti/wl18xx/acx.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index dd3b4c13fa3c..764d598bc968 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o tx.o +wl18xx-objs = main.o acx.o tx.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c new file mode 100644 index 000000000000..aed8d0dff91b --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -0,0 +1,57 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "../wlcore/cmd.h" +#include "../wlcore/debug.h" +#include "../wlcore/acx.h" + +#include "acx.h" + +int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, + u32 sdio_blk_size, u32 extra_mem_blks, + u32 len_field_size) +{ + struct wl18xx_acx_host_config_bitmap *bitmap_conf; + int ret; + + bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); + if (!bitmap_conf) { + ret = -ENOMEM; + goto out; + } + + bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); + bitmap_conf->host_sdio_block_size = cpu_to_le32(sdio_blk_size); + bitmap_conf->extra_mem_blocks = cpu_to_le32(extra_mem_blks); + bitmap_conf->length_field_size = cpu_to_le32(len_field_size); + + ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, + bitmap_conf, sizeof(*bitmap_conf)); + if (ret < 0) { + wl1271_warning("wl1271 bitmap config opt failed: %d", ret); + goto out; + } + +out: + kfree(bitmap_conf); + + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h new file mode 100644 index 000000000000..c8eebd8500c1 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -0,0 +1,52 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_ACX_H__ +#define __WL18XX_ACX_H__ + +#include "../wlcore/wlcore.h" + +/* numbers of bits the length field takes (add 1 for the actual number) */ +#define WL18XX_HOST_IF_LEN_SIZE_FIELD 15 + +struct wl18xx_acx_host_config_bitmap { + struct acx_header header; + + __le32 host_cfg_bitmap; + + __le32 host_sdio_block_size; + + /* extra mem blocks per frame in TX. */ + __le32 extra_mem_blocks; + + /* + * number of bits of the length field in the first TX word + * (up to 15 - for using the entire 16 bits). + */ + __le32 length_field_size; + +} __packed; + +int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, + u32 sdio_blk_size, u32 extra_mem_blks, + u32 len_field_size); + +#endif /* __WL12XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index ae71131a4bba..1e0719c7ccb9 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -33,6 +33,7 @@ #include "reg.h" #include "conf.h" +#include "acx.h" #include "tx.h" #include "wl18xx.h" @@ -473,6 +474,36 @@ static void wl18xx_tx_immediate_completion(struct wl1271 *wl) wl18xx_tx_immediate_complete(wl); } +static int wl18xx_hw_init(struct wl1271 *wl) +{ + int ret; + u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE | + HOST_IF_CFG_ADD_RX_ALIGNMENT; + + u32 sdio_align_size = 0; + + /* Enable Tx SDIO padding */ + if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) { + host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; + sdio_align_size = WL12XX_BUS_BLOCK_SIZE; + } + + /* Enable Rx SDIO padding */ + if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN) { + host_cfg_bitmap |= HOST_IF_CFG_RX_PAD_TO_SDIO_BLK; + sdio_align_size = WL12XX_BUS_BLOCK_SIZE; + } + + ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap, + sdio_align_size, + WL18XX_TX_HW_BLOCK_SPARE, + WL18XX_HOST_IF_LEN_SIZE_FIELD); + if (ret < 0) + return ret; + + return ret; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -485,6 +516,7 @@ static struct wlcore_ops wl18xx_ops = { .get_rx_packet_len = wl18xx_get_rx_packet_len, .tx_immediate_compl = wl18xx_tx_immediate_completion, .tx_delayed_compl = NULL, + .hw_init = wl18xx_hw_init, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index e6a74869a5ff..2d1302d23ac8 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -828,6 +828,8 @@ struct wl1271_acx_keep_alive_config { #define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0) #define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1) #define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3) +#define HOST_IF_CFG_RX_PAD_TO_SDIO_BLK BIT(4) +#define HOST_IF_CFG_ADD_RX_ALIGNMENT BIT(6) enum { WL1271_ACX_TRIG_TYPE_LEVEL = 0, -- cgit v1.2.3 From 2fc28de5989e1c40fee4e92e2a8f3bdd47b1b34a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:27 +0300 Subject: 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: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 8 ++++++++ drivers/net/wireless/ti/wl18xx/acx.c | 26 ++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/acx.h | 16 ++++++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 31 +++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/acx.h | 1 + drivers/net/wireless/ti/wlcore/hw_ops.h | 11 +++++++++++ drivers/net/wireless/ti/wlcore/tx.c | 3 ++- drivers/net/wireless/ti/wlcore/tx.h | 11 ++++++++++- drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 9 files changed, 108 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index d7dd3def07b5..8141447e8f96 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1288,6 +1288,13 @@ static void wl12xx_get_mac(struct wl1271 *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 = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, @@ -1306,6 +1313,7 @@ static struct wlcore_ops wl12xx_ops = { .sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask, .get_pg_ver = wl12xx_get_pg_ver, .get_mac = wl12xx_get_mac, + .set_tx_desc_csum = wl12xx_set_tx_desc_csum, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index aed8d0dff91b..3379db23a165 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -55,3 +55,29 @@ out: 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; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index c8eebd8500c1..544db6ef3841 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -45,8 +45,24 @@ struct wl18xx_acx_host_config_bitmap { } __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, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); +int wl18xx_acx_set_checksum_state(struct wl1271 *wl); #endif /* __WL12XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 1e0719c7ccb9..c47f52c81a72 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -21,6 +21,7 @@ #include #include +#include #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" @@ -501,9 +502,38 @@ static int wl18xx_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + ret = wl18xx_acx_set_checksum_state(wl); + if (ret != 0) + 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 = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -517,6 +547,7 @@ static struct wlcore_ops wl18xx_ops = { .tx_immediate_compl = wl18xx_tx_immediate_completion, .tx_delayed_compl = NULL, .hw_init = wl18xx_hw_init, + .set_tx_desc_csum = wl18xx_set_tx_desc_csum, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 2d1302d23ac8..ff0c5ba8e8ff 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1245,6 +1245,7 @@ enum { ACX_CONFIG_HANGOVER = 0x0042, ACX_FEATURE_CFG = 0x0043, ACX_PROTECTION_CFG = 0x0044, + ACX_CHECKSUM_CONFIG = 0x0045, }; diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 9384b4d56c24..4573249ea89f 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -119,4 +119,15 @@ static inline int wlcore_identify_fw(struct wl1271 *wl) 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 diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index b4e02ef22fc5..44e4e7bb29bd 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -270,6 +270,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (extra) { int hdrlen = ieee80211_hdrlen(frame_control); memmove(frame_start, hdr, hdrlen); + skb_set_network_header(skb, skb_network_offset(skb) + extra); } /* configure packet life time */ @@ -332,9 +333,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, ieee80211_has_protected(frame_control)) tx_attr |= TX_HW_ATTR_HOST_ENCRYPT; - desc->reserved = 0; 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); } diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 220727421fa2..e24c436bf65f 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -130,7 +130,16 @@ struct wl1271_tx_hw_descr { u8 tid; /* host link ID (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; enum wl1271_tx_hw_res_status { diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 0b3f0b586f4b..c062d304ab0d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -61,6 +61,9 @@ struct wlcore_ops { struct wl12xx_vif *wlvif); s8 (*get_pg_ver)(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 { -- cgit v1.2.3 From 169da04f523bafee46993b1efbddb913641aad56 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:28 +0300 Subject: wlcore/wl18xx: add hw op for Rx HW checksum Some chip families can checksum certain classes of Rx packets in FW. Implement the Rx-checksum feature as a HW-op. For the 18xx chip-family, set Rx-checsum according to indication from FW. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/main.c | 11 +++++++++++ drivers/net/wireless/ti/wlcore/hw_ops.h | 9 +++++++++ drivers/net/wireless/ti/wlcore/rx.c | 1 + drivers/net/wireless/ti/wlcore/wlcore.h | 4 ++++ 5 files changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 8141447e8f96..a3336534eb2a 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1314,6 +1314,7 @@ static struct wlcore_ops wl12xx_ops = { .get_pg_ver = wl12xx_get_pg_ver, .get_mac = wl12xx_get_mac, .set_tx_desc_csum = wl12xx_set_tx_desc_csum, + .set_rx_csum = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c47f52c81a72..90fccb775727 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -39,6 +39,8 @@ #include "wl18xx.h" +#define WL18XX_RX_CHECKSUM_MASK 0x40 + static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ @@ -534,6 +536,14 @@ static void wl18xx_set_tx_desc_csum(struct wl1271 *wl, desc->wl18xx_checksum_data |= (ip_hdr->protocol & 0x01); } +static void wl18xx_set_rx_csum(struct wl1271 *wl, + struct wl1271_rx_descriptor *desc, + struct sk_buff *skb) +{ + if (desc->status & WL18XX_RX_CHECKSUM_MASK) + skb->ip_summed = CHECKSUM_UNNECESSARY; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -548,6 +558,7 @@ static struct wlcore_ops wl18xx_ops = { .tx_delayed_compl = NULL, .hw_init = wl18xx_hw_init, .set_tx_desc_csum = wl18xx_set_tx_desc_csum, + .set_rx_csum = wl18xx_set_rx_csum, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 4573249ea89f..80f3d75ceddf 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -130,4 +130,13 @@ wlcore_hw_set_tx_desc_csum(struct wl1271 *wl, wl->ops->set_tx_desc_csum(wl, desc, skb); } +static inline void +wlcore_hw_set_rx_csum(struct wl1271 *wl, + struct wl1271_rx_descriptor *desc, + struct sk_buff *skb) +{ + if (wl->ops->set_rx_csum) + wl->ops->set_rx_csum(wl, desc, skb); +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index d6a3c6b07827..2672b1d16cef 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -186,6 +186,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, is_data = 1; wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); + wlcore_hw_set_rx_csum(wl, desc, skb); seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb, diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index c062d304ab0d..2922d8aec699 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -33,6 +33,7 @@ /* forward declaration */ struct wl1271_tx_hw_descr; enum wl_rx_buf_align; +struct wl1271_rx_descriptor; struct wlcore_ops { int (*identify_chip)(struct wl1271 *wl); @@ -64,6 +65,9 @@ struct wlcore_ops { void (*set_tx_desc_csum)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb); + void (*set_rx_csum)(struct wl1271 *wl, + struct wl1271_rx_descriptor *desc, + struct sk_buff *skb); }; enum wlcore_partitions { -- cgit v1.2.3 From 23ee9bf8c3cb57768bba31a8cc62b87d39ca4e56 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:29 +0300 Subject: wl18xx: add runtime configuration parameters Now wlcore requires the lower drivers to set the correct configuration. Move the existing private configuration to the proper place and add all generic configuration parameters. The important changes are in Tx interrupt pacing and Rx BA window size. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/conf.h | 4 +- drivers/net/wireless/ti/wl18xx/main.c | 381 +++++++++++++++++++++++++++++--- drivers/net/wireless/ti/wl18xx/wl18xx.h | 3 + 3 files changed, 360 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index 7cd7bf16879e..d2b097e4d24e 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -48,9 +48,7 @@ struct wl18xx_conf_phy { u8 secondary_clock_setting_time; }; -struct wl18xx_conf { - /* TODO: move the wlcore conf here? */ - +struct wl18xx_priv_conf { struct wl18xx_conf_phy phy; }; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 90fccb775727..fd7a803ff98c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -153,7 +153,324 @@ enum wl18xx_hw_rates { WL18XX_CONF_HW_RXTX_RATE_MAX, }; -static struct wl18xx_conf wl18xx_default_conf = { +static struct wlcore_conf wl18xx_conf = { + .sg = { + .params = { + [CONF_SG_ACL_BT_MASTER_MIN_BR] = 10, + [CONF_SG_ACL_BT_MASTER_MAX_BR] = 180, + [CONF_SG_ACL_BT_SLAVE_MIN_BR] = 10, + [CONF_SG_ACL_BT_SLAVE_MAX_BR] = 180, + [CONF_SG_ACL_BT_MASTER_MIN_EDR] = 10, + [CONF_SG_ACL_BT_MASTER_MAX_EDR] = 80, + [CONF_SG_ACL_BT_SLAVE_MIN_EDR] = 10, + [CONF_SG_ACL_BT_SLAVE_MAX_EDR] = 80, + [CONF_SG_ACL_WLAN_PS_MASTER_BR] = 8, + [CONF_SG_ACL_WLAN_PS_SLAVE_BR] = 8, + [CONF_SG_ACL_WLAN_PS_MASTER_EDR] = 20, + [CONF_SG_ACL_WLAN_PS_SLAVE_EDR] = 20, + [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_BR] = 20, + [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_BR] = 35, + [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_BR] = 16, + [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_BR] = 35, + [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MIN_EDR] = 32, + [CONF_SG_ACL_WLAN_ACTIVE_MASTER_MAX_EDR] = 50, + [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MIN_EDR] = 28, + [CONF_SG_ACL_WLAN_ACTIVE_SLAVE_MAX_EDR] = 50, + [CONF_SG_ACL_ACTIVE_SCAN_WLAN_BR] = 10, + [CONF_SG_ACL_ACTIVE_SCAN_WLAN_EDR] = 20, + [CONF_SG_ACL_PASSIVE_SCAN_BT_BR] = 75, + [CONF_SG_ACL_PASSIVE_SCAN_WLAN_BR] = 15, + [CONF_SG_ACL_PASSIVE_SCAN_BT_EDR] = 27, + [CONF_SG_ACL_PASSIVE_SCAN_WLAN_EDR] = 17, + /* active scan params */ + [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, + /* passive scan params */ + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_BR] = 800, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP_EDR] = 200, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + /* passive scan in dual antenna params */ + [CONF_SG_CONSECUTIVE_HV3_IN_PASSIVE_SCAN] = 0, + [CONF_SG_BCN_HV3_COLLISION_THRESH_IN_PASSIVE_SCAN] = 0, + [CONF_SG_TX_RX_PROTECTION_BWIDTH_IN_PASSIVE_SCAN] = 0, + /* general params */ + [CONF_SG_STA_FORCE_PS_IN_BT_SCO] = 1, + [CONF_SG_ANTENNA_CONFIGURATION] = 0, + [CONF_SG_BEACON_MISS_PERCENT] = 60, + [CONF_SG_DHCP_TIME] = 5000, + [CONF_SG_RXT] = 1200, + [CONF_SG_TXT] = 1000, + [CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [CONF_SG_GENERAL_USAGE_BIT_MAP] = 3, + [CONF_SG_HV3_MAX_SERVED] = 6, + [CONF_SG_PS_POLL_TIMEOUT] = 10, + [CONF_SG_UPSD_TIMEOUT] = 10, + [CONF_SG_CONSECUTIVE_CTS_THRESHOLD] = 2, + [CONF_SG_STA_RX_WINDOW_AFTER_DTIM] = 5, + [CONF_SG_STA_CONNECTION_PROTECTION_TIME] = 30, + /* AP params */ + [CONF_AP_BEACON_MISS_TX] = 3, + [CONF_AP_RX_WINDOW_AFTER_BEACON] = 10, + [CONF_AP_BEACON_WINDOW_INTERVAL] = 2, + [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, + [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, + [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, + /* CTS Diluting params */ + [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, + [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, + }, + .state = CONF_SG_PROTECTIVE, + }, + .rx = { + .rx_msdu_life_time = 512000, + .packet_detection_threshold = 0, + .ps_poll_timeout = 15, + .upsd_timeout = 15, + .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, + .rx_cca_threshold = 0, + .irq_blk_threshold = 0xFFFF, + .irq_pkt_threshold = 0, + .irq_timeout = 600, + .queue_type = CONF_RX_QUEUE_TYPE_LOW_PRIORITY, + }, + .tx = { + .tx_energy_detection = 0, + .sta_rc_conf = { + .enabled_rates = 0, + .short_retry_limit = 10, + .long_retry_limit = 10, + .aflags = 0, + }, + .ac_conf_count = 4, + .ac_conf = { + [CONF_TX_AC_BE] = { + .ac = CONF_TX_AC_BE, + .cw_min = 15, + .cw_max = 63, + .aifsn = 3, + .tx_op_limit = 0, + }, + [CONF_TX_AC_BK] = { + .ac = CONF_TX_AC_BK, + .cw_min = 15, + .cw_max = 63, + .aifsn = 7, + .tx_op_limit = 0, + }, + [CONF_TX_AC_VI] = { + .ac = CONF_TX_AC_VI, + .cw_min = 15, + .cw_max = 63, + .aifsn = CONF_TX_AIFS_PIFS, + .tx_op_limit = 3008, + }, + [CONF_TX_AC_VO] = { + .ac = CONF_TX_AC_VO, + .cw_min = 15, + .cw_max = 63, + .aifsn = CONF_TX_AIFS_PIFS, + .tx_op_limit = 1504, + }, + }, + .max_tx_retries = 100, + .ap_aging_period = 300, + .tid_conf_count = 4, + .tid_conf = { + [CONF_TX_AC_BE] = { + .queue_id = CONF_TX_AC_BE, + .channel_type = CONF_CHANNEL_TYPE_EDCF, + .tsid = CONF_TX_AC_BE, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [CONF_TX_AC_BK] = { + .queue_id = CONF_TX_AC_BK, + .channel_type = CONF_CHANNEL_TYPE_EDCF, + .tsid = CONF_TX_AC_BK, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [CONF_TX_AC_VI] = { + .queue_id = CONF_TX_AC_VI, + .channel_type = CONF_CHANNEL_TYPE_EDCF, + .tsid = CONF_TX_AC_VI, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + [CONF_TX_AC_VO] = { + .queue_id = CONF_TX_AC_VO, + .channel_type = CONF_CHANNEL_TYPE_EDCF, + .tsid = CONF_TX_AC_VO, + .ps_scheme = CONF_PS_SCHEME_LEGACY, + .ack_policy = CONF_ACK_POLICY_LEGACY, + .apsd_conf = {0, 0}, + }, + }, + .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, + .tx_compl_timeout = 350, + .tx_compl_threshold = 10, + .basic_rate = CONF_HW_BIT_RATE_1MBPS, + .basic_rate_5 = CONF_HW_BIT_RATE_6MBPS, + .tmpl_short_retry_limit = 10, + .tmpl_long_retry_limit = 10, + .tx_watchdog_timeout = 5000, + }, + .conn = { + .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, + .listen_interval = 1, + .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, + .suspend_listen_interval = 3, + .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, + .bcn_filt_ie_count = 2, + .bcn_filt_ie = { + [0] = { + .ie = WLAN_EID_CHANNEL_SWITCH, + .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, + }, + [1] = { + .ie = WLAN_EID_HT_OPERATION, + .rule = CONF_BCN_RULE_PASS_ON_CHANGE, + }, + }, + .synch_fail_thold = 10, + .bss_lose_timeout = 100, + .beacon_rx_timeout = 10000, + .broadcast_timeout = 20000, + .rx_broadcast_in_ps = 1, + .ps_poll_threshold = 10, + .bet_enable = CONF_BET_MODE_ENABLE, + .bet_max_consecutive = 50, + .psm_entry_retries = 8, + .psm_exit_retries = 16, + .psm_entry_nullfunc_retries = 3, + .dynamic_ps_timeout = 40, + .forced_ps = false, + .keep_alive_interval = 55000, + .max_listen_interval = 20, + }, + .itrim = { + .enable = false, + .timeout = 50000, + }, + .pm_config = { + .host_clk_settling_time = 5000, + .host_fast_wakeup_support = false + }, + .roam_trigger = { + .trigger_pacing = 1, + .avg_weight_rssi_beacon = 20, + .avg_weight_rssi_data = 10, + .avg_weight_snr_beacon = 20, + .avg_weight_snr_data = 10, + }, + .scan = { + .min_dwell_time_active = 7500, + .max_dwell_time_active = 30000, + .min_dwell_time_passive = 100000, + .max_dwell_time_passive = 100000, + .num_probe_reqs = 2, + .split_scan_timeout = 50000, + }, + .sched_scan = { + /* + * Values are in TU/1000 but since sched scan FW command + * params are in TUs rounding up may occur. + */ + .base_dwell_time = 7500, + .max_dwell_time_delta = 22500, + /* based on 250bits per probe @1Mbps */ + .dwell_time_delta_per_probe = 2000, + /* based on 250bits per probe @6Mbps (plus a bit more) */ + .dwell_time_delta_per_probe_5 = 350, + .dwell_time_passive = 100000, + .dwell_time_dfs = 150000, + .num_probe_reqs = 2, + .rssi_threshold = -90, + .snr_threshold = 0, + }, + .ht = { + .rx_ba_win_size = 10, + .tx_ba_win_size = 10, + .inactivity_timeout = 10000, + .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP, + }, + .mem = { + .num_stations = 1, + .ssid_profiles = 1, + .rx_block_num = 40, + .tx_min_block_num = 40, + .dynamic_memory = 1, + .min_req_tx_blocks = 45, + .min_req_rx_blocks = 22, + .tx_min = 27, + }, + .fm_coex = { + .enable = true, + .swallow_period = 5, + .n_divider_fref_set_1 = 0xff, /* default */ + .n_divider_fref_set_2 = 12, + .m_divider_fref_set_1 = 148, + .m_divider_fref_set_2 = 0xffff, /* default */ + .coex_pll_stabilization_time = 0xffffffff, /* default */ + .ldo_stabilization_time = 0xffff, /* default */ + .fm_disturbed_band_margin = 0xff, /* default */ + .swallow_clk_diff = 0xff, /* default */ + }, + .rx_streaming = { + .duration = 150, + .queues = 0x1, + .interval = 20, + .always = 0, + }, + .fwlog = { + .mode = WL12XX_FWLOG_ON_DEMAND, + .mem_blocks = 2, + .severity = 0, + .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, + .output = WL12XX_FWLOG_OUTPUT_HOST, + .threshold = 0, + }, + .rate = { + .rate_retry_score = 32000, + .per_add = 8192, + .per_th1 = 2048, + .per_th2 = 4096, + .max_per = 8100, + .inverse_curiosity_factor = 5, + .tx_fail_low_th = 4, + .tx_fail_high_th = 10, + .per_alpha_shift = 4, + .per_add_shift = 13, + .per_beta1_shift = 10, + .per_beta2_shift = 8, + .rate_check_up = 2, + .rate_check_down = 12, + .rate_retry_policy = { + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + }, + }, + .hangover = { + .recover_time = 0, + .hangover_period = 20, + .dynamic_mode = 1, + .early_termination_mode = 1, + .max_period = 20, + .min_period = 1, + .increase_delta = 1, + .decrease_delta = 2, + .quiet_time = 4, + .increase_time = 1, + .window_size = 16, + }, +}; + +static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .phy = { .phy_standalone = 0x00, .primary_clock_setting_time = 0x05, @@ -323,42 +640,43 @@ static void wl18xx_pre_upload(struct wl1271 *wl) static void wl18xx_set_mac_and_phy(struct wl1271 *wl) { + struct wl18xx_priv *priv = wl->priv; + struct wl18xx_conf_phy *phy = &priv->conf.phy; struct wl18xx_mac_and_phy_params params; memset(¶ms, 0, sizeof(params)); - params.phy_standalone = wl18xx_default_conf.phy.phy_standalone; - params.rdl = wl18xx_default_conf.phy.rdl; - params.enable_clpc = wl18xx_default_conf.phy.enable_clpc; + params.phy_standalone = phy->phy_standalone; + params.rdl = phy->rdl; + params.enable_clpc = phy->enable_clpc; params.enable_tx_low_pwr_on_siso_rdl = - wl18xx_default_conf.phy.enable_tx_low_pwr_on_siso_rdl; - params.auto_detect = wl18xx_default_conf.phy.auto_detect; - params.dedicated_fem = wl18xx_default_conf.phy.dedicated_fem; - params.low_band_component = wl18xx_default_conf.phy.low_band_component; + phy->enable_tx_low_pwr_on_siso_rdl; + params.auto_detect = phy->auto_detect; + params.dedicated_fem = phy->dedicated_fem; + params.low_band_component = phy->low_band_component; params.low_band_component_type = - wl18xx_default_conf.phy.low_band_component_type; - params.high_band_component = - wl18xx_default_conf.phy.high_band_component; + phy->low_band_component_type; + params.high_band_component = phy->high_band_component; params.high_band_component_type = - wl18xx_default_conf.phy.high_band_component_type; + phy->high_band_component_type; params.number_of_assembled_ant2_4 = - wl18xx_default_conf.phy.number_of_assembled_ant2_4; + phy->number_of_assembled_ant2_4; params.number_of_assembled_ant5 = - wl18xx_default_conf.phy.number_of_assembled_ant5; - params.external_pa_dc2dc = wl18xx_default_conf.phy.external_pa_dc2dc; - params.tcxo_ldo_voltage = wl18xx_default_conf.phy.tcxo_ldo_voltage; - params.xtal_itrim_val = wl18xx_default_conf.phy.xtal_itrim_val; - params.srf_state = wl18xx_default_conf.phy.srf_state; - params.io_configuration = wl18xx_default_conf.phy.io_configuration; - params.sdio_configuration = wl18xx_default_conf.phy.sdio_configuration; - params.settings = wl18xx_default_conf.phy.settings; - params.rx_profile = wl18xx_default_conf.phy.rx_profile; + phy->number_of_assembled_ant5; + params.external_pa_dc2dc = phy->external_pa_dc2dc; + params.tcxo_ldo_voltage = phy->tcxo_ldo_voltage; + params.xtal_itrim_val = phy->xtal_itrim_val; + params.srf_state = phy->srf_state; + params.io_configuration = phy->io_configuration; + params.sdio_configuration = phy->sdio_configuration; + params.settings = phy->settings; + params.rx_profile = phy->rx_profile; params.primary_clock_setting_time = - wl18xx_default_conf.phy.primary_clock_setting_time; + phy->primary_clock_setting_time; params.clock_valid_on_wake_up = - wl18xx_default_conf.phy.clock_valid_on_wake_up; + phy->clock_valid_on_wake_up; params.secondary_clock_setting_time = - wl18xx_default_conf.phy.secondary_clock_setting_time; + phy->secondary_clock_setting_time; /* TODO: hardcoded for now */ params.board_type = BOARD_TYPE_DVP_EVB_18XX; @@ -544,6 +862,17 @@ static void wl18xx_set_rx_csum(struct wl1271 *wl, skb->ip_summed = CHECKSUM_UNNECESSARY; } +static void wl18xx_conf_init(struct wl1271 *wl) +{ + struct wl18xx_priv *priv = wl->priv; + + /* apply driver default configuration */ + memcpy(&wl->conf, &wl18xx_conf, sizeof(wl18xx_conf)); + + /* apply default private configuration */ + memcpy(&priv->conf, &wl18xx_default_priv_conf, sizeof(priv->conf)); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -584,6 +913,8 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); + wl18xx_conf_init(wl); + return wlcore_probe(wl, pdev); } diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index d24f9c0fd277..c9bf5c6559f8 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -22,6 +22,7 @@ #ifndef __WL18XX_PRIV_H__ #define __WL18XX_PRIV_H__ +#include "conf.h" #define WL18XX_CMD_MAX_SIZE 740 @@ -29,6 +30,8 @@ struct wl18xx_priv { /* buffer for sending commands to FW */ u8 cmd_buf[WL18XX_CMD_MAX_SIZE]; + struct wl18xx_priv_conf conf; + /* Index of last released Tx desc in FW */ u8 last_fw_rls_idx; }; -- cgit v1.2.3 From 83d08d3ff385c810a341a5c1b97a8c81df96673d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:30 +0300 Subject: wlcore: track current channel type per vif Track the current 802.11 channel type, defaulting to a NO_HT channel. Update the channel type element when changed by mac80211. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 9 ++++++++- drivers/net/wireless/ti/wlcore/wl12xx.h | 1 + drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index acef93390d3d..6f0361b23611 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1767,6 +1767,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; + wl->channel_type = NL80211_CHAN_NO_HT; wl->tx_blocks_available = 0; wl->tx_allocated_blocks = 0; wl->tx_results_count = 0; @@ -1919,6 +1920,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wlvif->band = wl->band; wlvif->channel = wl->channel; wlvif->power_level = wl->power_level; + wlvif->channel_type = wl->channel_type; INIT_WORK(&wlvif->rx_streaming_enable_work, wl1271_rx_streaming_enable_work); @@ -2469,11 +2471,13 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, /* if the channel changes while joined, join again */ if (changed & IEEE80211_CONF_CHANGE_CHANNEL && ((wlvif->band != conf->channel->band) || - (wlvif->channel != channel))) { + (wlvif->channel != channel) || + (wlvif->channel_type != conf->channel_type))) { /* send all pending packets */ wl1271_tx_work_locked(wl); wlvif->band = conf->channel->band; wlvif->channel = channel; + wlvif->channel_type = conf->channel_type; if (!is_ap) { /* @@ -2593,6 +2597,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { wl->band = conf->channel->band; wl->channel = channel; + wl->channel_type = conf->channel_type; } if (changed & IEEE80211_CONF_CHANGE_POWER) @@ -3704,6 +3709,7 @@ sta_not_found: u32 rates; int ieoffset; wlvif->aid = bss_conf->aid; + wlvif->channel_type = bss_conf->channel_type; wlvif->beacon_int = bss_conf->beacon_int; do_join = true; set_assoc = true; @@ -5117,6 +5123,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->band = IEEE80211_BAND_2GHZ; + wl->channel_type = NL80211_CHAN_NO_HT; wl->flags = 0; wl->sg_enabled = true; wl->hw_pg_ver = -1; diff --git a/drivers/net/wireless/ti/wlcore/wl12xx.h b/drivers/net/wireless/ti/wlcore/wl12xx.h index f12bdf745180..05e6f74e4d25 100644 --- a/drivers/net/wireless/ti/wlcore/wl12xx.h +++ b/drivers/net/wireless/ti/wlcore/wl12xx.h @@ -367,6 +367,7 @@ struct wl12xx_vif { /* The current band */ enum ieee80211_band band; int channel; + enum nl80211_channel_type channel_type; u32 bitrate_masks[IEEE80211_NUM_BANDS]; u32 basic_rate_set; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 2922d8aec699..83c43c9b10ed 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -362,6 +362,9 @@ struct wl1271 { /* RX Data filter rule state - enabled/disabled */ bool rx_filter_enabled[WL1271_MAX_RX_FILTERS]; + + /* the current channel type */ + enum nl80211_channel_type channel_type; }; int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); -- cgit v1.2.3 From a6298dbe95b5121dfc74102e7a113001859a1335 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:31 +0300 Subject: wl18xx: send channel type to FW on role start Translate the NL80211 channel type to a FW-specific channel type and send it to the FW as part of the role-start command. For wl12xx this has no effect - this element is treated as padding. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 19 +++++++++++++++++++ drivers/net/wireless/ti/wlcore/cmd.h | 11 ++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 5b128a971449..8450f0eecc9b 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -291,6 +291,23 @@ static int wl12xx_get_new_session_id(struct wl1271 *wl, return wlvif->session_counter; } +static u8 wlcore_get_native_channel_type(u8 nl_channel_type) +{ + switch (nl_channel_type) { + case NL80211_CHAN_NO_HT: + return WLCORE_CHAN_NO_HT; + case NL80211_CHAN_HT20: + return WLCORE_CHAN_HT20; + case NL80211_CHAN_HT40MINUS: + return WLCORE_CHAN_HT40MINUS; + case NL80211_CHAN_HT40PLUS: + return WLCORE_CHAN_HT40PLUS; + default: + WARN_ON(1); + return WLCORE_CHAN_NO_HT; + } +} + static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) { @@ -407,6 +424,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif) memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len); memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN); cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set); + cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) { ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid); @@ -519,6 +537,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) /* FIXME: Change when adding DFS */ cmd->ap.reset_tsf = 1; /* By default reset AP TSF */ cmd->channel = wlvif->channel; + cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type); if (!bss_conf->hidden_ssid) { /* take the SSID from the beacon for backward compatibility */ diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index a46ae07cb77e..2aafe3df0fb0 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -266,13 +266,22 @@ enum wlcore_band { WLCORE_BAND_MAX_RADIO = 0x7F, }; +enum wlcore_channel_type { + WLCORE_CHAN_NO_HT, + WLCORE_CHAN_HT20, + WLCORE_CHAN_HT40MINUS, + WLCORE_CHAN_HT40PLUS +}; + struct wl12xx_cmd_role_start { struct wl1271_cmd_header header; u8 role_id; u8 band; u8 channel; - u8 padding; + + /* enum wlcore_channel_type */ + u8 channel_type; union { struct { -- cgit v1.2.3 From f13af3484f04f61794d90dbdab28df8bf8b9d667 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:32 +0300 Subject: wl18xx: ipmlement ap_rate_mask hw op Enable wide-chan or MIMO rates when appropriate. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 18 ++++++++++++++++++ drivers/net/wireless/ti/wl18xx/tx.h | 6 ++++++ drivers/net/wireless/ti/wlcore/conf.h | 6 ++++++ 3 files changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index fd7a803ff98c..77840ddd3129 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -862,6 +862,23 @@ static void wl18xx_set_rx_csum(struct wl1271 *wl, skb->ip_summed = CHECKSUM_UNNECESSARY; } +static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + u32 hw_rate_set = wlvif->rate_set; + + if (wlvif->channel_type == NL80211_CHAN_HT40MINUS || + wlvif->channel_type == NL80211_CHAN_HT40PLUS) { + wl1271_debug(DEBUG_ACX, "using wide channel rate mask"); + hw_rate_set |= CONF_TX_RATE_USE_WIDE_CHAN; + + /* we don't support MIMO in wide-channel mode */ + hw_rate_set &= ~CONF_TX_MIMO_RATES; + } + + return hw_rate_set; +} + static void wl18xx_conf_init(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; @@ -888,6 +905,7 @@ static struct wlcore_ops wl18xx_ops = { .hw_init = wl18xx_hw_init, .set_tx_desc_csum = wl18xx_set_tx_desc_csum, .set_rx_csum = wl18xx_set_rx_csum, + .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wl18xx/tx.h b/drivers/net/wireless/ti/wl18xx/tx.h index a91eb10a42f2..2417262de207 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.h +++ b/drivers/net/wireless/ti/wl18xx/tx.h @@ -31,6 +31,12 @@ #define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F #define WL18XX_TX_STATUS_STAT_BIT_IDX 7 +/* + * The FW uses a special bit to indicate a wide channel should be used in + * the rate policy. + */ +#define CONF_TX_RATE_USE_WIDE_CHAN BIT(31) + void wl18xx_tx_immediate_complete(struct wl1271 *wl); #endif /* __WL12XX_TX_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 0798c1e88814..14dd361fa828 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -443,6 +443,12 @@ struct conf_rx_settings { CONF_HW_BIT_RATE_MCS_5 | CONF_HW_BIT_RATE_MCS_6 | \ CONF_HW_BIT_RATE_MCS_7) +#define CONF_TX_MIMO_RATES (CONF_HW_BIT_RATE_MCS_8 | \ + CONF_HW_BIT_RATE_MCS_9 | CONF_HW_BIT_RATE_MCS_10 | \ + CONF_HW_BIT_RATE_MCS_11 | CONF_HW_BIT_RATE_MCS_12 | \ + CONF_HW_BIT_RATE_MCS_13 | CONF_HW_BIT_RATE_MCS_14 | \ + CONF_HW_BIT_RATE_MCS_15) + /* * Default rates for management traffic when operating in AP mode. This * should be configured according to the basic rate set of the AP -- cgit v1.2.3 From b3a47ee0ec07b16f68c38052e8cd1b5cb417bdca Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:33 +0300 Subject: wlcore: support peer MIMO rates Parse the peer ht_cap element containing MCS8-MCS15 rates and pass it to the FW. Rates unsupported by the HW will be sanitized by mac80211 before reaching us. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 4 +++- drivers/net/wireless/ti/wlcore/main.c | 3 ++- drivers/net/wireless/ti/wlcore/tx.c | 4 ++-- drivers/net/wireless/ti/wlcore/wl12xx.h | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 8450f0eecc9b..0dec46586ad5 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1371,7 +1371,9 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, sta_rates = sta->supp_rates[wlvif->band]; if (sta->ht_cap.ht_supported) - sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET; + sta_rates |= + (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | + (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET); cmd->supported_rates = cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6f0361b23611..47dfc9d1295c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3696,7 +3696,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band]; if (sta->ht_cap.ht_supported) sta_rate_set |= - (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); + (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) | + (sta->ht_cap.mcs.rx_mask[1] << HW_MIMO_RATES_OFFSET); sta_ht_cap = sta->ht_cap; sta_exists = true; diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 44e4e7bb29bd..200d091db6f9 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -428,10 +428,10 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, rate_set >>= 1; } - /* MCS rates indication are on bits 16 - 23 */ + /* MCS rates indication are on bits 16 - 31 */ rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates; - for (bit = 0; bit < 8; bit++) { + for (bit = 0; bit < 16; bit++) { if (rate_set & 0x1) enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit); rate_set >>= 1; diff --git a/drivers/net/wireless/ti/wlcore/wl12xx.h b/drivers/net/wireless/ti/wlcore/wl12xx.h index 05e6f74e4d25..571a4ef34a32 100644 --- a/drivers/net/wireless/ti/wlcore/wl12xx.h +++ b/drivers/net/wireless/ti/wlcore/wl12xx.h @@ -502,6 +502,7 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, /* Macros to handle wl1271.sta_rate_set */ #define HW_BG_RATES_MASK 0xffff #define HW_HT_RATES_OFFSET 16 +#define HW_MIMO_RATES_OFFSET 24 #define WL12XX_HW_BLOCK_SIZE 256 -- cgit v1.2.3 From ebc7e57ddd01ffa4c996dde7095746259693755d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:34 +0300 Subject: wlcore/wl18xx: enable MIMO/wide-chan rates in AP-mode rate config Add a HW op to add extra enabled rates for AP-mode data-rates. Since the rates might depend on channel properties, reconfigure AP-mode rates when these change. Implement the HW op for the 18xx family, where MIMO or wide-chan rates can be added. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/main.c | 14 ++++++++++++++ drivers/net/wireless/ti/wlcore/hw_ops.h | 10 ++++++++++ drivers/net/wireless/ti/wlcore/init.c | 3 +++ drivers/net/wireless/ti/wlcore/main.c | 7 ++++++- drivers/net/wireless/ti/wlcore/wlcore.h | 2 ++ 6 files changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index a3336534eb2a..ad760a329c6a 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1315,6 +1315,7 @@ static struct wlcore_ops wl12xx_ops = { .get_mac = wl12xx_get_mac, .set_tx_desc_csum = wl12xx_set_tx_desc_csum, .set_rx_csum = NULL, + .ap_get_mimo_wide_rate_mask = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 77840ddd3129..9da94e62b7ac 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -879,6 +879,19 @@ static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl, return hw_rate_set; } +static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + if (wlvif->channel_type == NL80211_CHAN_HT40MINUS || + wlvif->channel_type == NL80211_CHAN_HT40PLUS) { + wl1271_debug(DEBUG_ACX, "using wide channel rate mask"); + return CONF_TX_RATE_USE_WIDE_CHAN; + } else { + wl1271_debug(DEBUG_ACX, "using MIMO rate mask"); + return CONF_TX_MIMO_RATES; + } +} + static void wl18xx_conf_init(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; @@ -906,6 +919,7 @@ static struct wlcore_ops wl18xx_ops = { .set_tx_desc_csum = wl18xx_set_tx_desc_csum, .set_rx_csum = wl18xx_set_rx_csum, .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, + .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, }; int __devinit wl18xx_probe(struct platform_device *pdev) diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 80f3d75ceddf..6d3e378bd0c7 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -139,4 +139,14 @@ wlcore_hw_set_rx_csum(struct wl1271 *wl, wl->ops->set_rx_csum(wl, desc, skb); } +static inline u32 +wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + if (wl->ops->ap_get_mimo_wide_rate_mask) + return wl->ops->ap_get_mimo_wide_rate_mask(wl, wlvif); + + return 0; +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 9f89255eb6e6..645abd4b660d 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -460,6 +460,9 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) /* unconditionally enable HT rates */ supported_rates |= CONF_TX_MCS_RATES; + /* get extra MIMO or wide-chan rates where the HW supports it */ + supported_rates |= wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); + /* configure unicast TX rate classes */ for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { rc.enabled_rates = supported_rates; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 47dfc9d1295c..03d4a4b1f59c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2479,7 +2479,12 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, wlvif->channel = channel; wlvif->channel_type = conf->channel_type; - if (!is_ap) { + if (is_ap) { + ret = wl1271_init_ap_rates(wl, wlvif); + if (ret < 0) + wl1271_error("AP rate policy change failed %d", + ret); + } else { /* * FIXME: the mac80211 should really provide a fixed * rate to use here. for now, just use the smallest diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 83c43c9b10ed..f9ec84da3f79 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -68,6 +68,8 @@ struct wlcore_ops { void (*set_rx_csum)(struct wl1271 *wl, struct wl1271_rx_descriptor *desc, struct sk_buff *skb); + u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl, + struct wl12xx_vif *wlvif); }; enum wlcore_partitions { -- cgit v1.2.3 From 0e0f5a3b5c6f1b0e435a315a9cbe8a8de0c93630 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:35 +0300 Subject: wl18xx: set HT capabilities Define the default HT capabilities of the 18xx chip family - these include support for wide-channel. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 9da94e62b7ac..a0bec8f6c4ed 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -922,6 +922,20 @@ static struct wlcore_ops wl18xx_ops = { .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, }; +/* HT cap appropriate for wide channels */ +static struct ieee80211_sta_ht_cap wl18xx_ht_cap = { + .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(150), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, +}; + int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -945,6 +959,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); + memcpy(&wl->ht_cap, &wl18xx_ht_cap, sizeof(wl18xx_ht_cap)); wl18xx_conf_init(wl); return wlcore_probe(wl, pdev); -- cgit v1.2.3 From 3a8ddb61764a4ee0f6d2eb1fc650f41c4057ecc2 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:36 +0300 Subject: wl18xx: add module param for overriding HT caps Set an alternate HT cap allowing MIMO rates (but only 20mhz) channels, when the module is loaded with ht_mode=mimo. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a0bec8f6c4ed..17792e2a9849 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -41,6 +41,8 @@ #define WL18XX_RX_CHECKSUM_MASK 0x40 +static char *ht_mode_param; + static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ @@ -936,6 +938,19 @@ static struct ieee80211_sta_ht_cap wl18xx_ht_cap = { }, }; +/* HT cap appropriate for MIMO rates in 20mhz channel */ +static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap = { + .cap = IEEE80211_HT_CAP_SGI_20, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(144), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, +}; + int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -960,6 +975,10 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); memcpy(&wl->ht_cap, &wl18xx_ht_cap, sizeof(wl18xx_ht_cap)); + if (ht_mode_param && !strcmp(ht_mode_param, "mimo")) + memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, + sizeof(wl18xx_mimo_ht_cap)); + wl18xx_conf_init(wl); return wlcore_probe(wl, pdev); @@ -993,6 +1012,9 @@ static void __exit wl18xx_exit(void) } module_exit(wl18xx_exit); +module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); +MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or mimo"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From a9c130d522ddef8673728fa9738f4f3f8e61f9b9 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:37 +0300 Subject: wl18xx: add board type module argument Different board types (ie. FPGA, HDK and DVP/EVB) require slightly different init configuration options. Since we cannot probe the type of board from the actual hardware, we need to pass it as an option during module load. This patch adds a module parameters that accepts the 3 different board types, with DVP/EVB as the default, and uses this value where needed. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 26 ++++++++++++++++++++++++-- drivers/net/wireless/ti/wl18xx/wl18xx.h | 2 ++ 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 17792e2a9849..e5ebf4a14ba1 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -42,6 +42,7 @@ #define WL18XX_RX_CHECKSUM_MASK 0x40 static char *ht_mode_param; +static char *board_type_param; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -680,8 +681,7 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) params.secondary_clock_setting_time = phy->secondary_clock_setting_time; - /* TODO: hardcoded for now */ - params.board_type = BOARD_TYPE_DVP_EVB_18XX; + params.board_type = priv->board_type; wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)¶ms, @@ -964,6 +964,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) } wl = hw->priv; + priv = wl->priv; wl->ops = &wl18xx_ops; wl->ptable = wl18xx_ptable; wl->rtable = wl18xx_rtable; @@ -979,6 +980,24 @@ int __devinit wl18xx_probe(struct platform_device *pdev) memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, sizeof(wl18xx_mimo_ht_cap)); + if (!board_type_param) { + board_type_param = kstrdup("dvp_evb", GFP_KERNEL); + priv->board_type = BOARD_TYPE_DVP_EVB_18XX; + } else { + if (!strcmp(board_type_param, "fpga")) + priv->board_type = BOARD_TYPE_FPGA_18XX; + else if (!strcmp(board_type_param, "hdk")) + priv->board_type = BOARD_TYPE_HDK_18XX; + else if (!strcmp(board_type_param, "dvp_evb")) + priv->board_type = BOARD_TYPE_DVP_EVB_18XX; + else { + wl1271_error("invalid board type '%s'", + board_type_param); + wlcore_free_hw(wl); + return -EINVAL; + } + } + wl18xx_conf_init(wl); return wlcore_probe(wl, pdev); @@ -1015,6 +1034,9 @@ module_exit(wl18xx_exit); module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or mimo"); +module_param_named(board_type, board_type_param, charp, S_IRUSR); +MODULE_PARM_DESC(board_type, "Board type: fpga, hdk or dvp_evb (default)"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index c9bf5c6559f8..5bcc7f09f8ca 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -34,6 +34,8 @@ struct wl18xx_priv { /* Index of last released Tx desc in FW */ u8 last_fw_rls_idx; + + u8 board_type; }; #define WL18XX_FW_MAX_TX_STATUS_DESC 33 -- cgit v1.2.3 From d5b592764f5b21979f6260869bd96e69aa0536bb Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:38 +0300 Subject: wl18xx: translate and write the board type to SCR_PAD2 The firmware uses the SCR_PAD2 register to read the board type passed from the driver. The values don't match the ones used in the mac and phy configuration, so we need to map them before writing. This commit adds a translation table that is used when writing the board type to SCR_PAD2. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 16 +++++++++++----- drivers/net/wireless/ti/wl18xx/reg.h | 9 +++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index e5ebf4a14ba1..5214334c72c2 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -44,6 +44,12 @@ static char *ht_mode_param; static char *board_type_param; +static const u32 wl18xx_board_type_to_scrpad2[NUM_BOARD_TYPES] = { + [BOARD_TYPE_FPGA_18XX] = SCR_PAD2_BOARD_TYPE_FPGA, + [BOARD_TYPE_HDK_18XX] = SCR_PAD2_BOARD_TYPE_HDK, + [BOARD_TYPE_DVP_EVB_18XX] = SCR_PAD2_BOARD_TYPE_DVP_EVB, +}; + static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ @@ -584,11 +590,11 @@ out: static void wl18xx_set_clk(struct wl1271 *wl) { - /* - * TODO: this is hardcoded just for DVP/EVB, fix according to - * new unified_drv. - */ - wl1271_write32(wl, WL18XX_SCR_PAD2, 0xB3); + struct wl18xx_priv *priv = wl->priv; + + /* write the translated board type to SCR_PAD2 */ + wl1271_write32(wl, WL18XX_SCR_PAD2, + wl18xx_board_type_to_scrpad2[priv->board_type]); wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); wl1271_write32(wl, 0x00A02360, 0xD0078); diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 53987997f78f..e0170aeba05c 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -154,6 +154,15 @@ enum { BOARD_TYPE_FPGA_18XX = 0, BOARD_TYPE_HDK_18XX = 1, BOARD_TYPE_DVP_EVB_18XX = 2, + + NUM_BOARD_TYPES, +}; + +/* board type values used by the firmware in the SCR_PAD2 register */ +enum { + SCR_PAD2_BOARD_TYPE_FPGA = 0xB1, + SCR_PAD2_BOARD_TYPE_HDK = 0xB2, + SCR_PAD2_BOARD_TYPE_DVP_EVB = 0xB3, }; struct wl18xx_mac_and_phy_params { -- cgit v1.2.3 From 1c351da6673b2806ade099aa8204c262df5d91ac Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:39 +0300 Subject: wl18xx: read FW pc on recovery Define the FW pc in the 18xx register translation table. This specific register is only valid in the boot partition, so change the momentarily change partitions. This doesn't damage 12xx cards, where the FW pc is accessible via boot partition as well. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 5214334c72c2..82403e356eb8 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -549,7 +549,7 @@ static const int wl18xx_rtable[REG_TABLE_LEN] = { [REG_EVENT_MAILBOX_PTR] = WL18XX_REG_EVENT_MAILBOX_PTR, [REG_INTERRUPT_TRIG] = WL18XX_REG_INTERRUPT_TRIG_H, [REG_INTERRUPT_MASK] = WL18XX_REG_INTERRUPT_MASK, - [REG_PC_ON_RECOVERY] = 0, /* TODO: where is the PC? */ + [REG_PC_ON_RECOVERY] = WL18XX_SCR_PAD4, [REG_CHIP_ID_B] = WL18XX_REG_CHIP_ID_B, [REG_CMD_MBOX_ADDRESS] = WL18XX_CMD_MBOX_ADDRESS, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 03d4a4b1f59c..dfa87b4d3bb2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -872,9 +872,12 @@ static void wl1271_recovery_work(struct work_struct *work) wl12xx_read_fwlog_panic(wl); + /* change partitions momentarily so we can read the FW pc */ + wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", wl->chip.fw_ver_str, wlcore_read_reg(wl, REG_PC_ON_RECOVERY)); + wlcore_set_partition(wl, &wl->ptable[PART_WORK]); BUG_ON(bug_on_recovery && !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); -- cgit v1.2.3 From d9fedea2a31476e7d11b3cc80c843de6cf135bd9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:40 +0300 Subject: wl18xx: disable FW log functionality Currently (Fw .67) appears to do more harm than good. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 82403e356eb8..0f078860804d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -574,6 +574,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl->chip.id); wl->sr_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | + WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; /* TODO: need to blocksize alignment for RX/TX separately? */ -- cgit v1.2.3 From be65202a610ca96aa945789c8b0a7bc3529b556e Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:41 +0300 Subject: wl18xx: read clock frequency and do top init accordingly Instead of using hardcoded values for a single frequency, we need to read the frequency and use the appropriate values for it in the top initialization. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/io.c | 60 +++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/io.h | 28 +++++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 51 ++++++++++++++++++++++++++-- drivers/net/wireless/ti/wl18xx/reg.h | 22 ++++++++++++ drivers/net/wireless/ti/wl18xx/wl18xx.h | 22 ++++++++++++ 6 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 drivers/net/wireless/ti/wl18xx/io.c create mode 100644 drivers/net/wireless/ti/wl18xx/io.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index 764d598bc968..abd881f4ea01 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o acx.o tx.o +wl18xx-objs = main.o acx.o tx.o io.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/io.c b/drivers/net/wireless/ti/wl18xx/io.c new file mode 100644 index 000000000000..598c057e722b --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/io.c @@ -0,0 +1,60 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "../wlcore/wlcore.h" +#include "../wlcore/io.h" + +#include "io.h" + +void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) +{ + u32 tmp; + + if (WARN_ON(addr % 2)) + return; + + if ((addr % 4) == 0) { + tmp = wl1271_read32(wl, addr); + tmp = (tmp & 0xffff0000) | val; + wl1271_write32(wl, addr, tmp); + } else { + tmp = wl1271_read32(wl, addr - 2); + tmp = (tmp & 0xffff) | (val << 16); + wl1271_write32(wl, addr - 2, tmp); + } +} + +u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr) +{ + u32 val; + + if (WARN_ON(addr % 2)) + return 0; + + if ((addr % 4) == 0) { + /* address is 4-bytes aligned */ + val = wl1271_read32(wl, addr); + return val & 0xffff; + } else { + val = wl1271_read32(wl, addr - 2); + return (val & 0xffff0000) >> 16; + } +} diff --git a/drivers/net/wireless/ti/wl18xx/io.h b/drivers/net/wireless/ti/wl18xx/io.h new file mode 100644 index 000000000000..be4e126ff617 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/io.h @@ -0,0 +1,28 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_IO_H__ +#define __WL18XX_IO_H__ + +void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val); +u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr); + +#endif /* __WL18XX_IO_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 0f078860804d..ce132494ebed 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -37,6 +37,7 @@ #include "acx.h" #include "tx.h" #include "wl18xx.h" +#include "io.h" #define WL18XX_RX_CHECKSUM_MASK 0x40 @@ -561,6 +562,18 @@ static const int wl18xx_rtable[REG_TABLE_LEN] = { [REG_RAW_FW_STATUS_ADDR] = WL18XX_FW_STATUS_ADDR, }; +static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { + [CLOCK_CONFIG_16_2_M] = { 7, 104, 801, 4, true }, + [CLOCK_CONFIG_16_368_M] = { 9, 132, 3751, 4, true }, + [CLOCK_CONFIG_16_8_M] = { 7, 100, 0, 0, false }, + [CLOCK_CONFIG_19_2_M] = { 8, 100, 0, 0, false }, + [CLOCK_CONFIG_26_M] = { 13, 120, 0, 0, false }, + [CLOCK_CONFIG_32_736_M] = { 9, 132, 3751, 4, true }, + [CLOCK_CONFIG_33_6_M] = { 7, 100, 0, 0, false }, + [CLOCK_CONFIG_38_468_M] = { 8, 100, 0, 0, false }, + [CLOCK_CONFIG_52_M] = { 13, 120, 0, 0, false }, +}; + /* TODO: maybe move to a new header file? */ #define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw.bin" @@ -592,15 +605,47 @@ out: static void wl18xx_set_clk(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; + u32 clk_freq; /* write the translated board type to SCR_PAD2 */ wl1271_write32(wl, WL18XX_SCR_PAD2, wl18xx_board_type_to_scrpad2[priv->board_type]); wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); - wl1271_write32(wl, 0x00A02360, 0xD0078); - wl1271_write32(wl, 0x00A0236c, 0x12); - wl1271_write32(wl, 0x00A02390, 0x20118); + + /* TODO: PG2: apparently we need to read the clk type */ + + clk_freq = wl18xx_top_reg_read(wl, PRIMARY_CLK_DETECT); + wl1271_debug(DEBUG_BOOT, "clock freq %d (%d, %d, %d, %d, %s)", clk_freq, + wl18xx_clk_table[clk_freq].n, wl18xx_clk_table[clk_freq].m, + wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q, + wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit"); + + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n); + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_M, wl18xx_clk_table[clk_freq].m); + + if (wl18xx_clk_table[clk_freq].swallow) { + /* first the 16 lower bits */ + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_1, + wl18xx_clk_table[clk_freq].q & + PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK); + /* then the 16 higher bits, masked out */ + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_2, + (wl18xx_clk_table[clk_freq].q >> 16) & + PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK); + + /* first the 16 lower bits */ + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_1, + wl18xx_clk_table[clk_freq].p & + PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK); + /* then the 16 higher bits, masked out */ + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_2, + (wl18xx_clk_table[clk_freq].p >> 16) & + PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK); + } else { + wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_SWALLOW_EN, + PLLSH_WCS_PLL_SWALLOW_EN_VAL2); + } } static void wl18xx_boot_soft_reset(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index e0170aeba05c..1ea3393cff2c 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -107,6 +107,28 @@ #define WL18XX_WELP_ARM_COMMAND (WL18XX_REGISTERS_BASE + 0x7100) #define WL18XX_ENABLE (WL18XX_REGISTERS_BASE + 0x01543C) +/* PRCM registers */ +#define PLATFORM_DETECTION 0xA0E3E0 +#define OCS_EN 0xA02080 +#define PRIMARY_CLK_DETECT 0xA020A6 +#define PLLSH_WCS_PLL_N 0xA02362 +#define PLLSH_WCS_PLL_M 0xA02360 +#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1 0xA02364 +#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2 0xA02366 +#define PLLSH_WCS_PLL_P_FACTOR_CFG_1 0xA02368 +#define PLLSH_WCS_PLL_P_FACTOR_CFG_2 0xA0236A +#define PLLSH_WCS_PLL_SWALLOW_EN 0xA0236C +#define PLLSH_WL_PLL_EN 0xA02392 + +#define PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK 0xFFFF +#define PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK 0x007F +#define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK 0xFFFF +#define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK 0x000F + +#define PLLSH_WCS_PLL_SWALLOW_EN_VAL1 0x1 +#define PLLSH_WCS_PLL_SWALLOW_EN_VAL2 0x12 + + #define WL18XX_CMD_MBOX_ADDRESS 0xB007B4 #define WL18XX_FW_STATUS_ADDR 0x50F8 diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 5bcc7f09f8ca..75abb5b48c6e 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -56,4 +56,26 @@ struct wl18xx_fw_status_priv { u8 padding[2]; }; +struct wl18xx_clk_cfg { + u32 n; + u32 m; + u32 p; + u32 q; + bool swallow; +}; + +enum { + CLOCK_CONFIG_16_2_M = 1, + CLOCK_CONFIG_16_368_M, + CLOCK_CONFIG_16_8_M, + CLOCK_CONFIG_19_2_M, + CLOCK_CONFIG_26_M, + CLOCK_CONFIG_32_736_M, + CLOCK_CONFIG_33_6_M, + CLOCK_CONFIG_38_468_M, + CLOCK_CONFIG_52_M, + + NUM_CLOCK_CONFIGS, +}; + #endif /* __WL18XX_PRIV_H__ */ -- cgit v1.2.3 From 8388569a34396fccee22ae8348ca624959694ce0 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:42 +0300 Subject: wlcore: rename wl12xx.h to wlcore_i.h The wl12xx.h file now contains mostly definitions that are internal to wlcore. Still, some things need to me moved to the public header, so for now we keep including it in wlcore.h. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/wl12xx.h | 509 ------------------------------ drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- drivers/net/wireless/ti/wlcore/wlcore_i.h | 509 ++++++++++++++++++++++++++++++ 3 files changed, 510 insertions(+), 510 deletions(-) delete mode 100644 drivers/net/wireless/ti/wlcore/wl12xx.h create mode 100644 drivers/net/wireless/ti/wlcore/wlcore_i.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/wl12xx.h b/drivers/net/wireless/ti/wlcore/wl12xx.h deleted file mode 100644 index 571a4ef34a32..000000000000 --- a/drivers/net/wireless/ti/wlcore/wl12xx.h +++ /dev/null @@ -1,509 +0,0 @@ -/* - * This file is part of wl1271 - * - * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. - * Copyright (C) 2008-2009 Nokia Corporation - * - * Contact: Luciano Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __WL12XX_H__ -#define __WL12XX_H__ - -#include -#include -#include -#include -#include -#include - -#include "conf.h" -#include "ini.h" - -#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin" -#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin" - -#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin" -#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" - -#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" -#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" - -/* - * wl127x and wl128x are using the same NVS file name. However, the - * ini parameters between them are different. The driver validates - * the correct NVS size in wl1271_boot_upload_nvs(). - */ -#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin" - -#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) -#define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) -#define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff - -#define WL1271_CIPHER_SUITE_GEM 0x00147201 - -#define WL1271_BUSY_WORD_CNT 1 -#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) - -#define WL1271_ELP_HW_STATE_ASLEEP 0 -#define WL1271_ELP_HW_STATE_IRQ 1 - -#define WL1271_DEFAULT_BEACON_INT 100 -#define WL1271_DEFAULT_DTIM_PERIOD 1 - -#define WL12XX_MAX_ROLES 4 -#define WL12XX_MAX_LINKS 12 -#define WL12XX_INVALID_ROLE_ID 0xff -#define WL12XX_INVALID_LINK_ID 0xff - -#define WL12XX_MAX_RATE_POLICIES 16 - -/* Defined by FW as 0. Will not be freed or allocated. */ -#define WL12XX_SYSTEM_HLID 0 - -/* - * When in AP-mode, we allow (at least) this number of packets - * to be transmitted to FW for a STA in PS-mode. Only when packets are - * present in the FW buffers it will wake the sleeping STA. We want to put - * enough packets for the driver to transmit all of its buffered data before - * the STA goes to sleep again. But we don't want to take too much memory - * as it might hurt the throughput of active STAs. - */ -#define WL1271_PS_STA_MAX_PACKETS 2 - -#define WL1271_AP_BSS_INDEX 0 -#define WL1271_AP_DEF_BEACON_EXP 20 - -#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) - -enum wl1271_state { - WL1271_STATE_OFF, - WL1271_STATE_ON, -}; - -enum wl12xx_fw_type { - WL12XX_FW_TYPE_NONE, - WL12XX_FW_TYPE_NORMAL, - WL12XX_FW_TYPE_MULTI, - WL12XX_FW_TYPE_PLT, -}; - -struct wl1271; - -enum { - FW_VER_CHIP, - FW_VER_IF_TYPE, - FW_VER_MAJOR, - FW_VER_SUBTYPE, - FW_VER_MINOR, - - NUM_FW_VER -}; - -#define FW_VER_CHIP_WL127X 6 -#define FW_VER_CHIP_WL128X 7 - -#define FW_VER_IF_TYPE_STA 1 -#define FW_VER_IF_TYPE_AP 2 - -#define FW_VER_MINOR_1_SPARE_STA_MIN 58 -#define FW_VER_MINOR_1_SPARE_AP_MIN 47 - -#define FW_VER_MINOR_FWLOG_STA_MIN 70 - -struct wl1271_chip { - u32 id; - char fw_ver_str[ETHTOOL_BUSINFO_LEN]; - unsigned int fw_ver[NUM_FW_VER]; -}; - -struct wl1271_stats { - struct acx_statistics *fw_stats; - unsigned long fw_stats_update; - - unsigned int retry_count; - unsigned int excessive_retries; -}; - -#define NUM_TX_QUEUES 4 -#define NUM_RX_PKT_DESC 8 - -#define AP_MAX_STATIONS 8 - -struct wl_fw_packet_counters { - /* Cumulative counter of released packets per AC */ - u8 tx_released_pkts[NUM_TX_QUEUES]; - - /* Cumulative counter of freed packets per HLID */ - u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; - - /* Cumulative counter of released Voice memory blocks */ - u8 tx_voice_released_blks; - - u8 padding[3]; -} __packed; - -/* FW status registers */ -struct wl_fw_status { - __le32 intr; - u8 fw_rx_counter; - u8 drv_rx_counter; - u8 reserved; - u8 tx_results_counter; - __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; - __le32 fw_localtime; - - /* - * A bitmap (where each bit represents a single HLID) - * to indicate if the station is in PS mode. - */ - __le32 link_ps_bitmap; - - /* - * A bitmap (where each bit represents a single HLID) to indicate - * if the station is in Fast mode - */ - __le32 link_fast_bitmap; - - /* Cumulative counter of total released mem blocks since FW-reset */ - __le32 total_released_blks; - - /* Size (in Memory Blocks) of TX pool */ - __le32 tx_total; - - struct wl_fw_packet_counters counters; - - __le32 log_start_addr; - - /* Private status to be used by the lower drivers */ - u8 priv[0]; -} __packed; - -struct wl1271_rx_mem_pool_addr { - u32 addr; - u32 addr_extra; -}; - -#define WL1271_MAX_CHANNELS 64 -struct wl1271_scan { - struct cfg80211_scan_request *req; - unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)]; - bool failed; - u8 state; - u8 ssid[IEEE80211_MAX_SSID_LEN+1]; - size_t ssid_len; -}; - -struct wl1271_if_operations { - void (*read)(struct device *child, int addr, void *buf, size_t len, - bool fixed); - void (*write)(struct device *child, int addr, void *buf, size_t len, - bool fixed); - void (*reset)(struct device *child); - void (*init)(struct device *child); - int (*power)(struct device *child, bool enable); - void (*set_block_size) (struct device *child, unsigned int blksz); -}; - -#define MAX_NUM_KEYS 14 -#define MAX_KEY_SIZE 32 - -struct wl1271_ap_key { - u8 id; - u8 key_type; - u8 key_size; - u8 key[MAX_KEY_SIZE]; - u8 hlid; - u32 tx_seq_32; - u16 tx_seq_16; -}; - -enum wl12xx_flags { - WL1271_FLAG_GPIO_POWER, - WL1271_FLAG_TX_QUEUE_STOPPED, - WL1271_FLAG_TX_PENDING, - WL1271_FLAG_IN_ELP, - WL1271_FLAG_ELP_REQUESTED, - WL1271_FLAG_IRQ_RUNNING, - WL1271_FLAG_FW_TX_BUSY, - WL1271_FLAG_DUMMY_PACKET_PENDING, - WL1271_FLAG_SUSPENDED, - WL1271_FLAG_PENDING_WORK, - WL1271_FLAG_SOFT_GEMINI, - WL1271_FLAG_RECOVERY_IN_PROGRESS, - WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, - WL1271_FLAG_INTENDED_FW_RECOVERY, -}; - -enum wl12xx_vif_flags { - WLVIF_FLAG_INITIALIZED, - WLVIF_FLAG_STA_ASSOCIATED, - WLVIF_FLAG_STA_AUTHORIZED, - WLVIF_FLAG_IBSS_JOINED, - WLVIF_FLAG_AP_STARTED, - WLVIF_FLAG_IN_PS, - WLVIF_FLAG_STA_STATE_SENT, - WLVIF_FLAG_RX_STREAMING_STARTED, - WLVIF_FLAG_PSPOLL_FAILURE, - WLVIF_FLAG_CS_PROGRESS, - WLVIF_FLAG_AP_PROBE_RESP_SET, - WLVIF_FLAG_IN_USE, -}; - -struct wl1271_link { - /* AP-mode - TX queue per AC in link */ - struct sk_buff_head tx_queue[NUM_TX_QUEUES]; - - /* accounting for allocated / freed packets in FW */ - u8 allocated_pkts; - u8 prev_freed_pkts; - - u8 addr[ETH_ALEN]; - - /* bitmap of TIDs where RX BA sessions are active for this link */ - u8 ba_bitmap; -}; - -#define WL1271_MAX_RX_FILTERS 5 -#define WL1271_RX_FILTER_MAX_FIELDS 8 - -#define WL1271_RX_FILTER_ETH_HEADER_SIZE 14 -#define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95 -#define RX_FILTER_FIELD_OVERHEAD \ - (sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *)) -#define WL1271_RX_FILTER_MAX_PATTERN_SIZE \ - (WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD) - -#define WL1271_RX_FILTER_FLAG_MASK BIT(0) -#define WL1271_RX_FILTER_FLAG_IP_HEADER 0 -#define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1) - -enum rx_filter_action { - FILTER_DROP = 0, - FILTER_SIGNAL = 1, - FILTER_FW_HANDLE = 2 -}; - -struct wl12xx_rx_filter_field { - __le16 offset; - u8 len; - u8 flags; - u8 *pattern; -} __packed; - -struct wl12xx_rx_filter { - u8 action; - int num_fields; - struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS]; -}; - -struct wl1271_station { - u8 hlid; -}; - -struct wl12xx_vif { - struct wl1271 *wl; - struct list_head list; - unsigned long flags; - u8 bss_type; - u8 p2p; /* we are using p2p role */ - u8 role_id; - - /* sta/ibss specific */ - u8 dev_role_id; - u8 dev_hlid; - - union { - struct { - u8 hlid; - u8 ba_rx_bitmap; - - u8 basic_rate_idx; - u8 ap_rate_idx; - u8 p2p_rate_idx; - - bool qos; - } sta; - struct { - u8 global_hlid; - u8 bcast_hlid; - - /* HLIDs bitmap of associated stations */ - unsigned long sta_hlid_map[BITS_TO_LONGS( - WL12XX_MAX_LINKS)]; - - /* recoreded keys - set here before AP startup */ - struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS]; - - u8 mgmt_rate_idx; - u8 bcast_rate_idx; - u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT]; - } ap; - }; - - /* the hlid of the last transmitted skb */ - int last_tx_hlid; - - unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; - - u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; - u8 ssid_len; - - /* The current band */ - enum ieee80211_band band; - int channel; - enum nl80211_channel_type channel_type; - - u32 bitrate_masks[IEEE80211_NUM_BANDS]; - u32 basic_rate_set; - - /* - * currently configured rate set: - * bits 0-15 - 802.11abg rates - * bits 16-23 - 802.11n MCS index mask - * support only 1 stream, thus only 8 bits for the MCS rates (0-7). - */ - u32 basic_rate; - u32 rate_set; - - /* probe-req template for the current AP */ - struct sk_buff *probereq; - - /* Beaconing interval (needed for ad-hoc) */ - u32 beacon_int; - - /* Default key (for WEP) */ - u32 default_key; - - /* Our association ID */ - u16 aid; - - /* Session counter for the chipset */ - int session_counter; - - /* retry counter for PSM entries */ - u8 psm_entry_retry; - - /* in dBm */ - int power_level; - - int rssi_thold; - int last_rssi_event; - - /* save the current encryption type for auto-arp config */ - u8 encryption_type; - __be32 ip_addr; - - /* RX BA constraint value */ - bool ba_support; - bool ba_allowed; - - /* Rx Streaming */ - struct work_struct rx_streaming_enable_work; - struct work_struct rx_streaming_disable_work; - struct timer_list rx_streaming_timer; - - /* does the current role use GEM for encryption (AP or STA) */ - bool is_gem; - - /* - * This struct must be last! - * data that has to be saved acrossed reconfigs (e.g. recovery) - * should be declared in this struct. - */ - struct { - u8 persistent[0]; - /* - * Security sequence number - * bits 0-15: lower 16 bits part of sequence number - * bits 16-47: higher 32 bits part of sequence number - * bits 48-63: not in use - */ - u64 tx_security_seq; - - /* 8 bits of the last sequence number in use */ - u8 tx_security_last_seq_lsb; - }; -}; - -static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif) -{ - return (struct wl12xx_vif *)vif->drv_priv; -} - -static inline -struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif) -{ - return container_of((void *)wlvif, struct ieee80211_vif, drv_priv); -} - -#define wl12xx_for_each_wlvif(wl, wlvif) \ - list_for_each_entry(wlvif, &wl->wlvif_list, list) - -#define wl12xx_for_each_wlvif_continue(wl, wlvif) \ - list_for_each_entry_continue(wlvif, &wl->wlvif_list, list) - -#define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type) \ - wl12xx_for_each_wlvif(wl, wlvif) \ - if (wlvif->bss_type == _bss_type) - -#define wl12xx_for_each_wlvif_sta(wl, wlvif) \ - wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS) - -#define wl12xx_for_each_wlvif_ap(wl, wlvif) \ - wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS) - -int wl1271_plt_start(struct wl1271 *wl); -int wl1271_plt_stop(struct wl1271 *wl); -int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl12xx_queue_recovery_work(struct wl1271 *wl); -size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); -int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, - u16 offset, u8 flags, - u8 *pattern, u8 len); -void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); -struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); -int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); -void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, - u8 *buf); - -#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ - -#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */ -#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */ - -#define WL1271_DEFAULT_POWER_LEVEL 0 - -#define WL1271_TX_QUEUE_LOW_WATERMARK 32 -#define WL1271_TX_QUEUE_HIGH_WATERMARK 256 - -#define WL1271_DEFERRED_QUEUE_LIMIT 64 - -/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power - on in case is has been shut down shortly before */ -#define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */ -#define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */ - -/* Macros to handle wl1271.sta_rate_set */ -#define HW_BG_RATES_MASK 0xffff -#define HW_HT_RATES_OFFSET 16 -#define HW_MIMO_RATES_OFFSET 24 - -#define WL12XX_HW_BLOCK_SIZE 256 - -#endif diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index f9ec84da3f79..8f724daf4208 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -24,7 +24,7 @@ #include -#include "wl12xx.h" +#include "wlcore_i.h" #include "event.h" /* The maximum number of Tx descriptors in all chip families */ diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h new file mode 100644 index 000000000000..819c97cdc162 --- /dev/null +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -0,0 +1,509 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WLCORE_I_H__ +#define __WLCORE_I_H__ + +#include +#include +#include +#include +#include +#include + +#include "conf.h" +#include "ini.h" + +#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin" +#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin" + +#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin" +#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" + +#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" +#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" + +/* + * wl127x and wl128x are using the same NVS file name. However, the + * ini parameters between them are different. The driver validates + * the correct NVS size in wl1271_boot_upload_nvs(). + */ +#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin" + +#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) +#define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) +#define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff + +#define WL1271_CIPHER_SUITE_GEM 0x00147201 + +#define WL1271_BUSY_WORD_CNT 1 +#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) + +#define WL1271_ELP_HW_STATE_ASLEEP 0 +#define WL1271_ELP_HW_STATE_IRQ 1 + +#define WL1271_DEFAULT_BEACON_INT 100 +#define WL1271_DEFAULT_DTIM_PERIOD 1 + +#define WL12XX_MAX_ROLES 4 +#define WL12XX_MAX_LINKS 12 +#define WL12XX_INVALID_ROLE_ID 0xff +#define WL12XX_INVALID_LINK_ID 0xff + +#define WL12XX_MAX_RATE_POLICIES 16 + +/* Defined by FW as 0. Will not be freed or allocated. */ +#define WL12XX_SYSTEM_HLID 0 + +/* + * When in AP-mode, we allow (at least) this number of packets + * to be transmitted to FW for a STA in PS-mode. Only when packets are + * present in the FW buffers it will wake the sleeping STA. We want to put + * enough packets for the driver to transmit all of its buffered data before + * the STA goes to sleep again. But we don't want to take too much memory + * as it might hurt the throughput of active STAs. + */ +#define WL1271_PS_STA_MAX_PACKETS 2 + +#define WL1271_AP_BSS_INDEX 0 +#define WL1271_AP_DEF_BEACON_EXP 20 + +#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) + +enum wl1271_state { + WL1271_STATE_OFF, + WL1271_STATE_ON, +}; + +enum wl12xx_fw_type { + WL12XX_FW_TYPE_NONE, + WL12XX_FW_TYPE_NORMAL, + WL12XX_FW_TYPE_MULTI, + WL12XX_FW_TYPE_PLT, +}; + +struct wl1271; + +enum { + FW_VER_CHIP, + FW_VER_IF_TYPE, + FW_VER_MAJOR, + FW_VER_SUBTYPE, + FW_VER_MINOR, + + NUM_FW_VER +}; + +#define FW_VER_CHIP_WL127X 6 +#define FW_VER_CHIP_WL128X 7 + +#define FW_VER_IF_TYPE_STA 1 +#define FW_VER_IF_TYPE_AP 2 + +#define FW_VER_MINOR_1_SPARE_STA_MIN 58 +#define FW_VER_MINOR_1_SPARE_AP_MIN 47 + +#define FW_VER_MINOR_FWLOG_STA_MIN 70 + +struct wl1271_chip { + u32 id; + char fw_ver_str[ETHTOOL_BUSINFO_LEN]; + unsigned int fw_ver[NUM_FW_VER]; +}; + +struct wl1271_stats { + struct acx_statistics *fw_stats; + unsigned long fw_stats_update; + + unsigned int retry_count; + unsigned int excessive_retries; +}; + +#define NUM_TX_QUEUES 4 +#define NUM_RX_PKT_DESC 8 + +#define AP_MAX_STATIONS 8 + +struct wl_fw_packet_counters { + /* Cumulative counter of released packets per AC */ + u8 tx_released_pkts[NUM_TX_QUEUES]; + + /* Cumulative counter of freed packets per HLID */ + u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; + + /* Cumulative counter of released Voice memory blocks */ + u8 tx_voice_released_blks; + + u8 padding[3]; +} __packed; + +/* FW status registers */ +struct wl_fw_status { + __le32 intr; + u8 fw_rx_counter; + u8 drv_rx_counter; + u8 reserved; + u8 tx_results_counter; + __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; + __le32 fw_localtime; + + /* + * A bitmap (where each bit represents a single HLID) + * to indicate if the station is in PS mode. + */ + __le32 link_ps_bitmap; + + /* + * A bitmap (where each bit represents a single HLID) to indicate + * if the station is in Fast mode + */ + __le32 link_fast_bitmap; + + /* Cumulative counter of total released mem blocks since FW-reset */ + __le32 total_released_blks; + + /* Size (in Memory Blocks) of TX pool */ + __le32 tx_total; + + struct wl_fw_packet_counters counters; + + __le32 log_start_addr; + + /* Private status to be used by the lower drivers */ + u8 priv[0]; +} __packed; + +struct wl1271_rx_mem_pool_addr { + u32 addr; + u32 addr_extra; +}; + +#define WL1271_MAX_CHANNELS 64 +struct wl1271_scan { + struct cfg80211_scan_request *req; + unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)]; + bool failed; + u8 state; + u8 ssid[IEEE80211_MAX_SSID_LEN+1]; + size_t ssid_len; +}; + +struct wl1271_if_operations { + void (*read)(struct device *child, int addr, void *buf, size_t len, + bool fixed); + void (*write)(struct device *child, int addr, void *buf, size_t len, + bool fixed); + void (*reset)(struct device *child); + void (*init)(struct device *child); + int (*power)(struct device *child, bool enable); + void (*set_block_size) (struct device *child, unsigned int blksz); +}; + +#define MAX_NUM_KEYS 14 +#define MAX_KEY_SIZE 32 + +struct wl1271_ap_key { + u8 id; + u8 key_type; + u8 key_size; + u8 key[MAX_KEY_SIZE]; + u8 hlid; + u32 tx_seq_32; + u16 tx_seq_16; +}; + +enum wl12xx_flags { + WL1271_FLAG_GPIO_POWER, + WL1271_FLAG_TX_QUEUE_STOPPED, + WL1271_FLAG_TX_PENDING, + WL1271_FLAG_IN_ELP, + WL1271_FLAG_ELP_REQUESTED, + WL1271_FLAG_IRQ_RUNNING, + WL1271_FLAG_FW_TX_BUSY, + WL1271_FLAG_DUMMY_PACKET_PENDING, + WL1271_FLAG_SUSPENDED, + WL1271_FLAG_PENDING_WORK, + WL1271_FLAG_SOFT_GEMINI, + WL1271_FLAG_RECOVERY_IN_PROGRESS, + WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, + WL1271_FLAG_INTENDED_FW_RECOVERY, +}; + +enum wl12xx_vif_flags { + WLVIF_FLAG_INITIALIZED, + WLVIF_FLAG_STA_ASSOCIATED, + WLVIF_FLAG_STA_AUTHORIZED, + WLVIF_FLAG_IBSS_JOINED, + WLVIF_FLAG_AP_STARTED, + WLVIF_FLAG_IN_PS, + WLVIF_FLAG_STA_STATE_SENT, + WLVIF_FLAG_RX_STREAMING_STARTED, + WLVIF_FLAG_PSPOLL_FAILURE, + WLVIF_FLAG_CS_PROGRESS, + WLVIF_FLAG_AP_PROBE_RESP_SET, + WLVIF_FLAG_IN_USE, +}; + +struct wl1271_link { + /* AP-mode - TX queue per AC in link */ + struct sk_buff_head tx_queue[NUM_TX_QUEUES]; + + /* accounting for allocated / freed packets in FW */ + u8 allocated_pkts; + u8 prev_freed_pkts; + + u8 addr[ETH_ALEN]; + + /* bitmap of TIDs where RX BA sessions are active for this link */ + u8 ba_bitmap; +}; + +#define WL1271_MAX_RX_FILTERS 5 +#define WL1271_RX_FILTER_MAX_FIELDS 8 + +#define WL1271_RX_FILTER_ETH_HEADER_SIZE 14 +#define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95 +#define RX_FILTER_FIELD_OVERHEAD \ + (sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *)) +#define WL1271_RX_FILTER_MAX_PATTERN_SIZE \ + (WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD) + +#define WL1271_RX_FILTER_FLAG_MASK BIT(0) +#define WL1271_RX_FILTER_FLAG_IP_HEADER 0 +#define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1) + +enum rx_filter_action { + FILTER_DROP = 0, + FILTER_SIGNAL = 1, + FILTER_FW_HANDLE = 2 +}; + +struct wl12xx_rx_filter_field { + __le16 offset; + u8 len; + u8 flags; + u8 *pattern; +} __packed; + +struct wl12xx_rx_filter { + u8 action; + int num_fields; + struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS]; +}; + +struct wl1271_station { + u8 hlid; +}; + +struct wl12xx_vif { + struct wl1271 *wl; + struct list_head list; + unsigned long flags; + u8 bss_type; + u8 p2p; /* we are using p2p role */ + u8 role_id; + + /* sta/ibss specific */ + u8 dev_role_id; + u8 dev_hlid; + + union { + struct { + u8 hlid; + u8 ba_rx_bitmap; + + u8 basic_rate_idx; + u8 ap_rate_idx; + u8 p2p_rate_idx; + + bool qos; + } sta; + struct { + u8 global_hlid; + u8 bcast_hlid; + + /* HLIDs bitmap of associated stations */ + unsigned long sta_hlid_map[BITS_TO_LONGS( + WL12XX_MAX_LINKS)]; + + /* recoreded keys - set here before AP startup */ + struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS]; + + u8 mgmt_rate_idx; + u8 bcast_rate_idx; + u8 ucast_rate_idx[CONF_TX_MAX_AC_COUNT]; + } ap; + }; + + /* the hlid of the last transmitted skb */ + int last_tx_hlid; + + unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; + + u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; + u8 ssid_len; + + /* The current band */ + enum ieee80211_band band; + int channel; + enum nl80211_channel_type channel_type; + + u32 bitrate_masks[IEEE80211_NUM_BANDS]; + u32 basic_rate_set; + + /* + * currently configured rate set: + * bits 0-15 - 802.11abg rates + * bits 16-23 - 802.11n MCS index mask + * support only 1 stream, thus only 8 bits for the MCS rates (0-7). + */ + u32 basic_rate; + u32 rate_set; + + /* probe-req template for the current AP */ + struct sk_buff *probereq; + + /* Beaconing interval (needed for ad-hoc) */ + u32 beacon_int; + + /* Default key (for WEP) */ + u32 default_key; + + /* Our association ID */ + u16 aid; + + /* Session counter for the chipset */ + int session_counter; + + /* retry counter for PSM entries */ + u8 psm_entry_retry; + + /* in dBm */ + int power_level; + + int rssi_thold; + int last_rssi_event; + + /* save the current encryption type for auto-arp config */ + u8 encryption_type; + __be32 ip_addr; + + /* RX BA constraint value */ + bool ba_support; + bool ba_allowed; + + /* Rx Streaming */ + struct work_struct rx_streaming_enable_work; + struct work_struct rx_streaming_disable_work; + struct timer_list rx_streaming_timer; + + /* does the current role use GEM for encryption (AP or STA) */ + bool is_gem; + + /* + * This struct must be last! + * data that has to be saved acrossed reconfigs (e.g. recovery) + * should be declared in this struct. + */ + struct { + u8 persistent[0]; + /* + * Security sequence number + * bits 0-15: lower 16 bits part of sequence number + * bits 16-47: higher 32 bits part of sequence number + * bits 48-63: not in use + */ + u64 tx_security_seq; + + /* 8 bits of the last sequence number in use */ + u8 tx_security_last_seq_lsb; + }; +}; + +static inline struct wl12xx_vif *wl12xx_vif_to_data(struct ieee80211_vif *vif) +{ + return (struct wl12xx_vif *)vif->drv_priv; +} + +static inline +struct ieee80211_vif *wl12xx_wlvif_to_vif(struct wl12xx_vif *wlvif) +{ + return container_of((void *)wlvif, struct ieee80211_vif, drv_priv); +} + +#define wl12xx_for_each_wlvif(wl, wlvif) \ + list_for_each_entry(wlvif, &wl->wlvif_list, list) + +#define wl12xx_for_each_wlvif_continue(wl, wlvif) \ + list_for_each_entry_continue(wlvif, &wl->wlvif_list, list) + +#define wl12xx_for_each_wlvif_bss_type(wl, wlvif, _bss_type) \ + wl12xx_for_each_wlvif(wl, wlvif) \ + if (wlvif->bss_type == _bss_type) + +#define wl12xx_for_each_wlvif_sta(wl, wlvif) \ + wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_STA_BSS) + +#define wl12xx_for_each_wlvif_ap(wl, wlvif) \ + wl12xx_for_each_wlvif_bss_type(wl, wlvif, BSS_TYPE_AP_BSS) + +int wl1271_plt_start(struct wl1271 *wl); +int wl1271_plt_stop(struct wl1271 *wl); +int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); +void wl12xx_queue_recovery_work(struct wl1271 *wl); +size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); +int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, + u16 offset, u8 flags, + u8 *pattern, u8 len); +void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); +struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); +int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); +void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, + u8 *buf); + +#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ + +#define SESSION_COUNTER_MAX 6 /* maximum value for the session counter */ +#define SESSION_COUNTER_INVALID 7 /* used with dummy_packet */ + +#define WL1271_DEFAULT_POWER_LEVEL 0 + +#define WL1271_TX_QUEUE_LOW_WATERMARK 32 +#define WL1271_TX_QUEUE_HIGH_WATERMARK 256 + +#define WL1271_DEFERRED_QUEUE_LIMIT 64 + +/* WL1271 needs a 200ms sleep after power on, and a 20ms sleep before power + on in case is has been shut down shortly before */ +#define WL1271_PRE_POWER_ON_SLEEP 20 /* in milliseconds */ +#define WL1271_POWER_ON_SLEEP 200 /* in milliseconds */ + +/* Macros to handle wl1271.sta_rate_set */ +#define HW_BG_RATES_MASK 0xffff +#define HW_HT_RATES_OFFSET 16 +#define HW_MIMO_RATES_OFFSET 24 + +#define WL12XX_HW_BLOCK_SIZE 256 + +#endif /* __WLCORE_I_H__ */ -- cgit v1.2.3 From a5d751bb9298360f5547d30734aed4a54a8af3e4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:43 +0300 Subject: wlcore/wl12xx: move ref_clock and tcxo_clock elements to wl12xx Only wl12xx needs to get the ref_clock anc tcxo_clock values from the platform data. Move these elements from the wl1271 structure to wl12xx's private data. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/cmd.c | 8 +-- drivers/net/wireless/ti/wl12xx/main.c | 88 ++++++++++++++++++++++++++------ drivers/net/wireless/ti/wl12xx/wl12xx.h | 3 ++ drivers/net/wireless/ti/wlcore/debugfs.c | 3 +- drivers/net/wireless/ti/wlcore/main.c | 2 - drivers/net/wireless/ti/wlcore/wlcore.h | 3 -- 6 files changed, 82 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c index 8ffaeb5f2147..50ba7480b790 100644 --- a/drivers/net/wireless/ti/wl12xx/cmd.c +++ b/drivers/net/wireless/ti/wl12xx/cmd.c @@ -65,6 +65,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) struct wl1271_general_parms_cmd *gen_parms; struct wl1271_ini_general_params *gp = &((struct wl1271_nvs_file *)wl->nvs)->general_params; + struct wl12xx_priv *priv = wl->priv; bool answer = false; int ret; @@ -88,7 +89,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) answer = true; /* Override the REF CLK from the NVS with the one from platform data */ - gen_parms->general_params.ref_clock = wl->ref_clock; + gen_parms->general_params.ref_clock = priv->ref_clock; ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { @@ -118,6 +119,7 @@ int wl128x_cmd_general_parms(struct wl1271 *wl) struct wl128x_general_parms_cmd *gen_parms; struct wl128x_ini_general_params *gp = &((struct wl128x_nvs_file *)wl->nvs)->general_params; + struct wl12xx_priv *priv = wl->priv; bool answer = false; int ret; @@ -141,8 +143,8 @@ int wl128x_cmd_general_parms(struct wl1271 *wl) answer = true; /* Replace REF and TCXO CLKs with the ones from platform data */ - gen_parms->general_params.ref_clock = wl->ref_clock; - gen_parms->general_params.tcxo_ref_clock = wl->tcxo_clock; + gen_parms->general_params.ref_clock = priv->ref_clock; + gen_parms->general_params.tcxo_ref_clock = priv->tcxo_clock; ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index ad760a329c6a..962d85d625de 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -40,6 +40,9 @@ #include "cmd.h" #include "acx.h" +static char *fref_param; +static char *tcxo_param; + static struct wlcore_conf wl12xx_conf = { .sg = { .params = { @@ -773,6 +776,7 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) u16 spare_reg; u16 pll_config; u8 input_freq; + struct wl12xx_priv *priv = wl->priv; /* Mask bits [3:1] in the sys_clk_cfg register */ spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); @@ -782,8 +786,8 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); /* Handle special cases of the TCXO clock */ - if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || - wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6) + if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || + priv->tcxo_clock == WL12XX_TCXOCLOCK_33_6) return wl128x_manually_configure_mcs_pll(wl); /* Set the input frequency according to the selected clock source */ @@ -808,11 +812,12 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) */ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) { + struct wl12xx_priv *priv = wl->priv; u16 sys_clk_cfg; /* For XTAL-only modes, FREF will be used after switching from TCXO */ - if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL || - wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) { + if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL || + priv->ref_clock == WL12XX_REFCLOCK_38_XTAL) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; @@ -826,8 +831,8 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) goto fref_clk; /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ - if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || - wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { + if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || + priv->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { if (!wl128x_switch_tcxo_to_fref(wl)) return -EINVAL; goto fref_clk; @@ -836,14 +841,14 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) /* TCXO clock is selected */ if (!wl128x_is_tcxo_valid(wl)) return -EINVAL; - *selected_clock = wl->tcxo_clock; + *selected_clock = priv->tcxo_clock; goto config_mcs_pll; fref_clk: /* FREF clock is selected */ if (!wl128x_is_fref_valid(wl)) return -EINVAL; - *selected_clock = wl->ref_clock; + *selected_clock = priv->ref_clock; config_mcs_pll: return wl128x_configure_mcs_pll(wl, *selected_clock); @@ -851,25 +856,26 @@ config_mcs_pll: static int wl127x_boot_clk(struct wl1271 *wl) { + struct wl12xx_priv *priv = wl->priv; u32 pause; u32 clk; if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3) wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION; - if (wl->ref_clock == CONF_REF_CLK_19_2_E || - wl->ref_clock == CONF_REF_CLK_38_4_E || - wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) + if (priv->ref_clock == CONF_REF_CLK_19_2_E || + priv->ref_clock == CONF_REF_CLK_38_4_E || + priv->ref_clock == CONF_REF_CLK_38_4_M_XTAL) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; - else if (wl->ref_clock == CONF_REF_CLK_26_E || - wl->ref_clock == CONF_REF_CLK_52_E) + else if (priv->ref_clock == CONF_REF_CLK_26_E || + priv->ref_clock == CONF_REF_CLK_52_E) /* ref clk: 26/52 */ clk = 0x5; else return -EINVAL; - if (wl->ref_clock != CONF_REF_CLK_19_2_E) { + if (priv->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE); @@ -939,6 +945,7 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) static int wl12xx_pre_boot(struct wl1271 *wl) { + struct wl12xx_priv *priv = wl->priv; int ret = 0; u32 clk; int selected_clock = -1; @@ -970,7 +977,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl) if (wl->chip.id == CHIP_ID_1283_PG20) clk |= ((selected_clock & 0x3) << 1) << 4; else - clk |= (wl->ref_clock << 1) << 4; + clk |= (priv->ref_clock << 1) << 4; wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk); @@ -1333,6 +1340,7 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { static int __devinit wl12xx_probe(struct platform_device *pdev) { + struct wl12xx_platform_data *pdata = pdev->dev.platform_data; struct wl1271 *wl; struct ieee80211_hw *hw; struct wl12xx_priv *priv; @@ -1344,6 +1352,7 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) } wl = hw->priv; + priv = wl->priv; wl->ops = &wl12xx_ops; wl->ptable = wl12xx_ptable; wl->rtable = wl12xx_rtable; @@ -1357,6 +1366,48 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap)); wl12xx_conf_init(wl); + if (!fref_param) { + priv->ref_clock = pdata->board_ref_clock; + } else { + if (!strcmp(fref_param, "19.2")) + priv->ref_clock = WL12XX_REFCLOCK_19; + else if (!strcmp(fref_param, "26")) + priv->ref_clock = WL12XX_REFCLOCK_26; + else if (!strcmp(fref_param, "26x")) + priv->ref_clock = WL12XX_REFCLOCK_26_XTAL; + else if (!strcmp(fref_param, "38.4")) + priv->ref_clock = WL12XX_REFCLOCK_38; + else if (!strcmp(fref_param, "38.4x")) + priv->ref_clock = WL12XX_REFCLOCK_38_XTAL; + else if (!strcmp(fref_param, "52")) + priv->ref_clock = WL12XX_REFCLOCK_52; + else + wl1271_error("Invalid fref parameter %s", fref_param); + } + + if (!tcxo_param) { + priv->tcxo_clock = pdata->board_tcxo_clock; + } else { + if (!strcmp(tcxo_param, "19.2")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_19_2; + else if (!strcmp(tcxo_param, "26")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_26; + else if (!strcmp(tcxo_param, "38.4")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_38_4; + else if (!strcmp(tcxo_param, "52")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_52; + else if (!strcmp(tcxo_param, "16.368")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_16_368; + else if (!strcmp(tcxo_param, "32.736")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_32_736; + else if (!strcmp(tcxo_param, "16.8")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_16_8; + else if (!strcmp(tcxo_param, "33.6")) + priv->tcxo_clock = WL12XX_TCXOCLOCK_33_6; + else + wl1271_error("Invalid tcxo parameter %s", tcxo_param); + } + return wlcore_probe(wl, pdev); } @@ -1388,6 +1439,13 @@ static void __exit wl12xx_exit(void) } module_exit(wl12xx_exit); +module_param_named(fref, fref_param, charp, 0); +MODULE_PARM_DESC(fref, "FREF clock: 19.2, 26, 26x, 38.4, 38.4x, 52"); + +module_param_named(tcxo, tcxo_param, charp, 0); +MODULE_PARM_DESC(tcxo, + "TCXO clock: 19.2, 26, 38.4, 52, 16.368, 32.736, 16.8, 33.6"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 74cd332e23ef..0d81c1e00b4a 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -26,6 +26,9 @@ struct wl12xx_priv { struct wl12xx_priv_conf conf; + + int ref_clock; + int tcxo_clock; }; #endif /* __WL12XX_PRIV_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index d5aea1ff5ad1..d3775198d265 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -535,8 +535,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_LHEX(ap_ps_map); DRIVER_STATE_PRINT_HEX(quirks); DRIVER_STATE_PRINT_HEX(irq); - DRIVER_STATE_PRINT_HEX(ref_clock); - DRIVER_STATE_PRINT_HEX(tcxo_clock); + /* TODO: ref_clock and tcxo_clock were moved to wl12xx priv */ DRIVER_STATE_PRINT_HEX(hw_pg_ver); DRIVER_STATE_PRINT_HEX(platform_quirks); DRIVER_STATE_PRINT_HEX(chip.id); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index dfa87b4d3bb2..cd73dfe42448 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5295,8 +5295,6 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) wlcore_adjust_conf(wl); wl->irq = platform_get_irq(pdev, 0); - wl->ref_clock = pdata->board_ref_clock; - wl->tcxo_clock = pdata->board_tcxo_clock; wl->platform_quirks = pdata->platform_quirks; wl->set_power = pdata->set_power; wl->dev = &pdev->dev; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 8f724daf4208..89abbced7bae 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -130,7 +130,6 @@ struct wl1271 { void (*set_power)(bool enable); int irq; - int ref_clock; spinlock_t wl_lock; @@ -288,8 +287,6 @@ struct wl1271 { /* bands supported by this instance of wl12xx */ struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; - int tcxo_clock; - /* * wowlan trigger was configured during suspend. * (currently, only "ANY" trigger is supported) -- cgit v1.2.3 From 549562946fab225439bd6878982031ad6ce2aab9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:44 +0300 Subject: wl18xx: implement hw op to read PG version Read the HW PG version of the 18xx chip from FUSE. Based on an earlier patch by Luciano Coelho . Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 17 +++++++++++++++-- drivers/net/wireless/ti/wl18xx/reg.h | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index ce132494ebed..9aae0af25fef 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -659,8 +659,6 @@ static void wl18xx_boot_soft_reset(struct wl1271 *wl) static int wl18xx_pre_boot(struct wl1271 *wl) { - /* TODO: add hw_pg_ver reading */ - wl18xx_set_clk(wl); /* Continue the ELP wake up sequence */ @@ -946,6 +944,20 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, } } +static s8 wl18xx_get_pg_ver(struct wl1271 *wl) +{ + u32 fuse; + + wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + + fuse = wl1271_read32(wl, WL18XX_REG_FUSE_DATA_1_3); + fuse = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; + + wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + + return (s8)fuse; +} + static void wl18xx_conf_init(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; @@ -971,6 +983,7 @@ static struct wlcore_ops wl18xx_ops = { .tx_delayed_compl = NULL, .hw_init = wl18xx_hw_init, .set_tx_desc_csum = wl18xx_set_tx_desc_csum, + .get_pg_ver = wl18xx_get_pg_ver, .set_rx_csum = wl18xx_set_rx_csum, .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 1ea3393cff2c..43c480fb837f 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -128,6 +128,9 @@ #define PLLSH_WCS_PLL_SWALLOW_EN_VAL1 0x1 #define PLLSH_WCS_PLL_SWALLOW_EN_VAL2 0x12 +#define WL18XX_REG_FUSE_DATA_1_3 0xA0260C +#define WL18XX_PG_VER_MASK 0x70 +#define WL18XX_PG_VER_OFFSET 4 #define WL18XX_CMD_MBOX_ADDRESS 0xB007B4 -- cgit v1.2.3 From 4b4887e9710158dca6efd3646ba87f1da03ebe8b Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:45 +0300 Subject: wlcore/wl12xx: move rx_mem_pool_addr element to wl12xx Only wl127x chips use the rx_mem_pool_addr values, which need to be given to the firmware as part of the RX path. Move this from core to the wl12xx driver. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/main.c | 2 +- drivers/net/wireless/ti/wl12xx/wl12xx.h | 5 +++++ drivers/net/wireless/ti/wlcore/wlcore.h | 3 --- drivers/net/wireless/ti/wlcore/wlcore_i.h | 5 ----- 4 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 962d85d625de..02668557fef0 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -596,7 +596,7 @@ static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) { if (wl->chip.id != CHIP_ID_1283_PG20) { struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; - struct wl1271_rx_mem_pool_addr rx_mem_addr; + struct wl127x_rx_mem_pool_addr rx_mem_addr; /* * Choose the block we want to read diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 0d81c1e00b4a..de1132410876 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -24,6 +24,11 @@ #include "conf.h" +struct wl127x_rx_mem_pool_addr { + u32 addr; + u32 addr_extra; +}; + struct wl12xx_priv { struct wl12xx_priv_conf conf; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 89abbced7bae..f62152e35f3c 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -213,9 +213,6 @@ struct wl1271 { /* FW Rx counter */ u32 rx_counter; - /* Rx memory pool address */ - struct wl1271_rx_mem_pool_addr rx_mem_pool_addr; - /* Intermediate buffer, used for packet aggregation */ u8 *aggr_buf; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 819c97cdc162..53263a978a75 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -194,11 +194,6 @@ struct wl_fw_status { u8 priv[0]; } __packed; -struct wl1271_rx_mem_pool_addr { - u32 addr; - u32 addr_extra; -}; - #define WL1271_MAX_CHANNELS 64 struct wl1271_scan { struct cfg80211_scan_request *req; -- cgit v1.2.3 From f2baf075c5121b30922751f34920815743d7ee67 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:46 +0300 Subject: wl18xx: init Tx-released index to 0 on HW init This ensures the index is 0 on FW recovery and prevents spurious error prints - "WARNING illegal id in tx completion". Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 9aae0af25fef..a8b9ceacd9c2 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -850,11 +850,15 @@ static void wl18xx_tx_immediate_completion(struct wl1271 *wl) static int wl18xx_hw_init(struct wl1271 *wl) { int ret; + struct wl18xx_priv *priv = wl->priv; u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE | HOST_IF_CFG_ADD_RX_ALIGNMENT; u32 sdio_align_size = 0; + /* (re)init private structures. Relevant on recovery as well. */ + priv->last_fw_rls_idx = 0; + /* Enable Tx SDIO padding */ if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) { host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; -- cgit v1.2.3 From 2f1d74e6e7f26df2665b7df5ff912bc1d3ae75dc Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:47 +0300 Subject: wl18xx: don't upload NVS to FW In this chip family the NVS file should not be uploaded to FW. In fact, we encounter strange bugs (sdio errors) when trying to upload it with certain parameters. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index a8b9ceacd9c2..ddd11d00f033 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -755,10 +755,6 @@ static int wl18xx_boot(struct wl1271 *wl) if (ret < 0) goto out; - ret = wlcore_boot_upload_nvs(wl); - if (ret < 0) - goto out; - wl18xx_pre_upload(wl); ret = wlcore_boot_upload_firmware(wl); -- cgit v1.2.3 From 05057c0621dea083b617e7c35437faa9db7b7104 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:48 +0300 Subject: wl18xx: change board type enum according to new FW Add more board types and remove a now unneeded write to SCR_PAD2 setting the board type. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 43 ++++++++++++++--------------------- drivers/net/wireless/ti/wl18xx/reg.h | 15 ++++-------- 2 files changed, 22 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index ddd11d00f033..f892d37443e2 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -45,12 +45,6 @@ static char *ht_mode_param; static char *board_type_param; -static const u32 wl18xx_board_type_to_scrpad2[NUM_BOARD_TYPES] = { - [BOARD_TYPE_FPGA_18XX] = SCR_PAD2_BOARD_TYPE_FPGA, - [BOARD_TYPE_HDK_18XX] = SCR_PAD2_BOARD_TYPE_HDK, - [BOARD_TYPE_DVP_EVB_18XX] = SCR_PAD2_BOARD_TYPE_DVP_EVB, -}; - static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ 15, /* WL18XX_CONF_HW_RXTX_RATE_MCS15 */ @@ -604,13 +598,8 @@ out: static void wl18xx_set_clk(struct wl1271 *wl) { - struct wl18xx_priv *priv = wl->priv; u32 clk_freq; - /* write the translated board type to SCR_PAD2 */ - wl1271_write32(wl, WL18XX_SCR_PAD2, - wl18xx_board_type_to_scrpad2[priv->board_type]); - wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); /* TODO: PG2: apparently we need to read the clk type */ @@ -1046,21 +1035,22 @@ int __devinit wl18xx_probe(struct platform_device *pdev) sizeof(wl18xx_mimo_ht_cap)); if (!board_type_param) { - board_type_param = kstrdup("dvp_evb", GFP_KERNEL); - priv->board_type = BOARD_TYPE_DVP_EVB_18XX; + board_type_param = kstrdup("dvp", GFP_KERNEL); + priv->board_type = BOARD_TYPE_DVP_18XX; + } else if (!strcmp(board_type_param, "fpga")) { + priv->board_type = BOARD_TYPE_FPGA_18XX; + } else if (!strcmp(board_type_param, "hdk")) { + priv->board_type = BOARD_TYPE_HDK_18XX; + } else if (!strcmp(board_type_param, "dvp")) { + priv->board_type = BOARD_TYPE_DVP_18XX; + } else if (!strcmp(board_type_param, "evb")) { + priv->board_type = BOARD_TYPE_EVB_18XX; + } else if (!strcmp(board_type_param, "com8")) { + priv->board_type = BOARD_TYPE_COM8_18XX; } else { - if (!strcmp(board_type_param, "fpga")) - priv->board_type = BOARD_TYPE_FPGA_18XX; - else if (!strcmp(board_type_param, "hdk")) - priv->board_type = BOARD_TYPE_HDK_18XX; - else if (!strcmp(board_type_param, "dvp_evb")) - priv->board_type = BOARD_TYPE_DVP_EVB_18XX; - else { - wl1271_error("invalid board type '%s'", - board_type_param); - wlcore_free_hw(wl); - return -EINVAL; - } + wl1271_error("invalid board type '%s'", board_type_param); + wlcore_free_hw(wl); + return -EINVAL; } wl18xx_conf_init(wl); @@ -1100,7 +1090,8 @@ module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or mimo"); module_param_named(board_type, board_type_param, charp, S_IRUSR); -MODULE_PARM_DESC(board_type, "Board type: fpga, hdk or dvp_evb (default)"); +MODULE_PARM_DESC(board_type, "Board type: fpga, hdk, evb, com8 or " + "dvp (default)"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 43c480fb837f..1674c1ad002e 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -176,20 +176,15 @@ enum { }; enum { - BOARD_TYPE_FPGA_18XX = 0, - BOARD_TYPE_HDK_18XX = 1, - BOARD_TYPE_DVP_EVB_18XX = 2, + BOARD_TYPE_EVB_18XX = 0, + BOARD_TYPE_DVP_18XX = 1, + BOARD_TYPE_HDK_18XX = 2, + BOARD_TYPE_FPGA_18XX = 3, + BOARD_TYPE_COM8_18XX = 4, NUM_BOARD_TYPES, }; -/* board type values used by the firmware in the SCR_PAD2 register */ -enum { - SCR_PAD2_BOARD_TYPE_FPGA = 0xB1, - SCR_PAD2_BOARD_TYPE_HDK = 0xB2, - SCR_PAD2_BOARD_TYPE_DVP_EVB = 0xB3, -}; - struct wl18xx_mac_and_phy_params { u8 phy_standalone; u8 rdl; -- cgit v1.2.3 From c331b344d5def33194427b24cb2190ff345f7f55 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:49 +0300 Subject: wlcore/wl12xx: add plt_init op and move the code to wl12xx PLT mode needs to be initialized differently for each chip. This patch adds an operation to init PLT and moves the existing PLT initialization into the wl12xx driver. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/main.c | 57 +++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/acx.c | 4 +++ drivers/net/wireless/ti/wlcore/cmd.c | 1 + drivers/net/wireless/ti/wlcore/main.c | 59 +-------------------------------- drivers/net/wireless/ti/wlcore/wlcore.h | 1 + 5 files changed, 64 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 02668557fef0..1d097c1bcace 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1302,10 +1302,67 @@ static void wl12xx_set_tx_desc_csum(struct wl1271 *wl, desc->wl12xx_reserved = 0; } +static int wl12xx_plt_init(struct wl1271 *wl) +{ + int ret; + + ret = wl->ops->boot(wl); + if (ret < 0) + goto out; + + ret = wl->ops->hw_init(wl); + if (ret < 0) + goto out_irq_disable; + + ret = wl1271_acx_init_mem_config(wl); + if (ret < 0) + goto out_irq_disable; + + ret = wl12xx_acx_mem_cfg(wl); + if (ret < 0) + goto out_free_memmap; + + /* Enable data path */ + ret = wl1271_cmd_data_path(wl, 1); + if (ret < 0) + goto out_free_memmap; + + /* Configure for CAM power saving (ie. always active) */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + goto out_free_memmap; + + /* configure PM */ + ret = wl1271_acx_pm_config(wl); + if (ret < 0) + goto out_free_memmap; + + goto out; + +out_free_memmap: + kfree(wl->target_mem_map); + wl->target_mem_map = NULL; + +out_irq_disable: + mutex_unlock(&wl->mutex); + /* Unlocking the mutex in the middle of handling is + inherently unsafe. In this case we deem it safe to do, + because we need to let any possibly pending IRQ out of + the system (and while we are WL1271_STATE_OFF the IRQ + work function will not do anything.) Also, any other + possible concurrent operations will fail due to the + current state, hence the wl1271 struct should be safe. */ + wlcore_disable_interrupts(wl); + mutex_lock(&wl->mutex); +out: + return ret; +} + static struct wlcore_ops wl12xx_ops = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, .boot = wl12xx_boot, + .plt_init = wl12xx_plt_init, .trigger_cmd = wl12xx_trigger_cmd, .ack_event = wl12xx_ack_event, .calc_tx_blocks = wl12xx_calc_tx_blocks, diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index f3d6fa508269..fd33be7d5c94 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -86,6 +86,7 @@ out: kfree(auth); return ret; } +EXPORT_SYMBOL_GPL(wl1271_acx_sleep_auth); int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif, int power) @@ -997,6 +998,7 @@ out: kfree(mem_conf); return ret; } +EXPORT_SYMBOL_GPL(wl12xx_acx_mem_cfg); int wl1271_acx_init_mem_config(struct wl1271 *wl) { @@ -1027,6 +1029,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) return 0; } +EXPORT_SYMBOL_GPL(wl1271_acx_init_mem_config); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl) { @@ -1150,6 +1153,7 @@ out: kfree(acx); return ret; } +EXPORT_SYMBOL_GPL(wl1271_acx_pm_config); int wl1271_acx_keep_alive_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 0dec46586ad5..5c4756047098 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -816,6 +816,7 @@ out: kfree(cmd); return ret; } +EXPORT_SYMBOL_GPL(wl1271_cmd_data_path); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ps_mode, u16 auto_ps_timeout) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index cd73dfe42448..373be7f667ed 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -320,46 +320,6 @@ static void wlcore_adjust_conf(struct wl1271 *wl) } } -static int wl1271_plt_init(struct wl1271 *wl) -{ - int ret; - - ret = wl->ops->hw_init(wl); - if (ret < 0) - return ret; - - ret = wl1271_acx_init_mem_config(wl); - if (ret < 0) - return ret; - - ret = wl12xx_acx_mem_cfg(wl); - if (ret < 0) - goto out_free_memmap; - - /* Enable data path */ - ret = wl1271_cmd_data_path(wl, 1); - if (ret < 0) - goto out_free_memmap; - - /* Configure for CAM power saving (ie. always active) */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); - if (ret < 0) - goto out_free_memmap; - - /* configure PM */ - ret = wl1271_acx_pm_config(wl); - if (ret < 0) - goto out_free_memmap; - - return 0; - - out_free_memmap: - kfree(wl->target_mem_map); - wl->target_mem_map = NULL; - - return ret; -} - static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, u8 tx_pkts) @@ -1042,14 +1002,10 @@ int wl1271_plt_start(struct wl1271 *wl) if (ret < 0) goto power_off; - ret = wl->ops->boot(wl); + ret = wl->ops->plt_init(wl); if (ret < 0) goto power_off; - ret = wl1271_plt_init(wl); - if (ret < 0) - goto irq_disable; - wl->plt = true; wl->state = WL1271_STATE_ON; wl1271_notice("firmware booted in PLT mode (%s)", @@ -1062,19 +1018,6 @@ int wl1271_plt_start(struct wl1271 *wl) goto out; -irq_disable: - mutex_unlock(&wl->mutex); - /* Unlocking the mutex in the middle of handling is - inherently unsafe. In this case we deem it safe to do, - because we need to let any possibly pending IRQ out of - the system (and while we are WL1271_STATE_OFF the IRQ - work function will not do anything.) Also, any other - possible concurrent operations will fail due to the - current state, hence the wl1271 struct should be safe. */ - wlcore_disable_interrupts(wl); - wl1271_flush_deferred_work(wl); - cancel_work_sync(&wl->netstack_work); - mutex_lock(&wl->mutex); power_off: wl1271_power_off(wl); } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index f62152e35f3c..1fc3c77c10a3 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -39,6 +39,7 @@ struct wlcore_ops { int (*identify_chip)(struct wl1271 *wl); int (*identify_fw)(struct wl1271 *wl); int (*boot)(struct wl1271 *wl); + int (*plt_init)(struct wl1271 *wl); void (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len); void (*ack_event)(struct wl1271 *wl); -- cgit v1.2.3 From be42aee6df93597f669b4d20e7a32c8fc144f104 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:50 +0300 Subject: wl18xx: add plt_init operation Add the correct FW name for PLT (which is the same as the normal firmware) and implement the plt_init operation. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 10 ++++++++++ drivers/net/wireless/ti/wl18xx/reg.h | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f892d37443e2..59408291d30d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -580,6 +580,8 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)", wl->chip.id); wl->sr_fw_name = WL18XX_FW_NAME; + /* wl18xx uses the same firmware for PLT */ + wl->plt_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; @@ -958,9 +960,17 @@ static void wl18xx_conf_init(struct wl1271 *wl) memcpy(&priv->conf, &wl18xx_default_priv_conf, sizeof(priv->conf)); } +static int wl18xx_plt_init(struct wl1271 *wl) +{ + wl1271_write32(wl, WL18XX_SCR_PAD8, WL18XX_SCR_PAD8_PLT); + + return wl->ops->boot(wl); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, + .plt_init = wl18xx_plt_init, .trigger_cmd = wl18xx_trigger_cmd, .ack_event = wl18xx_ack_event, .calc_tx_blocks = wl18xx_calc_tx_blocks, diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 1674c1ad002e..aaedf91ed7b7 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -154,6 +154,12 @@ */ #define WL18XX_INTR_TRIG_EVENT_ACK BIT(29) +/* + * To boot the firmware in PLT mode we need to write this value in + * SCR_PAD8 before starting. + */ +#define WL18XX_SCR_PAD8_PLT 0xBABABEBE + /* TODO: maybe move elsewhere? */ #define NUM_OF_CHANNELS_11_ABG 150 #define NUM_OF_CHANNELS_11_P 7 -- cgit v1.2.3 From 0a1569f82b5ee4281c5c71e7bb4a285267158ce3 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:51 +0300 Subject: wl18xx: change the low_band_component_type for HDK boards Currently, all HDK boards use low_band_component_type 0x06. In the future this may change, but for now, we hardcode this value when the board_type is set to HDK. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 59408291d30d..f14141b3dbe4 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1051,6 +1051,8 @@ int __devinit wl18xx_probe(struct platform_device *pdev) priv->board_type = BOARD_TYPE_FPGA_18XX; } else if (!strcmp(board_type_param, "hdk")) { priv->board_type = BOARD_TYPE_HDK_18XX; + /* HACK! Just for now we hardcode HDK to 0x06 */ + priv->conf.phy.low_band_component_type = 0x06; } else if (!strcmp(board_type_param, "dvp")) { priv->board_type = BOARD_TYPE_DVP_18XX; } else if (!strcmp(board_type_param, "evb")) { -- cgit v1.2.3 From e9258815a8e21e34395d5b6a4da27f1bfcbdca11 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:52 +0300 Subject: wl18xx: add number of antennas and dc2dc type as module params We need to specify the number of 2.4GHz and 5GHz antennas and whether the board has an internal or external DC2DC. Add some module parameters that allow changing that. In the future this will come from the "NVS" file. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/conf.h | 3 --- drivers/net/wireless/ti/wl18xx/main.c | 21 +++++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index d2b097e4d24e..a6058fb86e7f 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -33,9 +33,6 @@ struct wl18xx_conf_phy { u8 low_band_component_type; u8 high_band_component; u8 high_band_component_type; - u8 number_of_assembled_ant2_4; - u8 number_of_assembled_ant5; - u8 external_pa_dc2dc; u8 tcxo_ldo_voltage; u8 xtal_itrim_val; u8 srf_state; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f14141b3dbe4..2f0cbf8f202c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -44,6 +44,9 @@ static char *ht_mode_param; static char *board_type_param; +static bool dc2dc_param = false; +static int n_antennas_2_param = 1; +static int n_antennas_5_param = 1; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -487,9 +490,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .low_band_component_type = 0x05, .high_band_component = COMPONENT_2_WAY_SWITCH, .high_band_component_type = 0x09, - .number_of_assembled_ant2_4 = 0x01, - .number_of_assembled_ant5 = 0x01, - .external_pa_dc2dc = 0x00, .tcxo_ldo_voltage = 0x00, .xtal_itrim_val = 0x04, .srf_state = 0x00, @@ -704,10 +704,10 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) params.high_band_component_type = phy->high_band_component_type; params.number_of_assembled_ant2_4 = - phy->number_of_assembled_ant2_4; + n_antennas_2_param; params.number_of_assembled_ant5 = - phy->number_of_assembled_ant5; - params.external_pa_dc2dc = phy->external_pa_dc2dc; + n_antennas_5_param; + params.external_pa_dc2dc = dc2dc_param; params.tcxo_ldo_voltage = phy->tcxo_ldo_voltage; params.xtal_itrim_val = phy->xtal_itrim_val; params.srf_state = phy->srf_state; @@ -1105,6 +1105,15 @@ module_param_named(board_type, board_type_param, charp, S_IRUSR); MODULE_PARM_DESC(board_type, "Board type: fpga, hdk, evb, com8 or " "dvp (default)"); +module_param_named(dc2dc, dc2dc_param, bool, S_IRUSR); +MODULE_PARM_DESC(dc2dc, "External DC2DC: boolean (defaults to false)"); + +module_param_named(n_antennas_2, n_antennas_2_param, uint, S_IRUSR); +MODULE_PARM_DESC(n_antennas_2, "Number of installed 2.4GHz antennas: 1 (default) or 2"); + +module_param_named(n_antennas_5, n_antennas_5_param, uint, S_IRUSR); +MODULE_PARM_DESC(n_antennas_5, "Number of installed 5GHz antennas: 1 (default) or 2"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From 102165c6d2b8d73a25a1567fcac496addc15aba7 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:53 +0300 Subject: wl18xx: add module parameter to disable TCP checksum Add a new module parameter to disable TCP checksum offload. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 2f0cbf8f202c..b76b6ac348da 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -47,6 +47,7 @@ static char *board_type_param; static bool dc2dc_param = false; static int n_antennas_2_param = 1; static int n_antennas_5_param = 1; +static bool checksum_param = true; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -865,9 +866,11 @@ static int wl18xx_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl18xx_acx_set_checksum_state(wl); - if (ret != 0) - return ret; + if (checksum_param) { + ret = wl18xx_acx_set_checksum_state(wl); + if (ret != 0) + return ret; + } return ret; } @@ -879,6 +882,11 @@ static void wl18xx_set_tx_desc_csum(struct wl1271 *wl, u32 ip_hdr_offset; struct iphdr *ip_hdr; + if (!checksum_param) { + desc->wl18xx_checksum_data = 0; + return; + } + if (skb->ip_summed != CHECKSUM_PARTIAL) { desc->wl18xx_checksum_data = 0; return; @@ -1065,6 +1073,11 @@ int __devinit wl18xx_probe(struct platform_device *pdev) return -EINVAL; } + if (!checksum_param) { + wl18xx_ops.set_rx_csum = NULL; + wl18xx_ops.init_vif = NULL; + } + wl18xx_conf_init(wl); return wlcore_probe(wl, pdev); @@ -1114,6 +1127,9 @@ MODULE_PARM_DESC(n_antennas_2, "Number of installed 2.4GHz antennas: 1 (default) module_param_named(n_antennas_5, n_antennas_5_param, uint, S_IRUSR); MODULE_PARM_DESC(n_antennas_5, "Number of installed 5GHz antennas: 1 (default) or 2"); +module_param_named(checksum, checksum_param, bool, S_IRUSR); +MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to true)"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From 0afd04e5e5d8db505fbb0f98c6306242c0006503 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 10 May 2012 12:13:54 +0300 Subject: wlcore/wl12xx/18xx: split fw_status struct into two The number of RX packet descriptors may vary from chip to chip and in different firmware versions. Unfortunately, the array that contains the actual descriptors is in the middle of the fw_status structure. To manage this, we split the struct into two so we can calculate the offset of what comes after the array and access the last elements more easily. [Changed the STATUS_LEN macro to be placement agnostic - Arik] Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/main.c | 4 +- drivers/net/wireless/ti/wl18xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/tx.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 64 +++++++++++++++++-------------- drivers/net/wireless/ti/wlcore/rx.c | 10 ++--- drivers/net/wireless/ti/wlcore/rx.h | 4 +- drivers/net/wireless/ti/wlcore/wlcore.h | 5 ++- drivers/net/wireless/ti/wlcore/wlcore_i.h | 18 +++++++-- 8 files changed, 66 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 1d097c1bcace..82e0c6a6ac89 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1156,7 +1156,8 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, static void wl12xx_tx_delayed_compl(struct wl1271 *wl) { - if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff)) + if (wl->fw_status_1->tx_results_counter == + (wl->tx_results_count & 0xff)) return; wl1271_tx_complete(wl); @@ -1414,6 +1415,7 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) wl->ptable = wl12xx_ptable; wl->rtable = wl12xx_rtable; wl->num_tx_desc = 16; + wl->num_rx_desc = 8; wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE; wl->band_rate_to_idx = wl12xx_band_rate_to_idx; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index b76b6ac348da..df407be9ed44 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1041,6 +1041,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->ptable = wl18xx_ptable; wl->rtable = wl18xx_rtable; wl->num_tx_desc = 32; + wl->num_rx_desc = 16; wl->normal_tx_spare = WL18XX_TX_HW_BLOCK_SPARE; wl->gem_tx_spare = WL18XX_TX_HW_GEM_BLOCK_SPARE; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c index 4e12f56b394f..4de00b901505 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.c +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -94,7 +94,7 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) void wl18xx_tx_immediate_complete(struct wl1271 *wl) { struct wl18xx_fw_status_priv *status_priv = - (struct wl18xx_fw_status_priv *)wl->fw_status->priv; + (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; struct wl18xx_priv *priv = wl->priv; u8 i; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 373be7f667ed..395da0b3377b 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -347,7 +347,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, static void wl12xx_irq_update_links_status(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct wl_fw_status *status) + struct wl_fw_status_2 *status) { struct wl1271_link *lnk; u32 cur_fw_ps_map; @@ -379,7 +379,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, } static void wl12xx_fw_status(struct wl1271 *wl, - struct wl_fw_status *status) + struct wl_fw_status_1 *status_1, + struct wl_fw_status_2 *status_2) { struct wl12xx_vif *wlvif; struct timespec ts; @@ -388,37 +389,38 @@ static void wl12xx_fw_status(struct wl1271 *wl, int i; size_t status_len; - status_len = sizeof(*status) + wl->fw_status_priv_len; + status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + + sizeof(*status_2) + wl->fw_status_priv_len; - wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status, + wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1, status_len, false); wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", - status->intr, - status->fw_rx_counter, - status->drv_rx_counter, - status->tx_results_counter); + status_1->intr, + status_1->fw_rx_counter, + status_1->drv_rx_counter, + status_1->tx_results_counter); for (i = 0; i < NUM_TX_QUEUES; i++) { /* prevent wrap-around in freed-packets counter */ wl->tx_allocated_pkts[i] -= - (status->counters.tx_released_pkts[i] - + (status_2->counters.tx_released_pkts[i] - wl->tx_pkts_freed[i]) & 0xff; - wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i]; + wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i]; } /* prevent wrap-around in total blocks counter */ if (likely(wl->tx_blocks_freed <= - le32_to_cpu(status->total_released_blks))) - freed_blocks = le32_to_cpu(status->total_released_blks) - + le32_to_cpu(status_2->total_released_blks))) + freed_blocks = le32_to_cpu(status_2->total_released_blks) - wl->tx_blocks_freed; else freed_blocks = 0x100000000LL - wl->tx_blocks_freed + - le32_to_cpu(status->total_released_blks); + le32_to_cpu(status_2->total_released_blks); - wl->tx_blocks_freed = le32_to_cpu(status->total_released_blks); + wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks); wl->tx_allocated_blocks -= freed_blocks; @@ -434,7 +436,7 @@ static void wl12xx_fw_status(struct wl1271 *wl, cancel_delayed_work(&wl->tx_watchdog_work); } - avail = le32_to_cpu(status->tx_total) - wl->tx_allocated_blocks; + avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks; /* * The FW might change the total number of TX memblocks before @@ -453,13 +455,13 @@ static void wl12xx_fw_status(struct wl1271 *wl, /* for AP update num of allocated TX blocks per link and ps status */ wl12xx_for_each_wlvif_ap(wl, wlvif) { - wl12xx_irq_update_links_status(wl, wlvif, status); + wl12xx_irq_update_links_status(wl, wlvif, status_2); } /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - - (s64)le32_to_cpu(status->fw_localtime); + (s64)le32_to_cpu(status_2->fw_localtime); } static void wl1271_flush_deferred_work(struct wl1271 *wl) @@ -528,11 +530,11 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); smp_mb__after_clear_bit(); - wl12xx_fw_status(wl, wl->fw_status); + wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2); wlcore_hw_tx_immediate_compl(wl); - intr = le32_to_cpu(wl->fw_status->intr); + intr = le32_to_cpu(wl->fw_status_1->intr); intr &= WL1271_INTR_MASK; if (!intr) { done = true; @@ -551,7 +553,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (likely(intr & WL1271_ACX_INTR_DATA)) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); - wl12xx_rx(wl, wl->fw_status); + wl12xx_rx(wl, wl->fw_status_1); /* Check if any tx blocks were freed */ spin_lock_irqsave(&wl->wl_lock, flags); @@ -786,8 +788,8 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ - wl12xx_fw_status(wl, wl->fw_status); - first_addr = le32_to_cpu(wl->fw_status->log_start_addr); + wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr); if (!first_addr) goto out; @@ -901,13 +903,18 @@ static void wl1271_fw_wakeup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) { - wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL); - if (!wl->fw_status) + wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + + sizeof(*wl->fw_status_2), GFP_KERNEL); + if (!wl->fw_status_1) return -ENOMEM; + wl->fw_status_2 = (struct wl_fw_status_2 *) + (((u8 *) wl->fw_status_1) + + WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); + wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); if (!wl->tx_res_if) { - kfree(wl->fw_status); + kfree(wl->fw_status_1); return -ENOMEM; } @@ -1746,8 +1753,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl1271_debugfs_reset(wl); - kfree(wl->fw_status); - wl->fw_status = NULL; + kfree(wl->fw_status_1); + wl->fw_status_1 = NULL; + wl->fw_status_2 = NULL; kfree(wl->tx_res_if); wl->tx_res_if = NULL; kfree(wl->target_mem_map); @@ -5181,7 +5189,7 @@ int wlcore_free_hw(struct wl1271 *wl) kfree(wl->nvs); wl->nvs = NULL; - kfree(wl->fw_status); + kfree(wl->fw_status_1); kfree(wl->tx_res_if); destroy_workqueue(wl->freezable_wq); diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 2672b1d16cef..78200dcacfca 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -200,12 +200,12 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, return is_data; } -void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status) +void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) { unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; u32 buf_size; - u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; - u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc; + u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc; u32 rx_counter; u32 pkt_len, align_pkt_len; u32 pkt_offset, des; @@ -224,7 +224,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status) break; buf_size += align_pkt_len; rx_counter++; - rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; + rx_counter %= wl->num_rx_desc; } if (buf_size == 0) { @@ -264,7 +264,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status) wl->rx_counter++; drv_rx_counter++; - drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; + drv_rx_counter %= wl->num_rx_desc; pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len); } } diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 277936e14137..9be780179456 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -38,8 +38,6 @@ #define RX_DESC_PACKETID_SHIFT 11 #define RX_MAX_PACKET_ID 3 -#define NUM_RX_PKT_DESC_MOD_MASK 7 - #define RX_DESC_VALID_FCS 0x0001 #define RX_DESC_MATCH_RXADDR1 0x0002 #define RX_DESC_MCAST 0x0004 @@ -139,7 +137,7 @@ struct wl1271_rx_descriptor { u8 reserved; } __packed; -void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status); +void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); int wl1271_rx_filter_enable(struct wl1271 *wl, int index, bool enable, diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 1fc3c77c10a3..9ca382918184 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -269,7 +269,8 @@ struct wl1271 { u32 buffer_cmd; u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; - struct wl_fw_status *fw_status; + struct wl_fw_status_1 *fw_status_1; + struct wl_fw_status_2 *fw_status_2; struct wl1271_tx_hw_res_if *tx_res_if; /* Current chipset configuration */ @@ -337,6 +338,8 @@ struct wl1271 { /* number of TX descriptors the HW supports. */ u32 num_tx_desc; + /* number of RX descriptors the HW supports. */ + u32 num_rx_desc; /* spare Tx blocks for normal/GEM operating modes */ u32 normal_tx_spare; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 53263a978a75..0422753f673c 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -141,7 +141,6 @@ struct wl1271_stats { }; #define NUM_TX_QUEUES 4 -#define NUM_RX_PKT_DESC 8 #define AP_MAX_STATIONS 8 @@ -159,13 +158,26 @@ struct wl_fw_packet_counters { } __packed; /* FW status registers */ -struct wl_fw_status { +struct wl_fw_status_1 { __le32 intr; u8 fw_rx_counter; u8 drv_rx_counter; u8 reserved; u8 tx_results_counter; - __le32 rx_pkt_descs[NUM_RX_PKT_DESC]; + __le32 rx_pkt_descs[0]; +} __packed; + +/* + * Each HW arch has a different number of Rx descriptors. + * The length of the status depends on it, since it holds an array + * of descriptors. + */ +#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \ + (sizeof(struct wl_fw_status_1) + \ + (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \ + num_rx_desc) + +struct wl_fw_status_2 { __le32 fw_localtime; /* -- cgit v1.2.3 From a5114d9c0def1286aa1d565280b350fee4d1c75b Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:55 +0300 Subject: wl18xx: derive the MAC address from the BD_ADDR in fuse ROM Add the get_mac operation in order to fetch the BD_ADDR from fuse ROM, so that we can derive the WLAN MAC addresses from it. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 18 ++++++++++++++++++ drivers/net/wireless/ti/wl18xx/reg.h | 3 +++ 2 files changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index df407be9ed44..52d63ff1d352 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -975,6 +975,23 @@ static int wl18xx_plt_init(struct wl1271 *wl) return wl->ops->boot(wl); } +static void wl18xx_get_mac(struct wl1271 *wl) +{ + u32 mac1, mac2; + + wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + + mac1 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1); + mac2 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_2); + + /* these are the two parts of the BD_ADDR */ + wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + + ((mac1 & 0xff000000) >> 24); + wl->fuse_nic_addr = (mac1 & 0xffffff); + + wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -994,6 +1011,7 @@ static struct wlcore_ops wl18xx_ops = { .set_rx_csum = wl18xx_set_rx_csum, .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, + .get_mac = wl18xx_get_mac, }; /* HT cap appropriate for wide channels */ diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index aaedf91ed7b7..43863d35feb2 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -132,6 +132,9 @@ #define WL18XX_PG_VER_MASK 0x70 #define WL18XX_PG_VER_OFFSET 4 +#define WL18XX_REG_FUSE_BD_ADDR_1 0xA02602 +#define WL18XX_REG_FUSE_BD_ADDR_2 0xA02606 + #define WL18XX_CMD_MBOX_ADDRESS 0xB007B4 #define WL18XX_FW_STATUS_ADDR 0x50F8 -- cgit v1.2.3 From 1ddbc7d4c744eb24aa0d51ab1f4ad6b5c0fd0c02 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:56 +0300 Subject: wl18xx: add a module parameter to control 11a support In wl18xx we don't have an NVS yet, so we need to control whether 11a is supported with a module parameter for now. 11a support is enabled by default and can be changed by setting enable_11a to false when loading the module. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 52d63ff1d352..d4e331fb73e3 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -48,6 +48,7 @@ static bool dc2dc_param = false; static int n_antennas_2_param = 1; static int n_antennas_5_param = 1; static bool checksum_param = true; +static bool enable_11a_param = true; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -1097,6 +1098,8 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl18xx_ops.init_vif = NULL; } + wl->enable_11a = enable_11a_param; + wl18xx_conf_init(wl); return wlcore_probe(wl, pdev); @@ -1149,6 +1152,9 @@ MODULE_PARM_DESC(n_antennas_5, "Number of installed 5GHz antennas: 1 (default) o module_param_named(checksum, checksum_param, bool, S_IRUSR); MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to true)"); +module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR); +MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From df472894ab21814edf54a3cd81c8ae7066365e0e Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:57 +0300 Subject: wlcore: add module parameter to dump SDIO reads and writes In some cases we may want to dump all the SDIO data in order to get detailed information for debugging the communication with the firmware. Add printks to dump the data and a module parameter to enable/disable it. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/sdio.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 1082e394e8f7..c67ec482e445 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "wlcore.h" #include "wl12xx_80211.h" @@ -46,6 +47,8 @@ #define SDIO_DEVICE_ID_TI_WL1271 0x4076 #endif +static bool dump = false; + struct wl12xx_sdio_glue { struct device *dev; struct platform_device *core; @@ -77,6 +80,13 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, sdio_claim_host(func); + if (unlikely(dump)) { + printk(KERN_DEBUG "wlcore_sdio: READ from 0x%04x\n", addr); + print_hex_dump(KERN_DEBUG, "wlcore_sdio: READ ", + DUMP_PREFIX_OFFSET, 16, 1, + buf, len, false); + } + if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n", @@ -106,6 +116,13 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, sdio_claim_host(func); + if (unlikely(dump)) { + printk(KERN_DEBUG "wlcore_sdio: WRITE to 0x%04x\n", addr); + print_hex_dump(KERN_DEBUG, "wlcore_sdio: WRITE ", + DUMP_PREFIX_OFFSET, 16, 1, + buf, len, false); + } + if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n", @@ -380,6 +397,9 @@ static void __exit wl1271_exit(void) module_init(wl1271_init); module_exit(wl1271_exit); +module_param(dump, bool, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(dump, "Enable sdio read/write dumps."); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); -- cgit v1.2.3 From 776f030aa0ed65fbc266219fb858bbfc909d97d2 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:58 +0300 Subject: wl18xx: copy the default configuration before checking the board_type We were changing the low_band_component_type in the private configuration structure before copying the default values to it, so the change was overwritten and only took effect after HW recovery. Reported-by: Dror Erez Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index d4e331fb73e3..f5ec6f60db33 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1072,6 +1072,8 @@ int __devinit wl18xx_probe(struct platform_device *pdev) memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, sizeof(wl18xx_mimo_ht_cap)); + wl18xx_conf_init(wl); + if (!board_type_param) { board_type_param = kstrdup("dvp", GFP_KERNEL); priv->board_type = BOARD_TYPE_DVP_18XX; @@ -1100,8 +1102,6 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->enable_11a = enable_11a_param; - wl18xx_conf_init(wl); - return wlcore_probe(wl, pdev); } -- cgit v1.2.3 From 4b9d2365d51e9726cd288dbeebf20a80d3ac8a38 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:13:59 +0300 Subject: wl18xx: changed default board_type to HDK Since the most common board we're using now is an HDK board, change the default board_type to hdk. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f5ec6f60db33..aa227a17187e 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -43,7 +43,7 @@ #define WL18XX_RX_CHECKSUM_MASK 0x40 static char *ht_mode_param; -static char *board_type_param; +static char *board_type_param = "hdk"; static bool dc2dc_param = false; static int n_antennas_2_param = 1; static int n_antennas_5_param = 1; @@ -1074,10 +1074,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl18xx_conf_init(wl); - if (!board_type_param) { - board_type_param = kstrdup("dvp", GFP_KERNEL); - priv->board_type = BOARD_TYPE_DVP_18XX; - } else if (!strcmp(board_type_param, "fpga")) { + if (!strcmp(board_type_param, "fpga")) { priv->board_type = BOARD_TYPE_FPGA_18XX; } else if (!strcmp(board_type_param, "hdk")) { priv->board_type = BOARD_TYPE_HDK_18XX; @@ -1137,8 +1134,8 @@ module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or mimo"); module_param_named(board_type, board_type_param, charp, S_IRUSR); -MODULE_PARM_DESC(board_type, "Board type: fpga, hdk, evb, com8 or " - "dvp (default)"); +MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or " + "dvp"); module_param_named(dc2dc, dc2dc_param, bool, S_IRUSR); MODULE_PARM_DESC(dc2dc, "External DC2DC: boolean (defaults to false)"); -- cgit v1.2.3 From 4f64a1e93f4749ea2ecacb7e93485ef9607b4e48 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:00 +0300 Subject: wlcore: add space for private area when allocating fw_status When allocating the fw_status structure, the space for the lower-driver private area was missing. This was causing an illegal write outside the allocated area, which was causing random oopses around the kernel. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 395da0b3377b..bc33ee25f7d1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -904,7 +904,8 @@ static void wl1271_fw_wakeup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) { wl->fw_status_1 = kmalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + - sizeof(*wl->fw_status_2), GFP_KERNEL); + sizeof(*wl->fw_status_2) + + wl->fw_status_priv_len, GFP_KERNEL); if (!wl->fw_status_1) return -ENOMEM; -- cgit v1.2.3 From 73d8a42464d3fd8de907d032a7dafa1d23d88490 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:01 +0300 Subject: wl18xx: change low_band_component_type value for COM8 Like HDK, COM8 uses 0x06 for the low_band_component_type as well. Hardcode the value for COM8 until this configuration can be done in the external configuration binary (to be implemented). Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index aa227a17187e..6047a6d90975 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1086,6 +1086,8 @@ int __devinit wl18xx_probe(struct platform_device *pdev) priv->board_type = BOARD_TYPE_EVB_18XX; } else if (!strcmp(board_type_param, "com8")) { priv->board_type = BOARD_TYPE_COM8_18XX; + /* HACK! Just for now we hardcode COM8 to 0x06 */ + priv->conf.phy.low_band_component_type = 0x06; } else { wl1271_error("invalid board type '%s'", board_type_param); wlcore_free_hw(wl); -- cgit v1.2.3 From 4afc37a0c1c58415ac3ad1c07afd8ebf81cb90c5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:02 +0300 Subject: wlcore: reorder identify_chip and get_hw_info We can do get the HW information and identify the chip already at probe time. This makes it easier to mangle with the parameters during identify chip before registering the hardware with mac80211. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/main.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index bc33ee25f7d1..2e77f731c0a1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -962,10 +962,6 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt) if (wl1271_set_block_size(wl)) wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; - ret = wl->ops->identify_chip(wl); - if (ret < 0) - goto out; - /* TODO: make sure the lower driver has set things up correctly */ ret = wl1271_setup(wl); @@ -4872,12 +4868,6 @@ static int wl1271_register_hw(struct wl1271 *wl) if (wl->mac80211_registered) return 0; - ret = wl12xx_get_hw_info(wl); - if (ret < 0) { - wl1271_error("couldn't get hw info"); - goto out; - } - ret = wl1271_fetch_nvs(wl); if (ret == 0) { /* NOTE: The wl->nvs->nvs element must be first, in @@ -5282,6 +5272,16 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) } disable_irq(wl->irq); + ret = wl12xx_get_hw_info(wl); + if (ret < 0) { + wl1271_error("couldn't get hw info"); + goto out; + } + + ret = wl->ops->identify_chip(wl); + if (ret < 0) + goto out; + ret = wl1271_init_ieee80211(wl); if (ret) goto out_irq; -- cgit v1.2.3 From 7ae25da3967298199881c72ee476a1f9ec682fd8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:03 +0300 Subject: wl18xx: disable MCS_13 for wl18xx PG 1.0 There are some problems with MCS_13 in PG 1.0 hardware. So we disable it when PG 1.0 is detected. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 6047a6d90975..afa2334d0aea 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -588,6 +588,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; + /* PG 1.0 has some problems with MCS_13, so disable it */ + wl->ht_cap.mcs.rx_mask[1] &= ~BIT(5); + /* TODO: need to blocksize alignment for RX/TX separately? */ break; default: @@ -914,6 +917,10 @@ static void wl18xx_set_rx_csum(struct wl1271 *wl, skb->ip_summed = CHECKSUM_UNNECESSARY; } +/* + * TODO: instead of having these two functions to get the rate mask, + * we should modify the wlvif->rate_set instead + */ static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { @@ -940,6 +947,17 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, return CONF_TX_RATE_USE_WIDE_CHAN; } else { wl1271_debug(DEBUG_ACX, "using MIMO rate mask"); + + /* + * PG 1.0 has some problems with MCS_13, so disable it + * + * TODO: instead of hacking this in here, we should + * make it more general and change a bit in the + * wlvif->rate_set instead. + */ + if (wl->chip.id == CHIP_ID_185x_PG10) + return CONF_TX_MIMO_RATES & ~CONF_HW_BIT_RATE_MCS_13; + return CONF_TX_MIMO_RATES; } } -- cgit v1.2.3 From 62c2e579e008784b929abef27f5728db2a1d88bf Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:04 +0300 Subject: wlcore: update beacon and probe_resp templates when rates change When the data rates change, we get BSS_CHANGED_BASIC_RATES. At this point, we should update all the templates to match the new rates. We were changing some of the templates, but the beacon and the probe response templates were missing. [Remove redundant min_rate variable - Arik] Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/main.c | 173 ++++++++++++++++++++-------------- 1 file changed, 100 insertions(+), 73 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 2e77f731c0a1..c9ccf2757e2a 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3277,8 +3277,15 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates, skb->data, skb->len, 0, rates); - dev_kfree_skb(skb); + + if (ret < 0) + goto out; + + wl1271_debug(DEBUG_AP, "probe response updated"); + set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags); + +out: return ret; } @@ -3383,6 +3390,87 @@ out: return ret; } +static int wlcore_set_beacon_template(struct wl1271 *wl, + struct ieee80211_vif *vif, + bool is_ap) +{ + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct ieee80211_hdr *hdr; + u32 min_rate; + int ret; + int ieoffset = offsetof(struct ieee80211_mgmt, + u.beacon.variable); + struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); + u16 tmpl_id; + + if (!beacon) { + ret = -EINVAL; + goto out; + } + + wl1271_debug(DEBUG_MASTER, "beacon updated"); + + ret = wl1271_ssid_set(vif, beacon, ieoffset); + if (ret < 0) { + dev_kfree_skb(beacon); + goto out; + } + min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); + tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON : + CMD_TEMPL_BEACON; + ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id, + beacon->data, + beacon->len, 0, + min_rate); + if (ret < 0) { + dev_kfree_skb(beacon); + goto out; + } + + /* + * In case we already have a probe-resp beacon set explicitly + * by usermode, don't use the beacon data. + */ + if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags)) + goto end_bcn; + + /* remove TIM ie from probe response */ + wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset); + + /* + * remove p2p ie from probe response. + * the fw reponds to probe requests that don't include + * the p2p ie. probe requests with p2p ie will be passed, + * and will be responded by the supplicant (the spec + * forbids including the p2p ie when responding to probe + * requests that didn't include it). + */ + wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P, ieoffset); + + hdr = (struct ieee80211_hdr *) beacon->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + if (is_ap) + ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif, + beacon->data, + beacon->len, + min_rate); + else + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_PROBE_RESPONSE, + beacon->data, + beacon->len, 0, + min_rate); +end_bcn: + dev_kfree_skb(beacon); + if (ret < 0) + goto out; + +out: + return ret; +} + static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -3401,81 +3489,12 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, if ((changed & BSS_CHANGED_AP_PROBE_RESP) && is_ap) { u32 rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - if (!wl1271_ap_set_probe_resp_tmpl(wl, rate, vif)) { - wl1271_debug(DEBUG_AP, "probe response updated"); - set_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags); - } + + wl1271_ap_set_probe_resp_tmpl(wl, rate, vif); } if ((changed & BSS_CHANGED_BEACON)) { - struct ieee80211_hdr *hdr; - u32 min_rate; - int ieoffset = offsetof(struct ieee80211_mgmt, - u.beacon.variable); - struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); - u16 tmpl_id; - - if (!beacon) { - ret = -EINVAL; - goto out; - } - - wl1271_debug(DEBUG_MASTER, "beacon updated"); - - ret = wl1271_ssid_set(vif, beacon, ieoffset); - if (ret < 0) { - dev_kfree_skb(beacon); - goto out; - } - min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON : - CMD_TEMPL_BEACON; - ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id, - beacon->data, - beacon->len, 0, - min_rate); - if (ret < 0) { - dev_kfree_skb(beacon); - goto out; - } - - /* - * In case we already have a probe-resp beacon set explicitly - * by usermode, don't use the beacon data. - */ - if (test_bit(WLVIF_FLAG_AP_PROBE_RESP_SET, &wlvif->flags)) - goto end_bcn; - - /* remove TIM ie from probe response */ - wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset); - - /* - * remove p2p ie from probe response. - * the fw reponds to probe requests that don't include - * the p2p ie. probe requests with p2p ie will be passed, - * and will be responded by the supplicant (the spec - * forbids including the p2p ie when responding to probe - * requests that didn't include it). - */ - wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA, - WLAN_OUI_TYPE_WFA_P2P, ieoffset); - - hdr = (struct ieee80211_hdr *) beacon->data; - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_RESP); - if (is_ap) - ret = wl1271_ap_set_probe_resp_tmpl_legacy(wl, vif, - beacon->data, - beacon->len, - min_rate); - else - ret = wl1271_cmd_template_set(wl, wlvif->role_id, - CMD_TEMPL_PROBE_RESPONSE, - beacon->data, - beacon->len, 0, - min_rate); -end_bcn: - dev_kfree_skb(beacon); + ret = wlcore_set_beacon_template(wl, vif, is_ap); if (ret < 0) goto out; } @@ -3512,6 +3531,14 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, ret = wl1271_ap_init_templates(wl, vif); if (ret < 0) goto out; + + ret = wl1271_ap_set_probe_resp_tmpl(wl, wlvif->basic_rate, vif); + if (ret < 0) + goto out; + + ret = wlcore_set_beacon_template(wl, vif, true); + if (ret < 0) + goto out; } ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed); -- cgit v1.2.3 From 15e05bc0593328149f872a86fa2bb048b9997402 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:05 +0300 Subject: wlcore: use all AP basic rates as default Sometimes we get a BSS_CHANGED_BEACON_ENABLED event before the basic rates have been properly set. To avoid problems with the firmware not expecting to receive frames at rates that are not set during CMD_START_ROLE, we now start with all basic rates by default. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/main.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index c9ccf2757e2a..9589aa956bf7 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1846,6 +1846,9 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx); wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx); wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx); + wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC; + wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC; + wlvif->rate_set = CONF_TX_RATE_MASK_BASIC; } else { /* init ap data */ wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID; @@ -1855,13 +1858,19 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++) wl12xx_allocate_rate_policy(wl, &wlvif->ap.ucast_rate_idx[i]); + wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES; + /* + * TODO: check if basic_rate shouldn't be + * wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); + * instead (the same thing for STA above). + */ + wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES; + /* TODO: this seems to be used only for STA, check it */ + wlvif->rate_set = CONF_TX_AP_ENABLED_RATES; } wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate; wlvif->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5; - wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC; - wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC; - wlvif->rate_set = CONF_TX_RATE_MASK_BASIC; wlvif->beacon_int = WL1271_DEFAULT_BEACON_INT; /* -- cgit v1.2.3 From 4987257c304bf5d12e7a5cedde1100ba7f14a8a3 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:06 +0300 Subject: wlcore: abstract debugfs fw_stats to be handled by the lower drivers The FW statistics differ from hardware to hardware. This commit prepares for hardware-specific implementation of the FW statistics debugfs entries. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/acx.c | 4 +- drivers/net/wireless/ti/wlcore/acx.h | 224 +----------------------- drivers/net/wireless/ti/wlcore/debugfs.c | 279 +++--------------------------- drivers/net/wireless/ti/wlcore/debugfs.h | 60 +++++++ drivers/net/wireless/ti/wlcore/hw_ops.h | 9 + drivers/net/wireless/ti/wlcore/wlcore.h | 10 ++ drivers/net/wireless/ti/wlcore/wlcore_i.h | 8 - 7 files changed, 104 insertions(+), 490 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index fd33be7d5c94..b9ec42c83757 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -709,14 +709,14 @@ out: return ret; } -int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) +int wl1271_acx_statistics(struct wl1271 *wl, void *stats) { int ret; wl1271_debug(DEBUG_ACX, "acx statistics"); ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats, - sizeof(*stats)); + wl->stats.fw_stats_len); if (ret < 0) { wl1271_warning("acx statistics failed: %d", ret); return -ENOMEM; diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index ff0c5ba8e8ff..8b3807b879d3 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -417,228 +417,6 @@ struct acx_ctsprotect { u8 padding[2]; } __packed; -struct acx_tx_statistics { - __le32 internal_desc_overflow; -} __packed; - -struct acx_rx_statistics { - __le32 out_of_mem; - __le32 hdr_overflow; - __le32 hw_stuck; - __le32 dropped; - __le32 fcs_err; - __le32 xfr_hint_trig; - __le32 path_reset; - __le32 reset_counter; -} __packed; - -struct acx_dma_statistics { - __le32 rx_requested; - __le32 rx_errors; - __le32 tx_requested; - __le32 tx_errors; -} __packed; - -struct acx_isr_statistics { - /* host command complete */ - __le32 cmd_cmplt; - - /* fiqisr() */ - __le32 fiqs; - - /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ - __le32 rx_headers; - - /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ - __le32 rx_completes; - - /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ - __le32 rx_mem_overflow; - - /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ - __le32 rx_rdys; - - /* irqisr() */ - __le32 irqs; - - /* (INT_STS_ND & INT_TRIG_TX_PROC) */ - __le32 tx_procs; - - /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ - __le32 decrypt_done; - - /* (INT_STS_ND & INT_TRIG_DMA0) */ - __le32 dma0_done; - - /* (INT_STS_ND & INT_TRIG_DMA1) */ - __le32 dma1_done; - - /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ - __le32 tx_exch_complete; - - /* (INT_STS_ND & INT_TRIG_COMMAND) */ - __le32 commands; - - /* (INT_STS_ND & INT_TRIG_RX_PROC) */ - __le32 rx_procs; - - /* (INT_STS_ND & INT_TRIG_PM_802) */ - __le32 hw_pm_mode_changes; - - /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ - __le32 host_acknowledges; - - /* (INT_STS_ND & INT_TRIG_PM_PCI) */ - __le32 pci_pm; - - /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ - __le32 wakeups; - - /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ - __le32 low_rssi; -} __packed; - -struct acx_wep_statistics { - /* WEP address keys configured */ - __le32 addr_key_count; - - /* default keys configured */ - __le32 default_key_count; - - __le32 reserved; - - /* number of times that WEP key not found on lookup */ - __le32 key_not_found; - - /* number of times that WEP key decryption failed */ - __le32 decrypt_fail; - - /* WEP packets decrypted */ - __le32 packets; - - /* WEP decrypt interrupts */ - __le32 interrupt; -} __packed; - -#define ACX_MISSED_BEACONS_SPREAD 10 - -struct acx_pwr_statistics { - /* the amount of enters into power save mode (both PD & ELP) */ - __le32 ps_enter; - - /* the amount of enters into ELP mode */ - __le32 elp_enter; - - /* the amount of missing beacon interrupts to the host */ - __le32 missing_bcns; - - /* the amount of wake on host-access times */ - __le32 wake_on_host; - - /* the amount of wake on timer-expire */ - __le32 wake_on_timer_exp; - - /* the number of packets that were transmitted with PS bit set */ - __le32 tx_with_ps; - - /* the number of packets that were transmitted with PS bit clear */ - __le32 tx_without_ps; - - /* the number of received beacons */ - __le32 rcvd_beacons; - - /* the number of entering into PowerOn (power save off) */ - __le32 power_save_off; - - /* the number of entries into power save mode */ - __le16 enable_ps; - - /* - * the number of exits from power save, not including failed PS - * transitions - */ - __le16 disable_ps; - - /* - * the number of times the TSF counter was adjusted because - * of drift - */ - __le32 fix_tsf_ps; - - /* Gives statistics about the spread continuous missed beacons. - * The 16 LSB are dedicated for the PS mode. - * The 16 MSB are dedicated for the PS mode. - * cont_miss_bcns_spread[0] - single missed beacon. - * cont_miss_bcns_spread[1] - two continuous missed beacons. - * cont_miss_bcns_spread[2] - three continuous missed beacons. - * ... - * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. - */ - __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; - - /* the number of beacons in awake mode */ - __le32 rcvd_awake_beacons; -} __packed; - -struct acx_mic_statistics { - __le32 rx_pkts; - __le32 calc_failure; -} __packed; - -struct acx_aes_statistics { - __le32 encrypt_fail; - __le32 decrypt_fail; - __le32 encrypt_packets; - __le32 decrypt_packets; - __le32 encrypt_interrupt; - __le32 decrypt_interrupt; -} __packed; - -struct acx_event_statistics { - __le32 heart_beat; - __le32 calibration; - __le32 rx_mismatch; - __le32 rx_mem_empty; - __le32 rx_pool; - __le32 oom_late; - __le32 phy_transmit_error; - __le32 tx_stuck; -} __packed; - -struct acx_ps_statistics { - __le32 pspoll_timeouts; - __le32 upsd_timeouts; - __le32 upsd_max_sptime; - __le32 upsd_max_apturn; - __le32 pspoll_max_apturn; - __le32 pspoll_utilization; - __le32 upsd_utilization; -} __packed; - -struct acx_rxpipe_statistics { - __le32 rx_prep_beacon_drop; - __le32 descr_host_int_trig_rx_data; - __le32 beacon_buffer_thres_host_int_trig_rx_data; - __le32 missed_beacon_host_int_trig_rx_data; - __le32 tx_xfr_host_int_trig_rx_data; -} __packed; - -struct acx_statistics { - struct acx_header header; - - struct acx_tx_statistics tx; - struct acx_rx_statistics rx; - struct acx_dma_statistics dma; - struct acx_isr_statistics isr; - struct acx_wep_statistics wep; - struct acx_pwr_statistics pwr; - struct acx_aes_statistics aes; - struct acx_mic_statistics mic; - struct acx_event_statistics event; - struct acx_ps_statistics ps; - struct acx_rxpipe_statistics rxpipe; -} __packed; - struct acx_rate_class { __le32 enabled_rates; u8 short_retry_limit; @@ -1284,7 +1062,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_preamble_type preamble); int wl1271_acx_cts_protect(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum acx_ctsprotect_type ctsprotect); -int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); +int wl1271_acx_statistics(struct wl1271 *wl, void *stats); int wl1271_acx_sta_rate_policies(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, u8 idx); diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index d3775198d265..57c1eae24d63 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -25,6 +25,7 @@ #include #include +#include #include "wlcore.h" #include "debug.h" @@ -32,14 +33,15 @@ #include "ps.h" #include "io.h" #include "tx.h" +#include "hw_ops.h" /* ms */ #define WL1271_DEBUGFS_STATS_LIFETIME 1000 /* debugfs macros idea from mac80211 */ #define DEBUGFS_FORMAT_BUFFER_SIZE 100 -static int wl1271_format_buffer(char __user *userbuf, size_t count, - loff_t *ppos, char *fmt, ...) +int wl1271_format_buffer(char __user *userbuf, size_t count, + loff_t *ppos, char *fmt, ...) { va_list args; char buf[DEBUGFS_FORMAT_BUFFER_SIZE]; @@ -51,59 +53,9 @@ static int wl1271_format_buffer(char __user *userbuf, size_t count, return simple_read_from_buffer(userbuf, count, ppos, buf, res); } +EXPORT_SYMBOL_GPL(wl1271_format_buffer); -#define DEBUGFS_READONLY_FILE(name, fmt, value...) \ -static ssize_t name## _read(struct file *file, char __user *userbuf, \ - size_t count, loff_t *ppos) \ -{ \ - struct wl1271 *wl = file->private_data; \ - return wl1271_format_buffer(userbuf, count, ppos, \ - fmt "\n", ##value); \ -} \ - \ -static const struct file_operations name## _ops = { \ - .read = name## _read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_ADD(name, parent) \ - entry = debugfs_create_file(#name, 0400, parent, \ - wl, &name## _ops); \ - if (!entry || IS_ERR(entry)) \ - goto err; \ - -#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ - do { \ - entry = debugfs_create_file(#name, 0400, parent, \ - wl, &prefix## _## name## _ops); \ - if (!entry || IS_ERR(entry)) \ - goto err; \ - } while (0); - -#define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \ -static ssize_t sub## _ ##name## _read(struct file *file, \ - char __user *userbuf, \ - size_t count, loff_t *ppos) \ -{ \ - struct wl1271 *wl = file->private_data; \ - \ - wl1271_debugfs_update_stats(wl); \ - \ - return wl1271_format_buffer(userbuf, count, ppos, fmt "\n", \ - wl->stats.fw_stats->sub.name); \ -} \ - \ -static const struct file_operations sub## _ ##name## _ops = { \ - .read = sub## _ ##name## _read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_FWSTATS_ADD(sub, name) \ - DEBUGFS_ADD(sub## _ ##name, stats) - -static void wl1271_debugfs_update_stats(struct wl1271 *wl) +void wl1271_debugfs_update_stats(struct wl1271 *wl) { int ret; @@ -125,97 +77,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl) out: mutex_unlock(&wl->mutex); } - -DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u"); - -DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u"); -DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u"); -DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u"); -DEBUGFS_FWSTATS_FILE(rx, dropped, "%u"); -DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u"); -DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u"); -DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u"); -DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u"); - -DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u"); -DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u"); -DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u"); -DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u"); - -DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u"); -DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u"); -DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u"); -DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u"); -DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u"); -DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); -DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u"); -DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u"); -DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u"); -DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u"); -DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u"); -DEBUGFS_FWSTATS_FILE(isr, commands, "%u"); -DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u"); -DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u"); -DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u"); -DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u"); -DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u"); -DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u"); - -DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u"); -DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u"); -/* skipping wep.reserved */ -DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u"); -DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u"); -DEBUGFS_FWSTATS_FILE(wep, packets, "%u"); -DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u"); - -DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u"); -DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u"); -/* skipping cont_miss_bcns_spread for now */ -DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u"); - -DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u"); -DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u"); - -DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u"); -DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u"); -DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u"); -DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u"); -DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u"); -DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u"); - -DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u"); -DEBUGFS_FWSTATS_FILE(event, calibration, "%u"); -DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u"); -DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u"); -DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u"); -DEBUGFS_FWSTATS_FILE(event, oom_late, "%u"); -DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u"); -DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u"); - -DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u"); -DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u"); -DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u"); -DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u"); -DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u"); -DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u"); -DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u"); - -DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u"); -DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u"); -DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, "%u"); -DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u"); -DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u"); +EXPORT_SYMBOL_GPL(wl1271_debugfs_update_stats); DEBUGFS_READONLY_FILE(retry_count, "%u", wl->stats.retry_count); DEBUGFS_READONLY_FILE(excessive_retries, "%u", @@ -1002,107 +864,10 @@ static const struct file_operations beacon_filtering_ops = { }; static int wl1271_debugfs_add_files(struct wl1271 *wl, - struct dentry *rootdir) + struct dentry *rootdir) { int ret = 0; - struct dentry *entry, *stats, *streaming; - - stats = debugfs_create_dir("fw-statistics", rootdir); - if (!stats || IS_ERR(stats)) { - entry = stats; - goto err; - } - - DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); - - DEBUGFS_FWSTATS_ADD(rx, out_of_mem); - DEBUGFS_FWSTATS_ADD(rx, hdr_overflow); - DEBUGFS_FWSTATS_ADD(rx, hw_stuck); - DEBUGFS_FWSTATS_ADD(rx, dropped); - DEBUGFS_FWSTATS_ADD(rx, fcs_err); - DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig); - DEBUGFS_FWSTATS_ADD(rx, path_reset); - DEBUGFS_FWSTATS_ADD(rx, reset_counter); - - DEBUGFS_FWSTATS_ADD(dma, rx_requested); - DEBUGFS_FWSTATS_ADD(dma, rx_errors); - DEBUGFS_FWSTATS_ADD(dma, tx_requested); - DEBUGFS_FWSTATS_ADD(dma, tx_errors); - - DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt); - DEBUGFS_FWSTATS_ADD(isr, fiqs); - DEBUGFS_FWSTATS_ADD(isr, rx_headers); - DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow); - DEBUGFS_FWSTATS_ADD(isr, rx_rdys); - DEBUGFS_FWSTATS_ADD(isr, irqs); - DEBUGFS_FWSTATS_ADD(isr, tx_procs); - DEBUGFS_FWSTATS_ADD(isr, decrypt_done); - DEBUGFS_FWSTATS_ADD(isr, dma0_done); - DEBUGFS_FWSTATS_ADD(isr, dma1_done); - DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete); - DEBUGFS_FWSTATS_ADD(isr, commands); - DEBUGFS_FWSTATS_ADD(isr, rx_procs); - DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes); - DEBUGFS_FWSTATS_ADD(isr, host_acknowledges); - DEBUGFS_FWSTATS_ADD(isr, pci_pm); - DEBUGFS_FWSTATS_ADD(isr, wakeups); - DEBUGFS_FWSTATS_ADD(isr, low_rssi); - - DEBUGFS_FWSTATS_ADD(wep, addr_key_count); - DEBUGFS_FWSTATS_ADD(wep, default_key_count); - /* skipping wep.reserved */ - DEBUGFS_FWSTATS_ADD(wep, key_not_found); - DEBUGFS_FWSTATS_ADD(wep, decrypt_fail); - DEBUGFS_FWSTATS_ADD(wep, packets); - DEBUGFS_FWSTATS_ADD(wep, interrupt); - - DEBUGFS_FWSTATS_ADD(pwr, ps_enter); - DEBUGFS_FWSTATS_ADD(pwr, elp_enter); - DEBUGFS_FWSTATS_ADD(pwr, missing_bcns); - DEBUGFS_FWSTATS_ADD(pwr, wake_on_host); - DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp); - DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps); - DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps); - DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons); - DEBUGFS_FWSTATS_ADD(pwr, power_save_off); - DEBUGFS_FWSTATS_ADD(pwr, enable_ps); - DEBUGFS_FWSTATS_ADD(pwr, disable_ps); - DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps); - /* skipping cont_miss_bcns_spread for now */ - DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons); - - DEBUGFS_FWSTATS_ADD(mic, rx_pkts); - DEBUGFS_FWSTATS_ADD(mic, calc_failure); - - DEBUGFS_FWSTATS_ADD(aes, encrypt_fail); - DEBUGFS_FWSTATS_ADD(aes, decrypt_fail); - DEBUGFS_FWSTATS_ADD(aes, encrypt_packets); - DEBUGFS_FWSTATS_ADD(aes, decrypt_packets); - DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt); - DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt); - - DEBUGFS_FWSTATS_ADD(event, heart_beat); - DEBUGFS_FWSTATS_ADD(event, calibration); - DEBUGFS_FWSTATS_ADD(event, rx_mismatch); - DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); - DEBUGFS_FWSTATS_ADD(event, rx_pool); - DEBUGFS_FWSTATS_ADD(event, oom_late); - DEBUGFS_FWSTATS_ADD(event, phy_transmit_error); - DEBUGFS_FWSTATS_ADD(event, tx_stuck); - - DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts); - DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts); - DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime); - DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn); - DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn); - DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization); - DEBUGFS_FWSTATS_ADD(ps, upsd_utilization); - - DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop); - DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data); - DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); - DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); - DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); + struct dentry *entry, *streaming; DEBUGFS_ADD(tx_queue_len, rootdir); DEBUGFS_ADD(retry_count, rootdir); @@ -1144,7 +909,7 @@ void wl1271_debugfs_reset(struct wl1271 *wl) if (!wl->stats.fw_stats) return; - memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); + memset(wl->stats.fw_stats, 0, wl->stats.fw_stats_len); wl->stats.retry_count = 0; wl->stats.excessive_retries = 0; } @@ -1159,34 +924,34 @@ int wl1271_debugfs_init(struct wl1271 *wl) if (IS_ERR(rootdir)) { ret = PTR_ERR(rootdir); - goto err; + goto out; } - wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats), - GFP_KERNEL); - + wl->stats.fw_stats = kzalloc(wl->stats.fw_stats_len, GFP_KERNEL); if (!wl->stats.fw_stats) { ret = -ENOMEM; - goto err_fw; + goto out_remove; } wl->stats.fw_stats_update = jiffies; ret = wl1271_debugfs_add_files(wl, rootdir); + if (ret < 0) + goto out_exit; + ret = wlcore_debugfs_init(wl, rootdir); if (ret < 0) - goto err_file; + goto out_exit; - return 0; + goto out; -err_file: - kfree(wl->stats.fw_stats); - wl->stats.fw_stats = NULL; +out_exit: + wl1271_debugfs_exit(wl); -err_fw: +out_remove: debugfs_remove_recursive(rootdir); -err: +out: return ret; } diff --git a/drivers/net/wireless/ti/wlcore/debugfs.h b/drivers/net/wireless/ti/wlcore/debugfs.h index a8d3aef011ff..e1a5bf1972bb 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.h +++ b/drivers/net/wireless/ti/wlcore/debugfs.h @@ -26,8 +26,68 @@ #include "wlcore.h" +int wl1271_format_buffer(char __user *userbuf, size_t count, + loff_t *ppos, char *fmt, ...); + int wl1271_debugfs_init(struct wl1271 *wl); void wl1271_debugfs_exit(struct wl1271 *wl); void wl1271_debugfs_reset(struct wl1271 *wl); +void wl1271_debugfs_update_stats(struct wl1271 *wl); + +#define DEBUGFS_READONLY_FILE(name, fmt, value...) \ +static ssize_t name## _read(struct file *file, char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wl1271 *wl = file->private_data; \ + return wl1271_format_buffer(userbuf, count, ppos, \ + fmt "\n", ##value); \ +} \ + \ +static const struct file_operations name## _ops = { \ + .read = name## _read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_ADD(name, parent) \ + do { \ + entry = debugfs_create_file(#name, 0400, parent, \ + wl, &name## _ops); \ + if (!entry || IS_ERR(entry)) \ + goto err; \ + } while (0); + + +#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ + do { \ + entry = debugfs_create_file(#name, 0400, parent, \ + wl, &prefix## _## name## _ops); \ + if (!entry || IS_ERR(entry)) \ + goto err; \ + } while (0); + +#define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \ +static ssize_t sub## _ ##name## _read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wl1271 *wl = file->private_data; \ + struct struct_type *stats = wl->stats.fw_stats; \ + \ + wl1271_debugfs_update_stats(wl); \ + \ + return wl1271_format_buffer(userbuf, count, ppos, fmt "\n", \ + stats->sub.name); \ +} \ + \ +static const struct file_operations sub## _ ##name## _ops = { \ + .read = sub## _ ##name## _read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_FWSTATS_ADD(sub, name) \ + DEBUGFS_ADD(sub## _ ##name, stats) + #endif /* WL1271_DEBUGFS_H */ diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 6d3e378bd0c7..1555c3e12c1b 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -149,4 +149,13 @@ wlcore_hw_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, return 0; } +static inline int +wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) +{ + if (wl->ops->debugfs_init) + return wl->ops->debugfs_init(wl, rootdir); + + return 0; +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 9ca382918184..85fd3d9a5471 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -71,6 +71,7 @@ struct wlcore_ops { struct sk_buff *skb); u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl, struct wl12xx_vif *wlvif); + int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir); }; enum wlcore_partitions { @@ -119,6 +120,15 @@ enum wlcore_registers { REG_TABLE_LEN, }; +struct wl1271_stats { + void *fw_stats; + unsigned long fw_stats_update; + size_t fw_stats_len; + + unsigned int retry_count; + unsigned int excessive_retries; +}; + struct wl1271 { struct ieee80211_hw *hw; bool mac80211_registered; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 0422753f673c..45c07dc0df5d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -132,14 +132,6 @@ struct wl1271_chip { unsigned int fw_ver[NUM_FW_VER]; }; -struct wl1271_stats { - struct acx_statistics *fw_stats; - unsigned long fw_stats_update; - - unsigned int retry_count; - unsigned int excessive_retries; -}; - #define NUM_TX_QUEUES 4 #define AP_MAX_STATIONS 8 -- cgit v1.2.3 From bcca1bbdd412fb6be32d04fc2407c81239de5f8c Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:07 +0300 Subject: wlcore: add debugfs macro to help print fw statistics arrays Add a macro to make it easy to print arrays from the FW statistics array in debugfs. At the same time, increase the buffer size so arrays fit more easily. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/debugfs.c | 1 - drivers/net/wireless/ti/wlcore/debugfs.h | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 57c1eae24d63..fc44262e4cf0 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -39,7 +39,6 @@ #define WL1271_DEBUGFS_STATS_LIFETIME 1000 /* debugfs macros idea from mac80211 */ -#define DEBUGFS_FORMAT_BUFFER_SIZE 100 int wl1271_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...) { diff --git a/drivers/net/wireless/ti/wlcore/debugfs.h b/drivers/net/wireless/ti/wlcore/debugfs.h index e1a5bf1972bb..f7381dd69009 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.h +++ b/drivers/net/wireless/ti/wlcore/debugfs.h @@ -34,6 +34,8 @@ void wl1271_debugfs_exit(struct wl1271 *wl); void wl1271_debugfs_reset(struct wl1271 *wl); void wl1271_debugfs_update_stats(struct wl1271 *wl); +#define DEBUGFS_FORMAT_BUFFER_SIZE 256 + #define DEBUGFS_READONLY_FILE(name, fmt, value...) \ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ @@ -86,6 +88,31 @@ static const struct file_operations sub## _ ##name## _ops = { \ .llseek = generic_file_llseek, \ }; +#define DEBUGFS_FWSTATS_FILE_ARRAY(sub, name, len, struct_type) \ +static ssize_t sub## _ ##name## _read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wl1271 *wl = file->private_data; \ + struct struct_type *stats = wl->stats.fw_stats; \ + char buf[DEBUGFS_FORMAT_BUFFER_SIZE] = ""; \ + int res, i; \ + \ + wl1271_debugfs_update_stats(wl); \ + \ + for (i = 0; i < len; i++) \ + res = snprintf(buf, sizeof(buf), "%s[%d] = %d\n", \ + buf, i, stats->sub.name[i]); \ + \ + return wl1271_format_buffer(userbuf, count, ppos, "%s", buf); \ +} \ + \ +static const struct file_operations sub## _ ##name## _ops = { \ + .read = sub## _ ##name## _read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + #define DEBUGFS_FWSTATS_ADD(sub, name) \ DEBUGFS_ADD(sub## _ ##name, stats) -- cgit v1.2.3 From 10b1e8a27a45559df7d02c864081e929369d83bf Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:08 +0300 Subject: wl12xx: implement fw status debugfs entries Implement the operations that are necessary to fetch the wl12xx-specific FW statistics. Re-add some of the code removed from wlcore. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/Makefile | 2 +- drivers/net/wireless/ti/wl12xx/acx.h | 223 +++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl12xx/debugfs.c | 236 +++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl12xx/debugfs.h | 28 ++++ drivers/net/wireless/ti/wl12xx/main.c | 8 ++ 5 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ti/wl12xx/debugfs.c create mode 100644 drivers/net/wireless/ti/wl12xx/debugfs.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/Makefile b/drivers/net/wireless/ti/wl12xx/Makefile index 87f64b14db35..da509aa7d009 100644 --- a/drivers/net/wireless/ti/wl12xx/Makefile +++ b/drivers/net/wireless/ti/wl12xx/Makefile @@ -1,3 +1,3 @@ -wl12xx-objs = main.o cmd.o acx.o +wl12xx-objs = main.o cmd.o acx.o debugfs.o obj-$(CONFIG_WL12XX) += wl12xx.o diff --git a/drivers/net/wireless/ti/wl12xx/acx.h b/drivers/net/wireless/ti/wl12xx/acx.h index d1f5aba0afce..1be0f2d31b19 100644 --- a/drivers/net/wireless/ti/wl12xx/acx.h +++ b/drivers/net/wireless/ti/wl12xx/acx.h @@ -24,6 +24,7 @@ #define __WL12XX_ACX_H__ #include "../wlcore/wlcore.h" +#include "../wlcore/acx.h" struct wl1271_acx_host_config_bitmap { struct acx_header header; @@ -31,6 +32,228 @@ struct wl1271_acx_host_config_bitmap { __le32 host_cfg_bitmap; } __packed; +struct wl12xx_acx_tx_statistics { + __le32 internal_desc_overflow; +} __packed; + +struct wl12xx_acx_rx_statistics { + __le32 out_of_mem; + __le32 hdr_overflow; + __le32 hw_stuck; + __le32 dropped; + __le32 fcs_err; + __le32 xfr_hint_trig; + __le32 path_reset; + __le32 reset_counter; +} __packed; + +struct wl12xx_acx_dma_statistics { + __le32 rx_requested; + __le32 rx_errors; + __le32 tx_requested; + __le32 tx_errors; +} __packed; + +struct wl12xx_acx_isr_statistics { + /* host command complete */ + __le32 cmd_cmplt; + + /* fiqisr() */ + __le32 fiqs; + + /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ + __le32 rx_headers; + + /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ + __le32 rx_completes; + + /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ + __le32 rx_mem_overflow; + + /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ + __le32 rx_rdys; + + /* irqisr() */ + __le32 irqs; + + /* (INT_STS_ND & INT_TRIG_TX_PROC) */ + __le32 tx_procs; + + /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ + __le32 decrypt_done; + + /* (INT_STS_ND & INT_TRIG_DMA0) */ + __le32 dma0_done; + + /* (INT_STS_ND & INT_TRIG_DMA1) */ + __le32 dma1_done; + + /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ + __le32 tx_exch_complete; + + /* (INT_STS_ND & INT_TRIG_COMMAND) */ + __le32 commands; + + /* (INT_STS_ND & INT_TRIG_RX_PROC) */ + __le32 rx_procs; + + /* (INT_STS_ND & INT_TRIG_PM_802) */ + __le32 hw_pm_mode_changes; + + /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ + __le32 host_acknowledges; + + /* (INT_STS_ND & INT_TRIG_PM_PCI) */ + __le32 pci_pm; + + /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ + __le32 wakeups; + + /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ + __le32 low_rssi; +} __packed; + +struct wl12xx_acx_wep_statistics { + /* WEP address keys configured */ + __le32 addr_key_count; + + /* default keys configured */ + __le32 default_key_count; + + __le32 reserved; + + /* number of times that WEP key not found on lookup */ + __le32 key_not_found; + + /* number of times that WEP key decryption failed */ + __le32 decrypt_fail; + + /* WEP packets decrypted */ + __le32 packets; + + /* WEP decrypt interrupts */ + __le32 interrupt; +} __packed; + +#define ACX_MISSED_BEACONS_SPREAD 10 + +struct wl12xx_acx_pwr_statistics { + /* the amount of enters into power save mode (both PD & ELP) */ + __le32 ps_enter; + + /* the amount of enters into ELP mode */ + __le32 elp_enter; + + /* the amount of missing beacon interrupts to the host */ + __le32 missing_bcns; + + /* the amount of wake on host-access times */ + __le32 wake_on_host; + + /* the amount of wake on timer-expire */ + __le32 wake_on_timer_exp; + + /* the number of packets that were transmitted with PS bit set */ + __le32 tx_with_ps; + + /* the number of packets that were transmitted with PS bit clear */ + __le32 tx_without_ps; + + /* the number of received beacons */ + __le32 rcvd_beacons; + + /* the number of entering into PowerOn (power save off) */ + __le32 power_save_off; + + /* the number of entries into power save mode */ + __le16 enable_ps; + + /* + * the number of exits from power save, not including failed PS + * transitions + */ + __le16 disable_ps; + + /* + * the number of times the TSF counter was adjusted because + * of drift + */ + __le32 fix_tsf_ps; + + /* Gives statistics about the spread continuous missed beacons. + * The 16 LSB are dedicated for the PS mode. + * The 16 MSB are dedicated for the PS mode. + * cont_miss_bcns_spread[0] - single missed beacon. + * cont_miss_bcns_spread[1] - two continuous missed beacons. + * cont_miss_bcns_spread[2] - three continuous missed beacons. + * ... + * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. + */ + __le32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; + + /* the number of beacons in awake mode */ + __le32 rcvd_awake_beacons; +} __packed; + +struct wl12xx_acx_mic_statistics { + __le32 rx_pkts; + __le32 calc_failure; +} __packed; + +struct wl12xx_acx_aes_statistics { + __le32 encrypt_fail; + __le32 decrypt_fail; + __le32 encrypt_packets; + __le32 decrypt_packets; + __le32 encrypt_interrupt; + __le32 decrypt_interrupt; +} __packed; + +struct wl12xx_acx_event_statistics { + __le32 heart_beat; + __le32 calibration; + __le32 rx_mismatch; + __le32 rx_mem_empty; + __le32 rx_pool; + __le32 oom_late; + __le32 phy_transmit_error; + __le32 tx_stuck; +} __packed; + +struct wl12xx_acx_ps_statistics { + __le32 pspoll_timeouts; + __le32 upsd_timeouts; + __le32 upsd_max_sptime; + __le32 upsd_max_apturn; + __le32 pspoll_max_apturn; + __le32 pspoll_utilization; + __le32 upsd_utilization; +} __packed; + +struct wl12xx_acx_rxpipe_statistics { + __le32 rx_prep_beacon_drop; + __le32 descr_host_int_trig_rx_data; + __le32 beacon_buffer_thres_host_int_trig_rx_data; + __le32 missed_beacon_host_int_trig_rx_data; + __le32 tx_xfr_host_int_trig_rx_data; +} __packed; + +struct wl12xx_acx_statistics { + struct acx_header header; + + struct wl12xx_acx_tx_statistics tx; + struct wl12xx_acx_rx_statistics rx; + struct wl12xx_acx_dma_statistics dma; + struct wl12xx_acx_isr_statistics isr; + struct wl12xx_acx_wep_statistics wep; + struct wl12xx_acx_pwr_statistics pwr; + struct wl12xx_acx_aes_statistics aes; + struct wl12xx_acx_mic_statistics mic; + struct wl12xx_acx_event_statistics event; + struct wl12xx_acx_ps_statistics ps; + struct wl12xx_acx_rxpipe_statistics rxpipe; +} __packed; + int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap); #endif /* __WL12XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.c b/drivers/net/wireless/ti/wl12xx/debugfs.c new file mode 100644 index 000000000000..9cc79d8e647b --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/debugfs.c @@ -0,0 +1,236 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2011-2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "../wlcore/debugfs.h" +#include "../wlcore/wlcore.h" + +#include "wl12xx.h" +#include "acx.h" + +#define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ + DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics) + +WL12XX_DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(rx, out_of_mem, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, hw_stuck, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, dropped, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, fcs_err, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, path_reset, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rx, reset_counter, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_requested, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(dma, rx_errors, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_requested, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(dma, tx_errors, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, fiqs, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_headers, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_rdys, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_procs, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, decrypt_done, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma0_done, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, dma1_done, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, commands, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, rx_procs, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, pci_pm, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, wakeups, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(isr, low_rssi, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(wep, addr_key_count, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(wep, default_key_count, "%u"); +/* skipping wep.reserved */ +WL12XX_DEBUGFS_FWSTATS_FILE(wep, key_not_found, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(wep, packets, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(wep, interrupt, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, ps_enter, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, elp_enter, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, power_save_off, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, enable_ps, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, disable_ps, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, "%u"); +/* skipping cont_miss_bcns_spread for now */ +WL12XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(mic, rx_pkts, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(mic, calc_failure, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(event, heart_beat, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, rx_pool, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, oom_late, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(event, tx_stuck, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, "%u"); + +WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, + "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, "%u"); +WL12XX_DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, "%u"); + +int wl12xx_debugfs_add_files(struct wl1271 *wl, + struct dentry *rootdir) +{ + int ret = 0; + struct dentry *entry, *stats; + + stats = debugfs_create_dir("wl12xx_fw_stats", rootdir); + if (!stats || IS_ERR(stats)) { + entry = stats; + goto err; + } + + DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); + + DEBUGFS_FWSTATS_ADD(rx, out_of_mem); + DEBUGFS_FWSTATS_ADD(rx, hdr_overflow); + DEBUGFS_FWSTATS_ADD(rx, hw_stuck); + DEBUGFS_FWSTATS_ADD(rx, dropped); + DEBUGFS_FWSTATS_ADD(rx, fcs_err); + DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig); + DEBUGFS_FWSTATS_ADD(rx, path_reset); + DEBUGFS_FWSTATS_ADD(rx, reset_counter); + + DEBUGFS_FWSTATS_ADD(dma, rx_requested); + DEBUGFS_FWSTATS_ADD(dma, rx_errors); + DEBUGFS_FWSTATS_ADD(dma, tx_requested); + DEBUGFS_FWSTATS_ADD(dma, tx_errors); + + DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt); + DEBUGFS_FWSTATS_ADD(isr, fiqs); + DEBUGFS_FWSTATS_ADD(isr, rx_headers); + DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow); + DEBUGFS_FWSTATS_ADD(isr, rx_rdys); + DEBUGFS_FWSTATS_ADD(isr, irqs); + DEBUGFS_FWSTATS_ADD(isr, tx_procs); + DEBUGFS_FWSTATS_ADD(isr, decrypt_done); + DEBUGFS_FWSTATS_ADD(isr, dma0_done); + DEBUGFS_FWSTATS_ADD(isr, dma1_done); + DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete); + DEBUGFS_FWSTATS_ADD(isr, commands); + DEBUGFS_FWSTATS_ADD(isr, rx_procs); + DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes); + DEBUGFS_FWSTATS_ADD(isr, host_acknowledges); + DEBUGFS_FWSTATS_ADD(isr, pci_pm); + DEBUGFS_FWSTATS_ADD(isr, wakeups); + DEBUGFS_FWSTATS_ADD(isr, low_rssi); + + DEBUGFS_FWSTATS_ADD(wep, addr_key_count); + DEBUGFS_FWSTATS_ADD(wep, default_key_count); + /* skipping wep.reserved */ + DEBUGFS_FWSTATS_ADD(wep, key_not_found); + DEBUGFS_FWSTATS_ADD(wep, decrypt_fail); + DEBUGFS_FWSTATS_ADD(wep, packets); + DEBUGFS_FWSTATS_ADD(wep, interrupt); + + DEBUGFS_FWSTATS_ADD(pwr, ps_enter); + DEBUGFS_FWSTATS_ADD(pwr, elp_enter); + DEBUGFS_FWSTATS_ADD(pwr, missing_bcns); + DEBUGFS_FWSTATS_ADD(pwr, wake_on_host); + DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp); + DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps); + DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps); + DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons); + DEBUGFS_FWSTATS_ADD(pwr, power_save_off); + DEBUGFS_FWSTATS_ADD(pwr, enable_ps); + DEBUGFS_FWSTATS_ADD(pwr, disable_ps); + DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps); + /* skipping cont_miss_bcns_spread for now */ + DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons); + + DEBUGFS_FWSTATS_ADD(mic, rx_pkts); + DEBUGFS_FWSTATS_ADD(mic, calc_failure); + + DEBUGFS_FWSTATS_ADD(aes, encrypt_fail); + DEBUGFS_FWSTATS_ADD(aes, decrypt_fail); + DEBUGFS_FWSTATS_ADD(aes, encrypt_packets); + DEBUGFS_FWSTATS_ADD(aes, decrypt_packets); + DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt); + DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt); + + DEBUGFS_FWSTATS_ADD(event, heart_beat); + DEBUGFS_FWSTATS_ADD(event, calibration); + DEBUGFS_FWSTATS_ADD(event, rx_mismatch); + DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); + DEBUGFS_FWSTATS_ADD(event, rx_pool); + DEBUGFS_FWSTATS_ADD(event, oom_late); + DEBUGFS_FWSTATS_ADD(event, phy_transmit_error); + DEBUGFS_FWSTATS_ADD(event, tx_stuck); + + DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts); + DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts); + DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime); + DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn); + DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn); + DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization); + DEBUGFS_FWSTATS_ADD(ps, upsd_utilization); + + DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop); + DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data); + DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); + DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); + DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); + + return 0; + +err: + if (IS_ERR(entry)) + ret = PTR_ERR(entry); + else + ret = -ENOMEM; + + return ret; +} diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.h b/drivers/net/wireless/ti/wl12xx/debugfs.h new file mode 100644 index 000000000000..96898e291b78 --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/debugfs.h @@ -0,0 +1,28 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL12XX_DEBUGFS_H__ +#define __WL12XX_DEBUGFS_H__ + +int wl12xx_debugfs_add_files(struct wl1271 *wl, + struct dentry *rootdir); + +#endif /* __WL12XX_DEBUGFS_H__ */ diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 82e0c6a6ac89..2ac840783551 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -39,6 +39,7 @@ #include "reg.h" #include "cmd.h" #include "acx.h" +#include "debugfs.h" static char *fref_param; static char *tcxo_param; @@ -1359,6 +1360,11 @@ out: return ret; } +static int wl12xx_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) +{ + return wl12xx_debugfs_add_files(wl, rootdir); +} + static struct wlcore_ops wl12xx_ops = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, @@ -1381,6 +1387,7 @@ static struct wlcore_ops wl12xx_ops = { .set_tx_desc_csum = wl12xx_set_tx_desc_csum, .set_rx_csum = NULL, .ap_get_mimo_wide_rate_mask = NULL, + .debugfs_init = wl12xx_debugfs_init, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { @@ -1422,6 +1429,7 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = 0; + wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap)); wl12xx_conf_init(wl); -- cgit v1.2.3 From 8c0ea1021c38cfd2f0ba5d8fdd48a9e9827bbc03 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:09 +0300 Subject: wl18xx: implement fw status debugfs entries Implement the operations that are necessary to fetch the wl18xx-specific FW statistics and export them in debugfs. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/debugfs.c | 1 + drivers/net/wireless/ti/wl18xx/Makefile | 2 +- drivers/net/wireless/ti/wl18xx/acx.h | 199 +++++++++++++++++- drivers/net/wireless/ti/wl18xx/debugfs.c | 345 +++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/debugfs.h | 28 +++ drivers/net/wireless/ti/wl18xx/main.c | 9 +- 6 files changed, 581 insertions(+), 3 deletions(-) create mode 100644 drivers/net/wireless/ti/wl18xx/debugfs.c create mode 100644 drivers/net/wireless/ti/wl18xx/debugfs.h (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.c b/drivers/net/wireless/ti/wl12xx/debugfs.c index 9cc79d8e647b..311703d5af84 100644 --- a/drivers/net/wireless/ti/wl12xx/debugfs.c +++ b/drivers/net/wireless/ti/wl12xx/debugfs.c @@ -25,6 +25,7 @@ #include "wl12xx.h" #include "acx.h" +#include "debugfs.h" #define WL12XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ DEBUGFS_FWSTATS_FILE(a, b, c, wl12xx_acx_statistics) diff --git a/drivers/net/wireless/ti/wl18xx/Makefile b/drivers/net/wireless/ti/wl18xx/Makefile index abd881f4ea01..67c098734c7f 100644 --- a/drivers/net/wireless/ti/wl18xx/Makefile +++ b/drivers/net/wireless/ti/wl18xx/Makefile @@ -1,3 +1,3 @@ -wl18xx-objs = main.o acx.o tx.o io.o +wl18xx-objs = main.o acx.o tx.o io.o debugfs.o obj-$(CONFIG_WL18XX) += wl18xx.o diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 544db6ef3841..c2ccf70bcfb3 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -23,6 +23,7 @@ #define __WL18XX_ACX_H__ #include "../wlcore/wlcore.h" +#include "../wlcore/acx.h" /* numbers of bits the length field takes (add 1 for the actual number) */ #define WL18XX_HOST_IF_LEN_SIZE_FIELD 15 @@ -60,9 +61,205 @@ struct wl18xx_acx_checksum_state { u8 pad[3]; } __packed; +struct wl18xx_acx_debug_stats { + u32 debug1; + u32 debug2; + u32 debug3; + u32 debug4; + u32 debug5; + u32 debug6; +} __packed; + +struct wl18xx_acx_ring_stats { + u32 tx_procs; + u32 prepared_descs; + u32 tx_xfr; + u32 tx_dma; + u32 tx_cmplt; + u32 rx_procs; + u32 rx_data; +} __packed; + +struct wl18xx_acx_tx_stats { + u32 tx_template_prepared; + u32 tx_data_prepared; + u32 tx_template_programmed; + u32 tx_data_programmed; + u32 tx_burst_programmed; + u32 tx_starts; + u32 tx_imm_resp; + u32 tx_start_templates; + u32 tx_start_int_templates; + u32 tx_start_fw_gen; + u32 tx_start_data; + u32 tx_start_null_frame; + u32 tx_exch; + u32 tx_retry_template; + u32 tx_retry_data; + u32 tx_exch_pending; + u32 tx_exch_expiry; + u32 tx_exch_mismatch; + u32 tx_done_template; + u32 tx_done_data; + u32 tx_done_int_template; + u32 tx_pre_xfr; + u32 tx_xfr; + u32 tx_xfr_out_of_mem; + u32 tx_dma_programmed; + u32 tx_dma_done; +} __packed; + +struct wl18xx_acx_rx_stats { + u32 rx_out_of_mem; + u32 rx_hdr_overflow; + u32 rx_hw_stuck; + u32 rx_dropped_frame; + u32 rx_complete_dropped_frame; + u32 rx_alloc_frame; + u32 rx_done_queue; + u32 rx_done; + u32 rx_defrag; + u32 rx_defrag_end; + u32 rx_mic; + u32 rx_mic_end; + u32 rx_xfr; + u32 rx_xfr_end; + u32 rx_cmplt; + u32 rx_pre_complt; + u32 rx_cmplt_task; + u32 rx_phy_hdr; + u32 rx_timeout; +} __packed; + +struct wl18xx_acx_dma_stats { + u32 rx_dma_errors; + u32 tx_dma_errors; +} __packed; + +struct wl18xx_acx_isr_stats { + u32 irqs; +} __packed; + +struct wl18xx_acx_wep_stats { + u32 wep_add_key_count; + u32 wep_default_key_count; + u32 wep_key_not_found; + u32 wep_decrypt_fail; + u32 wep_encrypt_fail; + u32 wep_dec_packets; + u32 wep_dec_interrupt; + u32 wep_enc_packets; + u32 wep_enc_interrupts; +} __packed; + +#define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10 + +struct wl18xx_acx_pwr_stats { + u32 missing_bcns_cnt; + u32 rcvd_bcns_cnt; + u32 connection_out_of_sync; + u32 cont_miss_bcns_spread[PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD]; + u32 rcvd_awake_bcns_cnt; +} __packed; + +struct wl18xx_acx_mic_stats { + u32 mic_rx_pkts; + u32 mic_calc_failure; +} __packed; + +struct wl18xx_acx_aes_stats { + u32 aes_encrypt_fail; + u32 aes_decrypt_fail; + u32 aes_encrypt_packets; + u32 aes_decrypt_packets; + u32 aes_encrypt_interrupt; + u32 aes_decrypt_interrupt; +} __packed; + +struct wl18xx_acx_gem_stats { + u32 gem_encrypt_fail; + u32 gem_decrypt_fail; + u32 gem_encrypt_packets; + u32 gem_decrypt_packets; + u32 gem_encrypt_interrupt; + u32 gem_decrypt_interrupt; +} __packed; + +struct wl18xx_acx_event_stats { + u32 calibration; + u32 rx_mismatch; + u32 rx_mem_empty; +} __packed; + +struct wl18xx_acx_ps_poll_stats { + u32 ps_poll_timeouts; + u32 upsd_timeouts; + u32 upsd_max_ap_turn; + u32 ps_poll_max_ap_turn; + u32 ps_poll_utilization; + u32 upsd_utilization; +} __packed; + +struct wl18xx_acx_rx_filter_stats { + u32 beacon_filter; + u32 arp_filter; + u32 mc_filter; + u32 dup_filter; + u32 data_filter; + u32 ibss_filter; + u32 protection_filter; +} __packed; + +struct wl18xx_acx_calibration_stats { + u32 init_cal_total; + u32 init_radio_bands_fail; + u32 init_set_params; + u32 init_tx_clpc_fail; + u32 init_rx_iw_mm_fail; + u32 tune_cal_total; + u32 tune_drpw_rtrim_fail; + u32 tune_drpw_pd_buf_fail; + u32 tune_drpw_tx_mix_freq_fail; + u32 tune_drpw_ta_cal; + u32 tune_drpw_rx_if_2_gain; + u32 tune_drpw_rx_dac; + u32 tune_drpw_chan_tune; + u32 tune_drpw_rx_tx_lpf; + u32 tune_drpw_lna_tank; + u32 tune_tx_lo_leak_fail; + u32 tune_tx_iq_mm_fail; + u32 tune_tx_pdet_fail; + u32 tune_tx_ppa_fail; + u32 tune_tx_clpc_fail; + u32 tune_rx_ana_dc_fail; + u32 tune_rx_dig_dc_fail; /* check if this is needed */ + u32 tune_rx_iq_mm_fail; + u32 cal_state_fail; +} __packed; + +struct wl18xx_acx_statistics { + struct acx_header header; + + struct wl18xx_acx_ring_stats ring; + struct wl18xx_acx_debug_stats debug; + struct wl18xx_acx_tx_stats tx; + struct wl18xx_acx_rx_stats rx; + struct wl18xx_acx_dma_stats dma; + struct wl18xx_acx_isr_stats isr; + struct wl18xx_acx_wep_stats wep; + struct wl18xx_acx_pwr_stats pwr; + struct wl18xx_acx_aes_stats aes; + struct wl18xx_acx_mic_stats mic; + struct wl18xx_acx_event_stats event; + struct wl18xx_acx_ps_poll_stats ps_poll; + struct wl18xx_acx_rx_filter_stats rx_filter; + struct wl18xx_acx_calibration_stats calibration; + struct wl18xx_acx_gem_stats gem; +} __packed; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); int wl18xx_acx_set_checksum_state(struct wl1271 *wl); -#endif /* __WL12XX_ACX_H__ */ +#endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c new file mode 100644 index 000000000000..8354dfc64bd8 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -0,0 +1,345 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2009 Nokia Corporation + * Copyright (C) 2011-2012 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "../wlcore/debugfs.h" +#include "../wlcore/wlcore.h" + +#include "wl18xx.h" +#include "acx.h" +#include "debugfs.h" + +#define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ + DEBUGFS_FWSTATS_FILE(a, b, c, wl18xx_acx_statistics) +#define WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \ + DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, wl18xx_acx_statistics) + +WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug1, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug2, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug3, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_procs, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ring, prepared_descs, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_xfr, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_dma, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_cmplt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ring, rx_procs, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ring, rx_data, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_programmed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_burst_programmed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_starts, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_imm_resp, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_templates, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_int_templates, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_fw_gen, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_data, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_start_null_frame, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_mismatch, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_pre_xfr, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_xfr, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_xfr_out_of_mem, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_dma_programmed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_dma_done, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mem, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hw_stuck, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_complete_dropped_frame, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_alloc_frame, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done_queue, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_mic, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_mic_end, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_xfr, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_xfr_end, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(dma, rx_dma_errors, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(dma, tx_dma_errors, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_add_key_count, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_default_key_count, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_key_not_found, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_decrypt_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_encrypt_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_dec_packets, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_dec_interrupt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_enc_packets, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_enc_interrupts, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread, + PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD); +WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(mic, mic_rx_pkts, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(mic, mic_calc_failure, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_encrypt_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_decrypt_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_encrypt_packets, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_decrypt_packets, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_encrypt_interrupt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_decrypt_interrupt, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_encrypt_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_decrypt_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_encrypt_packets, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_decrypt_packets, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_encrypt_interrupt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_decrypt_interrupt, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_timeouts, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_timeouts, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_max_ap_turn, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_max_ap_turn, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_utilization, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_utilization, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, beacon_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, arp_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, mc_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_cal_total, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_radio_bands_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_set_params, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_tx_clpc_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_rx_iw_mm_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_cal_total, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rtrim_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_pd_buf_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_tx_mix_freq_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_ta_cal, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rx_if_2_gain, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rx_dac, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_chan_tune, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rx_tx_lpf, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_lna_tank, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_lo_leak_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_iq_mm_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_pdet_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_ppa_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_clpc_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_rx_ana_dc_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_rx_dig_dc_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_rx_iq_mm_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(calibration, cal_state_fail, "%u"); + +int wl18xx_debugfs_add_files(struct wl1271 *wl, + struct dentry *rootdir) +{ + int ret = 0; + struct dentry *entry, *stats; + + stats = debugfs_create_dir("wl18xx_fw_stats", rootdir); + if (!stats || IS_ERR(stats)) { + entry = stats; + goto err; + } + + DEBUGFS_FWSTATS_ADD(debug, debug1); + DEBUGFS_FWSTATS_ADD(debug, debug2); + DEBUGFS_FWSTATS_ADD(debug, debug3); + DEBUGFS_FWSTATS_ADD(debug, debug4); + DEBUGFS_FWSTATS_ADD(debug, debug5); + DEBUGFS_FWSTATS_ADD(debug, debug6); + + DEBUGFS_FWSTATS_ADD(ring, tx_procs); + DEBUGFS_FWSTATS_ADD(ring, prepared_descs); + DEBUGFS_FWSTATS_ADD(ring, tx_xfr); + DEBUGFS_FWSTATS_ADD(ring, tx_dma); + DEBUGFS_FWSTATS_ADD(ring, tx_cmplt); + DEBUGFS_FWSTATS_ADD(ring, rx_procs); + DEBUGFS_FWSTATS_ADD(ring, rx_data); + + DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared); + DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared); + DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed); + DEBUGFS_FWSTATS_ADD(tx, tx_data_programmed); + DEBUGFS_FWSTATS_ADD(tx, tx_burst_programmed); + DEBUGFS_FWSTATS_ADD(tx, tx_starts); + DEBUGFS_FWSTATS_ADD(tx, tx_imm_resp); + DEBUGFS_FWSTATS_ADD(tx, tx_start_templates); + DEBUGFS_FWSTATS_ADD(tx, tx_start_int_templates); + DEBUGFS_FWSTATS_ADD(tx, tx_start_fw_gen); + DEBUGFS_FWSTATS_ADD(tx, tx_start_data); + DEBUGFS_FWSTATS_ADD(tx, tx_start_null_frame); + DEBUGFS_FWSTATS_ADD(tx, tx_exch); + DEBUGFS_FWSTATS_ADD(tx, tx_retry_template); + DEBUGFS_FWSTATS_ADD(tx, tx_retry_data); + DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending); + DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry); + DEBUGFS_FWSTATS_ADD(tx, tx_exch_mismatch); + DEBUGFS_FWSTATS_ADD(tx, tx_done_template); + DEBUGFS_FWSTATS_ADD(tx, tx_done_data); + DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template); + DEBUGFS_FWSTATS_ADD(tx, tx_pre_xfr); + DEBUGFS_FWSTATS_ADD(tx, tx_xfr); + DEBUGFS_FWSTATS_ADD(tx, tx_xfr_out_of_mem); + DEBUGFS_FWSTATS_ADD(tx, tx_dma_programmed); + DEBUGFS_FWSTATS_ADD(tx, tx_dma_done); + + DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mem); + DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow); + DEBUGFS_FWSTATS_ADD(rx, rx_hw_stuck); + DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame); + DEBUGFS_FWSTATS_ADD(rx, rx_complete_dropped_frame); + DEBUGFS_FWSTATS_ADD(rx, rx_alloc_frame); + DEBUGFS_FWSTATS_ADD(rx, rx_done_queue); + DEBUGFS_FWSTATS_ADD(rx, rx_done); + DEBUGFS_FWSTATS_ADD(rx, rx_defrag); + DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end); + DEBUGFS_FWSTATS_ADD(rx, rx_mic); + DEBUGFS_FWSTATS_ADD(rx, rx_mic_end); + DEBUGFS_FWSTATS_ADD(rx, rx_xfr); + DEBUGFS_FWSTATS_ADD(rx, rx_xfr_end); + DEBUGFS_FWSTATS_ADD(rx, rx_cmplt); + DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt); + DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task); + DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr); + DEBUGFS_FWSTATS_ADD(rx, rx_timeout); + + DEBUGFS_FWSTATS_ADD(dma, rx_dma_errors); + DEBUGFS_FWSTATS_ADD(dma, tx_dma_errors); + + DEBUGFS_FWSTATS_ADD(isr, irqs); + + DEBUGFS_FWSTATS_ADD(wep, wep_add_key_count); + DEBUGFS_FWSTATS_ADD(wep, wep_default_key_count); + DEBUGFS_FWSTATS_ADD(wep, wep_key_not_found); + DEBUGFS_FWSTATS_ADD(wep, wep_decrypt_fail); + DEBUGFS_FWSTATS_ADD(wep, wep_encrypt_fail); + DEBUGFS_FWSTATS_ADD(wep, wep_dec_packets); + DEBUGFS_FWSTATS_ADD(wep, wep_dec_interrupt); + DEBUGFS_FWSTATS_ADD(wep, wep_enc_packets); + DEBUGFS_FWSTATS_ADD(wep, wep_enc_interrupts); + + DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt); + DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt); + DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync); + DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread); + DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt); + + DEBUGFS_FWSTATS_ADD(mic, mic_rx_pkts); + DEBUGFS_FWSTATS_ADD(mic, mic_calc_failure); + + DEBUGFS_FWSTATS_ADD(aes, aes_encrypt_fail); + DEBUGFS_FWSTATS_ADD(aes, aes_decrypt_fail); + DEBUGFS_FWSTATS_ADD(aes, aes_encrypt_packets); + DEBUGFS_FWSTATS_ADD(aes, aes_decrypt_packets); + DEBUGFS_FWSTATS_ADD(aes, aes_encrypt_interrupt); + DEBUGFS_FWSTATS_ADD(aes, aes_decrypt_interrupt); + + DEBUGFS_FWSTATS_ADD(gem, gem_encrypt_fail); + DEBUGFS_FWSTATS_ADD(gem, gem_decrypt_fail); + DEBUGFS_FWSTATS_ADD(gem, gem_encrypt_packets); + DEBUGFS_FWSTATS_ADD(gem, gem_decrypt_packets); + DEBUGFS_FWSTATS_ADD(gem, gem_encrypt_interrupt); + DEBUGFS_FWSTATS_ADD(gem, gem_decrypt_interrupt); + + DEBUGFS_FWSTATS_ADD(event, calibration); + DEBUGFS_FWSTATS_ADD(event, rx_mismatch); + DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); + + DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_timeouts); + DEBUGFS_FWSTATS_ADD(ps_poll, upsd_timeouts); + DEBUGFS_FWSTATS_ADD(ps_poll, upsd_max_ap_turn); + DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_max_ap_turn); + DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_utilization); + DEBUGFS_FWSTATS_ADD(ps_poll, upsd_utilization); + + DEBUGFS_FWSTATS_ADD(rx_filter, beacon_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, arp_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, mc_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, dup_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, data_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter); + + DEBUGFS_FWSTATS_ADD(calibration, init_cal_total); + DEBUGFS_FWSTATS_ADD(calibration, init_radio_bands_fail); + DEBUGFS_FWSTATS_ADD(calibration, init_set_params); + DEBUGFS_FWSTATS_ADD(calibration, init_tx_clpc_fail); + DEBUGFS_FWSTATS_ADD(calibration, init_rx_iw_mm_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_cal_total); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rtrim_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_pd_buf_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_tx_mix_freq_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_ta_cal); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rx_if_2_gain); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rx_dac); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_chan_tune); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rx_tx_lpf); + DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_lna_tank); + DEBUGFS_FWSTATS_ADD(calibration, tune_tx_lo_leak_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_tx_iq_mm_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_tx_pdet_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_tx_ppa_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_tx_clpc_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_rx_ana_dc_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_rx_dig_dc_fail); + DEBUGFS_FWSTATS_ADD(calibration, tune_rx_iq_mm_fail); + DEBUGFS_FWSTATS_ADD(calibration, cal_state_fail); + + return 0; + +err: + if (IS_ERR(entry)) + ret = PTR_ERR(entry); + else + ret = -ENOMEM; + + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.h b/drivers/net/wireless/ti/wl18xx/debugfs.h new file mode 100644 index 000000000000..ed679bebf620 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/debugfs.h @@ -0,0 +1,28 @@ +/* + * This file is part of wl18xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_DEBUGFS_H__ +#define __WL18XX_DEBUGFS_H__ + +int wl18xx_debugfs_add_files(struct wl1271 *wl, + struct dentry *rootdir); + +#endif /* __WL18XX_DEBUGFS_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index afa2334d0aea..24673e37a1dd 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -38,7 +38,7 @@ #include "tx.h" #include "wl18xx.h" #include "io.h" - +#include "debugfs.h" #define WL18XX_RX_CHECKSUM_MASK 0x40 @@ -1011,6 +1011,11 @@ static void wl18xx_get_mac(struct wl1271 *wl) wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); } +static int wl18xx_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) +{ + return wl18xx_debugfs_add_files(wl, rootdir); +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -1031,6 +1036,7 @@ static struct wlcore_ops wl18xx_ops = { .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, .get_mac = wl18xx_get_mac, + .debugfs_init = wl18xx_debugfs_init, }; /* HT cap appropriate for wide channels */ @@ -1085,6 +1091,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); + wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); memcpy(&wl->ht_cap, &wl18xx_ht_cap, sizeof(wl18xx_ht_cap)); if (ht_mode_param && !strcmp(ht_mode_param, "mimo")) memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, -- cgit v1.2.3 From 7140df6e51ecca70e8963f18e9836e62090221c2 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:10 +0300 Subject: wlcore: create private static_data area and add operation to parse it The wl18xx firmware has more information in the static_data than wl12xx. To be able to parse that in an abstracted way, this patch adds a priv area to the static data struct and an operation that allows the lower driver to parse it if necessary. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/boot.c | 53 +++++++++++++++++++-------------- drivers/net/wireless/ti/wlcore/boot.h | 1 + drivers/net/wireless/ti/wlcore/hw_ops.h | 9 ++++++ drivers/net/wireless/ti/wlcore/wlcore.h | 6 ++++ 4 files changed, 46 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 9b98230f84ce..ed718f7ddcce 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -45,10 +45,17 @@ static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl); } -static int wlcore_parse_fw_ver(struct wl1271 *wl) +static int wlcore_boot_parse_fw_ver(struct wl1271 *wl, + struct wl1271_static_data *static_data) { int ret; + strncpy(wl->chip.fw_ver_str, static_data->fw_version, + sizeof(wl->chip.fw_ver_str)); + + /* make sure the string is NULL-terminated */ + wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0'; + ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u", &wl->chip.fw_ver[0], &wl->chip.fw_ver[1], &wl->chip.fw_ver[2], &wl->chip.fw_ver[3], @@ -57,43 +64,43 @@ static int wlcore_parse_fw_ver(struct wl1271 *wl) if (ret != 5) { wl1271_warning("fw version incorrect value"); memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); - return -EINVAL; + ret = -EINVAL; + goto out; } ret = wlcore_identify_fw(wl); if (ret < 0) - return ret; - - return 0; + goto out; +out: + return ret; } -static int wlcore_boot_fw_version(struct wl1271 *wl) +static int wlcore_boot_static_data(struct wl1271 *wl) { struct wl1271_static_data *static_data; + size_t len = sizeof(*static_data) + wl->static_data_priv_len; int ret; - static_data = kmalloc(sizeof(*static_data), GFP_KERNEL | GFP_DMA); + static_data = kmalloc(len, GFP_KERNEL); if (!static_data) { - wl1271_error("Couldn't allocate memory for static data!"); - return -ENOMEM; + ret = -ENOMEM; + goto out; } - wl1271_read(wl, wl->cmd_box_addr, static_data, sizeof(*static_data), - false); + wl1271_read(wl, wl->cmd_box_addr, static_data, len, false); - strncpy(wl->chip.fw_ver_str, static_data->fw_version, - sizeof(wl->chip.fw_ver_str)); - - kfree(static_data); - - /* make sure the string is NULL-terminated */ - wl->chip.fw_ver_str[sizeof(wl->chip.fw_ver_str) - 1] = '\0'; + ret = wlcore_boot_parse_fw_ver(wl, static_data); + if (ret < 0) + goto out_free; - ret = wlcore_parse_fw_ver(wl); + ret = wlcore_handle_static_data(wl, static_data); if (ret < 0) - return ret; + goto out_free; - return 0; +out_free: + kfree(static_data); +out: + return ret; } static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, @@ -400,9 +407,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); - ret = wlcore_boot_fw_version(wl); + ret = wlcore_boot_static_data(wl); if (ret < 0) { - wl1271_error("couldn't boot firmware"); + wl1271_error("error getting static data"); return ret; } diff --git a/drivers/net/wireless/ti/wlcore/boot.h b/drivers/net/wireless/ti/wlcore/boot.h index 094981dd2227..a525225f990c 100644 --- a/drivers/net/wireless/ti/wlcore/boot.h +++ b/drivers/net/wireless/ti/wlcore/boot.h @@ -40,6 +40,7 @@ struct wl1271_static_data { u8 fw_version[WL1271_FW_VERSION_MAX_LEN]; u32 hw_version; u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS]; + u8 priv[0]; }; /* number of times we try to read the INIT interrupt */ diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 1555c3e12c1b..c590b6f529d1 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -158,4 +158,13 @@ wlcore_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) return 0; } +static inline int +wlcore_handle_static_data(struct wl1271 *wl, void *static_data) +{ + if (wl->ops->handle_static_data) + return wl->ops->handle_static_data(wl, static_data); + + return 0; +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 85fd3d9a5471..19678738a069 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -26,6 +26,7 @@ #include "wlcore_i.h" #include "event.h" +#include "boot.h" /* The maximum number of Tx descriptors in all chip families */ #define WLCORE_MAX_TX_DESCRIPTORS 32 @@ -72,6 +73,8 @@ struct wlcore_ops { u32 (*ap_get_mimo_wide_rate_mask)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir); + int (*handle_static_data)(struct wl1271 *wl, + struct wl1271_static_data *static_data); }; enum wlcore_partitions { @@ -373,6 +376,9 @@ struct wl1271 { /* RX Data filter rule state - enabled/disabled */ bool rx_filter_enabled[WL1271_MAX_RX_FILTERS]; + /* size of the private static data */ + size_t static_data_priv_len; + /* the current channel type */ enum nl80211_channel_type channel_type; }; -- cgit v1.2.3 From 283e8c425f6eab2002457c27a5182869bb414771 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:11 +0300 Subject: wl18xx: print the PHY firmware version from the private static data The wl18xx firmware writes the PHY firmware version in the static data. Add an operation to parse the static data and print the PHY firmware version when booting. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 13 +++++++++++++ drivers/net/wireless/ti/wl18xx/wl18xx.h | 6 ++++++ 2 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 24673e37a1dd..8262a4f7a10f 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1016,6 +1016,17 @@ static int wl18xx_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) return wl18xx_debugfs_add_files(wl, rootdir); } +static int wl18xx_handle_static_data(struct wl1271 *wl, + struct wl1271_static_data *static_data) +{ + struct wl18xx_static_data_priv *static_data_priv = + (struct wl18xx_static_data_priv *) static_data->priv; + + wl1271_info("PHY firmware version: %s", static_data_priv->phy_version); + + return 0; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -1037,6 +1048,7 @@ static struct wlcore_ops wl18xx_ops = { .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, .get_mac = wl18xx_get_mac, .debugfs_init = wl18xx_debugfs_init, + .handle_static_data = wl18xx_handle_static_data, }; /* HT cap appropriate for wide channels */ @@ -1092,6 +1104,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); + wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); memcpy(&wl->ht_cap, &wl18xx_ht_cap, sizeof(wl18xx_ht_cap)); if (ht_mode_param && !strcmp(ht_mode_param, "mimo")) memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 75abb5b48c6e..34e202bc2bb3 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -56,6 +56,12 @@ struct wl18xx_fw_status_priv { u8 padding[2]; }; +#define WL18XX_PHY_VERSION_MAX_LEN 20 + +struct wl18xx_static_data_priv { + char phy_version[WL18XX_PHY_VERSION_MAX_LEN]; +}; + struct wl18xx_clk_cfg { u32 n; u32 m; -- cgit v1.2.3 From 06bba80e711b9b66e9316d4d228b55f768c27749 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:12 +0300 Subject: wlcore: print the interrupt status when recovery is triggered In some cases it may be useful for debugging to check what is the status of the interrupt register when a hardware recovery happens. Print the contents of REG_INTERRUPT_NO_CLEAR (aka. HINT_STS_RAW) when recovery starts. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 9589aa956bf7..00a482199a32 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -836,9 +836,11 @@ static void wl1271_recovery_work(struct work_struct *work) /* change partitions momentarily so we can read the FW pc */ wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); - wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", + wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x " + "hint_sts: 0x%08x", wl->chip.fw_ver_str, - wlcore_read_reg(wl, REG_PC_ON_RECOVERY)); + wlcore_read_reg(wl, REG_PC_ON_RECOVERY), + wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR)); wlcore_set_partition(wl, &wl->ptable[PART_WORK]); BUG_ON(bug_on_recovery && -- cgit v1.2.3 From 174a73034cdbe2bb2784c9963e75d196364c348e Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:13 +0300 Subject: wl18xx: don't use MIMO when ht_mode is set to wide If the wl18xx module is loaded with ht_mode=wide (the default), we shouldn't use MIMO rates when the channel type is not HT40. Fix this by checking the ht_mode before deciding which rates to used. Additionally, set the ht_mode parameter explicitly to "wide" as the default. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 8262a4f7a10f..6a81edf702eb 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -42,7 +42,7 @@ #define WL18XX_RX_CHECKSUM_MASK 0x40 -static char *ht_mode_param; +static char *ht_mode_param = "wide"; static char *board_type_param = "hdk"; static bool dc2dc_param = false; static int n_antennas_2_param = 1; @@ -941,11 +941,12 @@ static u32 wl18xx_sta_get_ap_rate_mask(struct wl1271 *wl, static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - if (wlvif->channel_type == NL80211_CHAN_HT40MINUS || - wlvif->channel_type == NL80211_CHAN_HT40PLUS) { + if ((wlvif->channel_type == NL80211_CHAN_HT40MINUS || + wlvif->channel_type == NL80211_CHAN_HT40PLUS) && + !strcmp(ht_mode_param, "wide")) { wl1271_debug(DEBUG_ACX, "using wide channel rate mask"); return CONF_TX_RATE_USE_WIDE_CHAN; - } else { + } else if (!strcmp(ht_mode_param, "mimo")) { wl1271_debug(DEBUG_ACX, "using MIMO rate mask"); /* @@ -959,6 +960,8 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, return CONF_TX_MIMO_RATES & ~CONF_HW_BIT_RATE_MCS_13; return CONF_TX_MIMO_RATES; + } else { + return 0; } } @@ -1106,7 +1109,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); memcpy(&wl->ht_cap, &wl18xx_ht_cap, sizeof(wl18xx_ht_cap)); - if (ht_mode_param && !strcmp(ht_mode_param, "mimo")) + if (!strcmp(ht_mode_param, "mimo")) memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, sizeof(wl18xx_mimo_ht_cap)); -- cgit v1.2.3 From 60462b4885450410df03cf3829367b285baf9ab8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:14 +0300 Subject: wlcore: use proper values for supported local rates We were setting all the rates bits when starting the AP role. Instead of doing this, we should set only the rates we really support (eg. MIMO rates or wide-channel rates). This commit changes that so that we always use the default rates (basic rates + MCS0-7) and add the values returned by the ap_get_mimo_wide_rate_mask operation. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/cmd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 5c4756047098..5b14446ece4a 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -36,6 +36,7 @@ #include "cmd.h" #include "event.h" #include "tx.h" +#include "hw_ops.h" #define WL1271_CMD_FAST_POLL_COUNT 50 @@ -500,6 +501,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) struct wl12xx_cmd_role_start *cmd; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + u32 supported_rates; int ret; wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id); @@ -550,7 +552,13 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len); } - cmd->ap.local_rates = cpu_to_le32(0xffffffff); + supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES | + wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); + + wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x", + supported_rates); + + cmd->ap.local_rates = cpu_to_le32(supported_rates); switch (wlvif->band) { case IEEE80211_BAND_2GHZ: -- cgit v1.2.3 From 8334271882b46e4bc15337ed27428be121156165 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:15 +0300 Subject: wl18xx: add module parameter to force SISO 20MHz In some cases it may be useful to force narrow-band SISO channels. Add a new value to the ht_mode module parameter to force the device to operate in SISO 20MHz. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 38 +++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 6a81edf702eb..07955984face 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1055,7 +1055,7 @@ static struct wlcore_ops wl18xx_ops = { }; /* HT cap appropriate for wide channels */ -static struct ieee80211_sta_ht_cap wl18xx_ht_cap = { +static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = { .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40, .ht_supported = true, @@ -1068,6 +1068,19 @@ static struct ieee80211_sta_ht_cap wl18xx_ht_cap = { }, }; +/* HT cap appropriate for SISO 20 */ +static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { + .cap = IEEE80211_HT_CAP_SGI_20, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, +}; + /* HT cap appropriate for MIMO rates in 20mhz channel */ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap = { .cap = IEEE80211_HT_CAP_SGI_20, @@ -1108,10 +1121,20 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); - memcpy(&wl->ht_cap, &wl18xx_ht_cap, sizeof(wl18xx_ht_cap)); - if (!strcmp(ht_mode_param, "mimo")) + + if (!strcmp(ht_mode_param, "wide")) { + memcpy(&wl->ht_cap, &wl18xx_siso40_ht_cap, + sizeof(wl18xx_siso40_ht_cap)); + } else if (!strcmp(ht_mode_param, "mimo")) { memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, sizeof(wl18xx_mimo_ht_cap)); + } else if (!strcmp(ht_mode_param, "siso20")) { + memcpy(&wl->ht_cap, &wl18xx_siso20_ht_cap, + sizeof(wl18xx_siso20_ht_cap)); + } else { + wl1271_error("invalid ht_mode '%s'", ht_mode_param); + goto out_free; + } wl18xx_conf_init(wl); @@ -1131,8 +1154,7 @@ int __devinit wl18xx_probe(struct platform_device *pdev) priv->conf.phy.low_band_component_type = 0x06; } else { wl1271_error("invalid board type '%s'", board_type_param); - wlcore_free_hw(wl); - return -EINVAL; + goto out_free; } if (!checksum_param) { @@ -1143,6 +1165,10 @@ int __devinit wl18xx_probe(struct platform_device *pdev) wl->enable_11a = enable_11a_param; return wlcore_probe(wl, pdev); + +out_free: + wlcore_free_hw(wl); + return -EINVAL; } static const struct platform_device_id wl18xx_id_table[] __devinitconst = { @@ -1174,7 +1200,7 @@ static void __exit wl18xx_exit(void) module_exit(wl18xx_exit); module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); -MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or mimo"); +MODULE_PARM_DESC(ht_mode, "Force HT mode: wide (default), mimo or siso20"); module_param_named(board_type, board_type_param, charp, S_IRUSR); MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or " -- cgit v1.2.3 From 5add82edd14d9bf051e06588ac65c7e2182bd330 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:16 +0300 Subject: wl18xx: add power limit reference value to mac_and_phy settings With more recent PHY firmware versions (>8.1.0.0.116), we need to use the correct value for the pwr_limit_reference_11_abg parameter when setting the mac_and_phy options. For now we use a hardcoded 0xc8 as the value. This will be moved to the configuration binary when it gets implemented. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/conf.h | 1 + drivers/net/wireless/ti/wl18xx/main.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index a6058fb86e7f..ffad302b6cb7 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -43,6 +43,7 @@ struct wl18xx_conf_phy { u8 primary_clock_setting_time; u8 clock_valid_on_wake_up; u8 secondary_clock_setting_time; + u8 pwr_limit_reference_11_abg; }; struct wl18xx_priv_conf { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 07955984face..ab3dd118f7de 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -501,6 +501,7 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .enable_clpc = 0x00, .enable_tx_low_pwr_on_siso_rdl = 0x00, .rx_profile = 0x00, + .pwr_limit_reference_11_abg = 0xc8, }, }; @@ -726,6 +727,8 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) phy->clock_valid_on_wake_up; params.secondary_clock_setting_time = phy->secondary_clock_setting_time; + params.pwr_limit_reference_11_abg = + phy->pwr_limit_reference_11_abg; params.board_type = priv->board_type; -- cgit v1.2.3 From 858403ab4434a3acd59eb892a49e1dbea30a2fcf Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:17 +0300 Subject: wl18xx: export low/high band component values as module params We use hardcoded values for the different board types. In some cases we may need to override the defaults, so export the values as module params. If not defined, the defaults for the specified board type will be used. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index ab3dd118f7de..6baeb26b79c8 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -49,6 +49,10 @@ static int n_antennas_2_param = 1; static int n_antennas_5_param = 1; static bool checksum_param = true; static bool enable_11a_param = true; +static int low_band_component = -1; +static int low_band_component_type = -1; +static int high_band_component = -1; +static int high_band_component_type = -1; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -1160,6 +1164,32 @@ int __devinit wl18xx_probe(struct platform_device *pdev) goto out_free; } + /* + * If the module param is not set, update it with the one from + * conf. If it is set, overwrite conf with it. + */ + if (low_band_component == -1) + low_band_component = priv->conf.phy.low_band_component; + else + priv->conf.phy.low_band_component = low_band_component; + if (low_band_component_type == -1) + low_band_component_type = + priv->conf.phy.low_band_component_type; + else + priv->conf.phy.low_band_component_type = + low_band_component_type; + + if (high_band_component == -1) + high_band_component = priv->conf.phy.high_band_component; + else + priv->conf.phy.high_band_component = high_band_component; + if (high_band_component_type == -1) + high_band_component_type = + priv->conf.phy.high_band_component_type; + else + priv->conf.phy.high_band_component_type = + high_band_component_type; + if (!checksum_param) { wl18xx_ops.set_rx_csum = NULL; wl18xx_ops.init_vif = NULL; @@ -1224,6 +1254,22 @@ MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to true)"); module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR); MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)"); +module_param(low_band_component, uint, S_IRUSR); +MODULE_PARM_DESC(low_band_component, "Low band component: u8 " + "(default is 0x01)"); + +module_param(low_band_component_type, uint, S_IRUSR); +MODULE_PARM_DESC(low_band_component_type, "Low band component type: u8 " + "(default is 0x05 or 0x06 depending on the board_type)"); + +module_param(high_band_component, uint, S_IRUSR); +MODULE_PARM_DESC(high_band_component, "High band component: u8, " + "(default is 0x01)"); + +module_param(high_band_component_type, uint, S_IRUSR); +MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 " + "(default is 0x09)"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From 7b03c306da4a5ae415036a16b1a5844ca42e2778 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:18 +0300 Subject: wl18xx: export pwr_limit_reference_11_abg value as a module parameter Yet another temporary module parameter requested by the firmware team. This will be replaced by the conf binary. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 6baeb26b79c8..524f44c4b991 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -53,6 +53,7 @@ static int low_band_component = -1; static int low_band_component_type = -1; static int high_band_component = -1; static int high_band_component_type = -1; +static int pwr_limit_reference_11_abg = -1; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -1190,6 +1191,13 @@ int __devinit wl18xx_probe(struct platform_device *pdev) priv->conf.phy.high_band_component_type = high_band_component_type; + if (pwr_limit_reference_11_abg == -1) + pwr_limit_reference_11_abg = + priv->conf.phy.pwr_limit_reference_11_abg; + else + priv->conf.phy.pwr_limit_reference_11_abg = + pwr_limit_reference_11_abg; + if (!checksum_param) { wl18xx_ops.set_rx_csum = NULL; wl18xx_ops.init_vif = NULL; @@ -1270,6 +1278,10 @@ module_param(high_band_component_type, uint, S_IRUSR); MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 " "(default is 0x09)"); +module_param(pwr_limit_reference_11_abg, uint, S_IRUSR); +MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 " + "(default is 0xc8)"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From ad62d81a9e5262555de0501329bd635f5886124f Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:19 +0300 Subject: wlcore/wl12xx/wl18xx: move lower driver debugfs to a subdir Instead of adding more files from the lower drivers into the same directory in debugfs as wlcore, we now add a subdirectory for the lower driver. This makes things a bit easier, because we can quickly see where the debugfs entry is implemented and what is specific to the lower driver. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl12xx/debugfs.c | 10 ++++++++-- drivers/net/wireless/ti/wl12xx/main.c | 7 +------ drivers/net/wireless/ti/wl18xx/debugfs.c | 10 ++++++++-- drivers/net/wireless/ti/wl18xx/main.c | 7 +------ 4 files changed, 18 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/debugfs.c b/drivers/net/wireless/ti/wl12xx/debugfs.c index 311703d5af84..0521cbf858cf 100644 --- a/drivers/net/wireless/ti/wl12xx/debugfs.c +++ b/drivers/net/wireless/ti/wl12xx/debugfs.c @@ -126,9 +126,15 @@ int wl12xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { int ret = 0; - struct dentry *entry, *stats; + struct dentry *entry, *stats, *moddir; - stats = debugfs_create_dir("wl12xx_fw_stats", rootdir); + moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir); + if (!moddir || IS_ERR(moddir)) { + entry = moddir; + goto err; + } + + stats = debugfs_create_dir("fw_stats", moddir); if (!stats || IS_ERR(stats)) { entry = stats; goto err; diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 2ac840783551..ba5afa46a430 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1360,11 +1360,6 @@ out: return ret; } -static int wl12xx_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) -{ - return wl12xx_debugfs_add_files(wl, rootdir); -} - static struct wlcore_ops wl12xx_ops = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, @@ -1387,7 +1382,7 @@ static struct wlcore_ops wl12xx_ops = { .set_tx_desc_csum = wl12xx_set_tx_desc_csum, .set_rx_csum = NULL, .ap_get_mimo_wide_rate_mask = NULL, - .debugfs_init = wl12xx_debugfs_init, + .debugfs_init = wl12xx_debugfs_add_files, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 8354dfc64bd8..f4127bfb9642 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -181,9 +181,15 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { int ret = 0; - struct dentry *entry, *stats; + struct dentry *entry, *stats, *moddir; - stats = debugfs_create_dir("wl18xx_fw_stats", rootdir); + moddir = debugfs_create_dir(KBUILD_MODNAME, rootdir); + if (!moddir || IS_ERR(moddir)) { + entry = moddir; + goto err; + } + + stats = debugfs_create_dir("fw_stats", moddir); if (!stats || IS_ERR(stats)) { entry = stats; goto err; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 524f44c4b991..27cd9b8a6e85 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1022,11 +1022,6 @@ static void wl18xx_get_mac(struct wl1271 *wl) wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); } -static int wl18xx_debugfs_init(struct wl1271 *wl, struct dentry *rootdir) -{ - return wl18xx_debugfs_add_files(wl, rootdir); -} - static int wl18xx_handle_static_data(struct wl1271 *wl, struct wl1271_static_data *static_data) { @@ -1058,7 +1053,7 @@ static struct wlcore_ops wl18xx_ops = { .sta_get_ap_rate_mask = wl18xx_sta_get_ap_rate_mask, .ap_get_mimo_wide_rate_mask = wl18xx_ap_get_mimo_wide_rate_mask, .get_mac = wl18xx_get_mac, - .debugfs_init = wl18xx_debugfs_init, + .debugfs_init = wl18xx_debugfs_add_files, .handle_static_data = wl18xx_handle_static_data, }; -- cgit v1.2.3 From 5a7589be344580f0ed186ab66819bf5671d3f145 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:20 +0300 Subject: wlcore: increase aggregation buffer size by one page With 4 pages (16Kb), we can't fit 10 frames in the aggregation buffer during iperf. This is the optimal for the firmware. Thus, increase the buffer size by one page. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/wlcore_i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 45c07dc0df5d..83c9869105c3 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -89,7 +89,7 @@ #define WL1271_AP_BSS_INDEX 0 #define WL1271_AP_DEF_BEACON_EXP 20 -#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) +#define WL1271_AGGR_BUFFER_SIZE (5 * PAGE_SIZE) enum wl1271_state { WL1271_STATE_OFF, -- cgit v1.2.3 From 3507efa08417f416a8b8a64ab701ad5df37ae077 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:21 +0300 Subject: wl18xx: increase tx_ba_win_size to 64 Now the firmware can support TX block ack sessions with 64 frames. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 27cd9b8a6e85..5053c054aaad 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -408,7 +408,7 @@ static struct wlcore_conf wl18xx_conf = { }, .ht = { .rx_ba_win_size = 10, - .tx_ba_win_size = 10, + .tx_ba_win_size = 64, .inactivity_timeout = 10000, .tx_ba_tid_bitmap = CONF_TX_BA_ENABLED_TID_BITMAP, }, -- cgit v1.2.3 From c5d94169e8189d02dfbd6143411908357865d777 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 May 2012 12:14:22 +0300 Subject: wl18xx: use new fw stats structures Some of the structures were updated, other structures had a few missing values and a few new ones were added. Change the driver structs accordingly. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wl18xx/acx.h | 156 ++++++++----------- drivers/net/wireless/ti/wl18xx/debugfs.c | 248 +++++++++++++------------------ 2 files changed, 169 insertions(+), 235 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index c2ccf70bcfb3..cb6fd85d077f 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -71,13 +71,8 @@ struct wl18xx_acx_debug_stats { } __packed; struct wl18xx_acx_ring_stats { - u32 tx_procs; u32 prepared_descs; - u32 tx_xfr; - u32 tx_dma; u32 tx_cmplt; - u32 rx_procs; - u32 rx_data; } __packed; struct wl18xx_acx_tx_stats { @@ -98,60 +93,53 @@ struct wl18xx_acx_tx_stats { u32 tx_retry_data; u32 tx_exch_pending; u32 tx_exch_expiry; - u32 tx_exch_mismatch; u32 tx_done_template; u32 tx_done_data; u32 tx_done_int_template; - u32 tx_pre_xfr; - u32 tx_xfr; - u32 tx_xfr_out_of_mem; - u32 tx_dma_programmed; - u32 tx_dma_done; + u32 tx_frame_checksum; + u32 tx_checksum_result; + u32 frag_called; + u32 frag_mpdu_alloc_failed; + u32 frag_init_called; + u32 frag_in_process_called; + u32 frag_tkip_called; + u32 frag_key_not_found; + u32 frag_need_fragmentation; + u32 frag_bad_mblk_num; + u32 frag_failed; + u32 frag_cache_hit; + u32 frag_cache_miss; } __packed; struct wl18xx_acx_rx_stats { u32 rx_out_of_mem; u32 rx_hdr_overflow; - u32 rx_hw_stuck; u32 rx_dropped_frame; - u32 rx_complete_dropped_frame; - u32 rx_alloc_frame; - u32 rx_done_queue; + u32 rx_done_stage; u32 rx_done; u32 rx_defrag; u32 rx_defrag_end; - u32 rx_mic; - u32 rx_mic_end; - u32 rx_xfr; - u32 rx_xfr_end; u32 rx_cmplt; u32 rx_pre_complt; u32 rx_cmplt_task; u32 rx_phy_hdr; u32 rx_timeout; -} __packed; - -struct wl18xx_acx_dma_stats { - u32 rx_dma_errors; - u32 tx_dma_errors; + u32 rx_frame_checksum; + u32 rx_checksum_result; + u32 defrag_called; + u32 defrag_init_called; + u32 defrag_in_process_called; + u32 defrag_tkip_called; + u32 defrag_need_defrag; + u32 defrag_decrypt_failed; + u32 decrypt_key_not_found; + u32 defrag_need_decrypt; } __packed; struct wl18xx_acx_isr_stats { u32 irqs; } __packed; -struct wl18xx_acx_wep_stats { - u32 wep_add_key_count; - u32 wep_default_key_count; - u32 wep_key_not_found; - u32 wep_decrypt_fail; - u32 wep_encrypt_fail; - u32 wep_dec_packets; - u32 wep_dec_interrupt; - u32 wep_enc_packets; - u32 wep_enc_interrupts; -} __packed; - #define PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD 10 struct wl18xx_acx_pwr_stats { @@ -162,29 +150,6 @@ struct wl18xx_acx_pwr_stats { u32 rcvd_awake_bcns_cnt; } __packed; -struct wl18xx_acx_mic_stats { - u32 mic_rx_pkts; - u32 mic_calc_failure; -} __packed; - -struct wl18xx_acx_aes_stats { - u32 aes_encrypt_fail; - u32 aes_decrypt_fail; - u32 aes_encrypt_packets; - u32 aes_decrypt_packets; - u32 aes_encrypt_interrupt; - u32 aes_decrypt_interrupt; -} __packed; - -struct wl18xx_acx_gem_stats { - u32 gem_encrypt_fail; - u32 gem_decrypt_fail; - u32 gem_encrypt_packets; - u32 gem_decrypt_packets; - u32 gem_encrypt_interrupt; - u32 gem_decrypt_interrupt; -} __packed; - struct wl18xx_acx_event_stats { u32 calibration; u32 rx_mismatch; @@ -210,31 +175,44 @@ struct wl18xx_acx_rx_filter_stats { u32 protection_filter; } __packed; -struct wl18xx_acx_calibration_stats { - u32 init_cal_total; - u32 init_radio_bands_fail; - u32 init_set_params; - u32 init_tx_clpc_fail; - u32 init_rx_iw_mm_fail; - u32 tune_cal_total; - u32 tune_drpw_rtrim_fail; - u32 tune_drpw_pd_buf_fail; - u32 tune_drpw_tx_mix_freq_fail; - u32 tune_drpw_ta_cal; - u32 tune_drpw_rx_if_2_gain; - u32 tune_drpw_rx_dac; - u32 tune_drpw_chan_tune; - u32 tune_drpw_rx_tx_lpf; - u32 tune_drpw_lna_tank; - u32 tune_tx_lo_leak_fail; - u32 tune_tx_iq_mm_fail; - u32 tune_tx_pdet_fail; - u32 tune_tx_ppa_fail; - u32 tune_tx_clpc_fail; - u32 tune_rx_ana_dc_fail; - u32 tune_rx_dig_dc_fail; /* check if this is needed */ - u32 tune_rx_iq_mm_fail; - u32 cal_state_fail; +struct wl18xx_acx_rx_rate_stats { + u32 rx_frames_per_rates[50]; +} __packed; + +#define AGGR_STATS_TX_SIZE_LEN 11 +#define AGGR_STATS_RX_SIZE_LEN 11 + +struct wl18xx_acx_aggr_stats { + u32 tx_size[AGGR_STATS_TX_SIZE_LEN]; + u32 rx_size[AGGR_STATS_RX_SIZE_LEN]; +} __packed; + +struct wl18xx_acx_pipeline_stats { + u32 hs_tx_stat_fifo_int; + u32 hs_rx_stat_fifo_int; + u32 tcp_tx_stat_fifo_int; + u32 tcp_rx_stat_fifo_int; + u32 enc_tx_stat_fifo_int; + u32 enc_rx_stat_fifo_int; + u32 rx_complete_stat_fifo_int; + u32 pre_proc_swi; + u32 post_proc_swi; + u32 sec_frag_swi; + u32 pre_to_defrag_swi; + u32 defrag_to_csum_swi; + u32 csum_to_rx_xfer_swi; + u32 dec_packet_in; + u32 dec_packet_in_fifo_full; + u32 dec_packet_out; + u32 cs_rx_packet_in; + u32 cs_rx_packet_out; +} __packed; + +struct wl18xx_acx_mem_stats { + u32 rx_free_mem_blks; + u32 tx_free_mem_blks; + u32 fwlog_free_mem_blks; + u32 fw_gen_free_mem_blks; } __packed; struct wl18xx_acx_statistics { @@ -244,17 +222,15 @@ struct wl18xx_acx_statistics { struct wl18xx_acx_debug_stats debug; struct wl18xx_acx_tx_stats tx; struct wl18xx_acx_rx_stats rx; - struct wl18xx_acx_dma_stats dma; struct wl18xx_acx_isr_stats isr; - struct wl18xx_acx_wep_stats wep; struct wl18xx_acx_pwr_stats pwr; - struct wl18xx_acx_aes_stats aes; - struct wl18xx_acx_mic_stats mic; struct wl18xx_acx_event_stats event; struct wl18xx_acx_ps_poll_stats ps_poll; struct wl18xx_acx_rx_filter_stats rx_filter; - struct wl18xx_acx_calibration_stats calibration; - struct wl18xx_acx_gem_stats gem; + struct wl18xx_acx_rx_rate_stats rx_rate; + struct wl18xx_acx_aggr_stats aggr_size; + struct wl18xx_acx_pipeline_stats pipeline; + struct wl18xx_acx_mem_stats mem; } __packed; int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index f4127bfb9642..93c625b23a2f 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -32,6 +32,7 @@ #define WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c) \ DEBUGFS_FWSTATS_FILE_ARRAY(a, b, c, wl18xx_acx_statistics) + WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug1, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug2, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug3, "%u"); @@ -39,13 +40,8 @@ WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_procs, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ring, prepared_descs, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_xfr, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_dma, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_cmplt, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, rx_procs, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, rx_data, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u"); @@ -64,51 +60,47 @@ WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_template, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_retry_data, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_pending, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_expiry, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_exch_mismatch, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_template, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_data, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_done_int_template, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_pre_xfr, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_xfr, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_xfr_out_of_mem, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_dma_programmed, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_dma_done, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_frame_checksum, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_checksum_result, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_mpdu_alloc_failed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_init_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_in_process_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_tkip_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_key_not_found, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_need_fragmentation, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_bad_mblk_num, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mem, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hw_stuck, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_complete_dropped_frame, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_alloc_frame, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done_queue, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_defrag_end, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_mic, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_mic_end, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_xfr, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_xfr_end, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u"); - -WL18XX_DEBUGFS_FWSTATS_FILE(dma, rx_dma_errors, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(dma, tx_dma_errors, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_frame_checksum, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_checksum_result, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_init_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_in_process_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_tkip_called, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_add_key_count, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_default_key_count, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_key_not_found, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_decrypt_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_encrypt_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_dec_packets, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_dec_interrupt, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_enc_packets, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(wep, wep_enc_interrupts, "%u"); - WL18XX_DEBUGFS_FWSTATS_FILE(pwr, missing_bcns_cnt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_bcns_cnt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, connection_out_of_sync, "%u"); @@ -116,22 +108,6 @@ WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread, PWR_STAT_MAX_CONT_MISSED_BCNS_SPREAD); WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(mic, mic_rx_pkts, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(mic, mic_calc_failure, "%u"); - -WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_encrypt_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_decrypt_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_encrypt_packets, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_decrypt_packets, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_encrypt_interrupt, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(aes, aes_decrypt_interrupt, "%u"); - -WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_encrypt_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_decrypt_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_encrypt_packets, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_decrypt_packets, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_encrypt_interrupt, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(gem, gem_decrypt_interrupt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u"); @@ -152,30 +128,35 @@ WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_cal_total, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_radio_bands_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_set_params, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_tx_clpc_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, init_rx_iw_mm_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_cal_total, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rtrim_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_pd_buf_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_tx_mix_freq_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_ta_cal, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rx_if_2_gain, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rx_dac, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_chan_tune, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_rx_tx_lpf, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_drpw_lna_tank, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_lo_leak_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_iq_mm_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_pdet_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_ppa_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_tx_clpc_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_rx_ana_dc_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_rx_dig_dc_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, tune_rx_iq_mm_fail, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(calibration, cal_state_fail, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_size, + AGGR_STATS_TX_SIZE_LEN); +WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size, + AGGR_STATS_RX_SIZE_LEN); + +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, hs_tx_stat_fifo_int, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_tx_stat_fifo_int, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, tcp_rx_stat_fifo_int, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_tx_stat_fifo_int, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, enc_rx_stat_fifo_int, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, rx_complete_stat_fifo_int, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_proc_swi, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, post_proc_swi, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, sec_frag_swi, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, pre_to_defrag_swi, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, defrag_to_csum_swi, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, csum_to_rx_xfer_swi, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_in_fifo_full, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_in, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_out, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(mem, rx_free_mem_blks, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u"); int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) @@ -202,13 +183,8 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(debug, debug5); DEBUGFS_FWSTATS_ADD(debug, debug6); - DEBUGFS_FWSTATS_ADD(ring, tx_procs); DEBUGFS_FWSTATS_ADD(ring, prepared_descs); - DEBUGFS_FWSTATS_ADD(ring, tx_xfr); - DEBUGFS_FWSTATS_ADD(ring, tx_dma); DEBUGFS_FWSTATS_ADD(ring, tx_cmplt); - DEBUGFS_FWSTATS_ADD(ring, rx_procs); - DEBUGFS_FWSTATS_ADD(ring, rx_data); DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared); DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared); @@ -227,74 +203,53 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(tx, tx_retry_data); DEBUGFS_FWSTATS_ADD(tx, tx_exch_pending); DEBUGFS_FWSTATS_ADD(tx, tx_exch_expiry); - DEBUGFS_FWSTATS_ADD(tx, tx_exch_mismatch); DEBUGFS_FWSTATS_ADD(tx, tx_done_template); DEBUGFS_FWSTATS_ADD(tx, tx_done_data); DEBUGFS_FWSTATS_ADD(tx, tx_done_int_template); - DEBUGFS_FWSTATS_ADD(tx, tx_pre_xfr); - DEBUGFS_FWSTATS_ADD(tx, tx_xfr); - DEBUGFS_FWSTATS_ADD(tx, tx_xfr_out_of_mem); - DEBUGFS_FWSTATS_ADD(tx, tx_dma_programmed); - DEBUGFS_FWSTATS_ADD(tx, tx_dma_done); + DEBUGFS_FWSTATS_ADD(tx, tx_frame_checksum); + DEBUGFS_FWSTATS_ADD(tx, tx_checksum_result); + DEBUGFS_FWSTATS_ADD(tx, frag_called); + DEBUGFS_FWSTATS_ADD(tx, frag_mpdu_alloc_failed); + DEBUGFS_FWSTATS_ADD(tx, frag_init_called); + DEBUGFS_FWSTATS_ADD(tx, frag_in_process_called); + DEBUGFS_FWSTATS_ADD(tx, frag_tkip_called); + DEBUGFS_FWSTATS_ADD(tx, frag_key_not_found); + DEBUGFS_FWSTATS_ADD(tx, frag_need_fragmentation); + DEBUGFS_FWSTATS_ADD(tx, frag_bad_mblk_num); + DEBUGFS_FWSTATS_ADD(tx, frag_failed); + DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit); + DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss); DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mem); DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow); - DEBUGFS_FWSTATS_ADD(rx, rx_hw_stuck); DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame); - DEBUGFS_FWSTATS_ADD(rx, rx_complete_dropped_frame); - DEBUGFS_FWSTATS_ADD(rx, rx_alloc_frame); - DEBUGFS_FWSTATS_ADD(rx, rx_done_queue); DEBUGFS_FWSTATS_ADD(rx, rx_done); DEBUGFS_FWSTATS_ADD(rx, rx_defrag); DEBUGFS_FWSTATS_ADD(rx, rx_defrag_end); - DEBUGFS_FWSTATS_ADD(rx, rx_mic); - DEBUGFS_FWSTATS_ADD(rx, rx_mic_end); - DEBUGFS_FWSTATS_ADD(rx, rx_xfr); - DEBUGFS_FWSTATS_ADD(rx, rx_xfr_end); DEBUGFS_FWSTATS_ADD(rx, rx_cmplt); DEBUGFS_FWSTATS_ADD(rx, rx_pre_complt); DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task); DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr); DEBUGFS_FWSTATS_ADD(rx, rx_timeout); - - DEBUGFS_FWSTATS_ADD(dma, rx_dma_errors); - DEBUGFS_FWSTATS_ADD(dma, tx_dma_errors); + DEBUGFS_FWSTATS_ADD(rx, rx_frame_checksum); + DEBUGFS_FWSTATS_ADD(rx, rx_checksum_result); + DEBUGFS_FWSTATS_ADD(rx, defrag_called); + DEBUGFS_FWSTATS_ADD(rx, defrag_init_called); + DEBUGFS_FWSTATS_ADD(rx, defrag_in_process_called); + DEBUGFS_FWSTATS_ADD(rx, defrag_tkip_called); + DEBUGFS_FWSTATS_ADD(rx, defrag_need_defrag); + DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed); + DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found); + DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt); DEBUGFS_FWSTATS_ADD(isr, irqs); - DEBUGFS_FWSTATS_ADD(wep, wep_add_key_count); - DEBUGFS_FWSTATS_ADD(wep, wep_default_key_count); - DEBUGFS_FWSTATS_ADD(wep, wep_key_not_found); - DEBUGFS_FWSTATS_ADD(wep, wep_decrypt_fail); - DEBUGFS_FWSTATS_ADD(wep, wep_encrypt_fail); - DEBUGFS_FWSTATS_ADD(wep, wep_dec_packets); - DEBUGFS_FWSTATS_ADD(wep, wep_dec_interrupt); - DEBUGFS_FWSTATS_ADD(wep, wep_enc_packets); - DEBUGFS_FWSTATS_ADD(wep, wep_enc_interrupts); - DEBUGFS_FWSTATS_ADD(pwr, missing_bcns_cnt); DEBUGFS_FWSTATS_ADD(pwr, rcvd_bcns_cnt); DEBUGFS_FWSTATS_ADD(pwr, connection_out_of_sync); DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread); DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt); - DEBUGFS_FWSTATS_ADD(mic, mic_rx_pkts); - DEBUGFS_FWSTATS_ADD(mic, mic_calc_failure); - - DEBUGFS_FWSTATS_ADD(aes, aes_encrypt_fail); - DEBUGFS_FWSTATS_ADD(aes, aes_decrypt_fail); - DEBUGFS_FWSTATS_ADD(aes, aes_encrypt_packets); - DEBUGFS_FWSTATS_ADD(aes, aes_decrypt_packets); - DEBUGFS_FWSTATS_ADD(aes, aes_encrypt_interrupt); - DEBUGFS_FWSTATS_ADD(aes, aes_decrypt_interrupt); - - DEBUGFS_FWSTATS_ADD(gem, gem_encrypt_fail); - DEBUGFS_FWSTATS_ADD(gem, gem_decrypt_fail); - DEBUGFS_FWSTATS_ADD(gem, gem_encrypt_packets); - DEBUGFS_FWSTATS_ADD(gem, gem_decrypt_packets); - DEBUGFS_FWSTATS_ADD(gem, gem_encrypt_interrupt); - DEBUGFS_FWSTATS_ADD(gem, gem_decrypt_interrupt); - DEBUGFS_FWSTATS_ADD(event, calibration); DEBUGFS_FWSTATS_ADD(event, rx_mismatch); DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); @@ -314,30 +269,33 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter); DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter); - DEBUGFS_FWSTATS_ADD(calibration, init_cal_total); - DEBUGFS_FWSTATS_ADD(calibration, init_radio_bands_fail); - DEBUGFS_FWSTATS_ADD(calibration, init_set_params); - DEBUGFS_FWSTATS_ADD(calibration, init_tx_clpc_fail); - DEBUGFS_FWSTATS_ADD(calibration, init_rx_iw_mm_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_cal_total); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rtrim_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_pd_buf_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_tx_mix_freq_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_ta_cal); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rx_if_2_gain); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rx_dac); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_chan_tune); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_rx_tx_lpf); - DEBUGFS_FWSTATS_ADD(calibration, tune_drpw_lna_tank); - DEBUGFS_FWSTATS_ADD(calibration, tune_tx_lo_leak_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_tx_iq_mm_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_tx_pdet_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_tx_ppa_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_tx_clpc_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_rx_ana_dc_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_rx_dig_dc_fail); - DEBUGFS_FWSTATS_ADD(calibration, tune_rx_iq_mm_fail); - DEBUGFS_FWSTATS_ADD(calibration, cal_state_fail); + DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates); + + DEBUGFS_FWSTATS_ADD(aggr_size, tx_size); + DEBUGFS_FWSTATS_ADD(aggr_size, rx_size); + + DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int); + DEBUGFS_FWSTATS_ADD(pipeline, tcp_tx_stat_fifo_int); + DEBUGFS_FWSTATS_ADD(pipeline, tcp_rx_stat_fifo_int); + DEBUGFS_FWSTATS_ADD(pipeline, enc_tx_stat_fifo_int); + DEBUGFS_FWSTATS_ADD(pipeline, enc_rx_stat_fifo_int); + DEBUGFS_FWSTATS_ADD(pipeline, rx_complete_stat_fifo_int); + DEBUGFS_FWSTATS_ADD(pipeline, pre_proc_swi); + DEBUGFS_FWSTATS_ADD(pipeline, post_proc_swi); + DEBUGFS_FWSTATS_ADD(pipeline, sec_frag_swi); + DEBUGFS_FWSTATS_ADD(pipeline, pre_to_defrag_swi); + DEBUGFS_FWSTATS_ADD(pipeline, defrag_to_csum_swi); + DEBUGFS_FWSTATS_ADD(pipeline, csum_to_rx_xfer_swi); + DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in); + DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_in_fifo_full); + DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out); + DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_in); + DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_out); + + DEBUGFS_FWSTATS_ADD(mem, rx_free_mem_blks); + DEBUGFS_FWSTATS_ADD(mem, tx_free_mem_blks); + DEBUGFS_FWSTATS_ADD(mem, fwlog_free_mem_blks); + DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks); return 0; -- cgit v1.2.3 From 3d62eb5a7eb8d0be68442e119583508926731887 Mon Sep 17 00:00:00 2001 From: Assaf Azulay Date: Thu, 10 May 2012 12:14:23 +0300 Subject: wl18xx: change default tcp_checksum to false as tcp check sum is going to be removed from firmware, and as there is a problem with getting dns in security when checksum is enabled, it was decided to disable it by default. for none security modes it can be enabled by module paramenter. Signed-off-by: Assaf Azulay Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 5053c054aaad..dda9c18e5506 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -47,7 +47,7 @@ static char *board_type_param = "hdk"; static bool dc2dc_param = false; static int n_antennas_2_param = 1; static int n_antennas_5_param = 1; -static bool checksum_param = true; +static bool checksum_param = false; static bool enable_11a_param = true; static int low_band_component = -1; static int low_band_component_type = -1; @@ -1252,7 +1252,7 @@ module_param_named(n_antennas_5, n_antennas_5_param, uint, S_IRUSR); MODULE_PARM_DESC(n_antennas_5, "Number of installed 5GHz antennas: 1 (default) or 2"); module_param_named(checksum, checksum_param, bool, S_IRUSR); -MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to true)"); +MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to false)"); module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR); MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)"); -- cgit v1.2.3 From b5d6d9b28ca1fac178e05f185ee38e9c0770e268 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 5 Jun 2012 00:02:25 +0300 Subject: wlcore/wl12xx/wl18xx: don't use TX align quirk for wl127x Commit 4afc37 (wlcore: reorder identify_chip and get_hw_info) broke support for wl127x chips. When we moved the identify_chip operation to an earlier stage (ie. to the probe function), we broke wl127x support because during HW init we would set the WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN. To avoid this, set this quirk in the identify_chip operations and only force it to be unset if the bus module doesn't support it. We were doing the opposite and setting the flag if the bus module supports it. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 10 ++++------ drivers/net/wireless/ti/wl18xx/main.c | 5 +++-- drivers/net/wireless/ti/wlcore/main.c | 7 +++++-- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index ba5afa46a430..67974f6c635a 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -625,9 +625,6 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", wl->chip.id); - /* clear the alignment quirk, since we don't support it */ - wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; - wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; @@ -643,9 +640,6 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl->chip.id); - /* clear the alignment quirk, since we don't support it */ - wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; - wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; wl->plt_fw_name = WL127X_PLT_FW_NAME; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; @@ -664,6 +658,10 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->plt_fw_name = WL128X_PLT_FW_NAME; wl->sr_fw_name = WL128X_FW_NAME_SINGLE; wl->mr_fw_name = WL128X_FW_NAME_MULTI; + + /* wl128x requires TX blocksize alignment */ + wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; + break; case CHIP_ID_1283_PG10: default: diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index dda9c18e5506..2e9b3cb6e074 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -591,8 +591,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl) /* wl18xx uses the same firmware for PLT */ wl->plt_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | - WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | - WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; + WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | + WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | + WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; /* PG 1.0 has some problems with MCS_13, so disable it */ wl->ht_cap.mcs.rx_mask[1] &= ~BIT(5); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 00a482199a32..af00dabf96b7 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -960,9 +960,12 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt) * simplify the code and since the performance impact is * negligible, we use the same block size for all different * chip types. + * + * Check if the bus supports blocksize alignment and, if it + * doesn't, make sure we don't have the quirk. */ - if (wl1271_set_block_size(wl)) - wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; + if (!wl1271_set_block_size(wl)) + wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; /* TODO: make sure the lower driver has set things up correctly */ -- cgit v1.2.3 From bfd6185ddecc6e6f6bd654c053c307c9e49ca391 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Jun 2012 14:29:36 +0100 Subject: regmap: Don't try to map non-existant IRQs If the driver supplied an empty entry in the array of IRQs then return an error rather than trying to do the mapping. This is intended for use with handling chip variants and similar situations. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 4fac4b9be88f..b74e14c4dff4 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -346,6 +346,10 @@ EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); */ int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) { + /* Handle holes in the IRQ list */ + if (!data->chip->irqs[irq].mask) + return -EINVAL; + return irq_create_mapping(data->domain, irq); } EXPORT_SYMBOL_GPL(regmap_irq_get_virq); -- cgit v1.2.3 From a43fd50dc99a5f65505f174eca5a421707d73b4c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Jun 2012 14:34:03 +0100 Subject: regmap: Implement support for wake IRQs Allow chips to provide a bank of registers for controlling the wake state in a similar fashion to the masks and propagate the wake count to the parent interrupt controller. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index b74e14c4dff4..b480b529f020 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -29,9 +29,13 @@ struct regmap_irq_chip_data { int irq_base; struct irq_domain *domain; + int irq; + int wake_count; + unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; + unsigned int *wake_buf; unsigned int irq_reg_stride; }; @@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data) d->chip->mask_base + (i * map->reg_stride)); } + /* If we've changed our wakeup count propagate it to the parent */ + if (d->wake_count < 0) + for (i = d->wake_count; i < 0; i++) + irq_set_irq_wake(d->irq, 0); + else if (d->wake_count > 0) + for (i = 0; i < d->wake_count; i++) + irq_set_irq_wake(d->irq, 1); + + d->wake_count = 0; + mutex_unlock(&d->lock); } @@ -92,12 +106,35 @@ static void regmap_irq_disable(struct irq_data *data) d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } +static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); + + if (!d->chip->wake_base) + return -EINVAL; + + if (on) { + d->wake_buf[irq_data->reg_offset / map->reg_stride] + &= ~irq_data->mask; + d->wake_count++; + } else { + d->wake_buf[irq_data->reg_offset / map->reg_stride] + |= irq_data->mask; + d->wake_count--; + } + + return 0; +} + static struct irq_chip regmap_irq_chip = { .name = "regmap", .irq_bus_lock = regmap_irq_lock, .irq_bus_sync_unlock = regmap_irq_sync_unlock, .irq_disable = regmap_irq_disable, .irq_enable = regmap_irq_enable, + .irq_set_wake = regmap_irq_set_wake, }; static irqreturn_t regmap_irq_thread(int irq, void *d) @@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d->mask_buf_def) goto err_alloc; + if (chip->wake_base) { + d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, + GFP_KERNEL); + if (!d->wake_buf) + goto err_alloc; + } + + d->irq = irq; d->map = map; d->chip = chip; d->irq_base = irq_base; @@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, err_domain: /* Should really dispose of the domain but... */ err_alloc: + kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_buf); @@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) free_irq(irq, d); /* We should unmap the domain but... */ + kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_buf); -- cgit v1.2.3 From d35dc739f6a0b3680a591bd4e831fd3afa912632 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 15 May 2012 17:08:51 +0300 Subject: wlcore: fix sparse warnings related to static functions The "static" modifier was mistakenly forgotten for some functions. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 2e9b3cb6e074..31cd4cc3c1b2 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1098,7 +1098,7 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap = { }, }; -int __devinit wl18xx_probe(struct platform_device *pdev) +static int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; struct ieee80211_hw *hw; -- cgit v1.2.3 From 1ab0f212629462ae2600bb3523dc75b96e8544eb Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 17:08:52 +0300 Subject: wlcore: use the original elp time in forced_ps mode The dynamic PS timeout is meaningless in forced PS mode. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/ps.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 756eee2257b4..958535dee9f3 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -28,6 +28,8 @@ #define WL1271_WAKEUP_TIMEOUT 500 +#define ELP_ENTRY_DELAY 5 + void wl1271_elp_work(struct work_struct *work) { struct delayed_work *dwork; @@ -72,6 +74,7 @@ out: void wl1271_ps_elp_sleep(struct wl1271 *wl) { struct wl12xx_vif *wlvif; + u32 timeout; if (wl->quirks & WLCORE_QUIRK_NO_ELP) return; @@ -89,8 +92,13 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) return; } + if (wl->conf.conn.forced_ps) + timeout = ELP_ENTRY_DELAY; + else + timeout = wl->conf.conn.dynamic_ps_timeout; + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, - msecs_to_jiffies(wl->conf.conn.dynamic_ps_timeout)); + msecs_to_jiffies(timeout)); } int wl1271_ps_elp_wakeup(struct wl1271 *wl) -- cgit v1.2.3 From 6e066921b3970232d5faadcdf33a92f43ec84334 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 15 May 2012 17:08:53 +0300 Subject: wlcore: fix dynamic_ps_timeout time regression In patch d7b63b9fc7ee73e75a4c7fdb899 we have raised the dynamic PS timeout to 200ms to improve user experience. Re-apply the change, since it was reverted in the wlcore split. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 2 +- drivers/net/wireless/ti/wl18xx/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 67974f6c635a..ba14acae1d59 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -238,7 +238,7 @@ static struct wlcore_conf wl12xx_conf = { .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, - .dynamic_ps_timeout = 40, + .dynamic_ps_timeout = 200, .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 31cd4cc3c1b2..bbd935fb5f64 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -361,7 +361,7 @@ static struct wlcore_conf wl18xx_conf = { .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, - .dynamic_ps_timeout = 40, + .dynamic_ps_timeout = 200, .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, -- cgit v1.2.3 From 6b8bf5bc5e99f52334bec1b06b14d28dc595c95a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 15 May 2012 17:08:54 +0300 Subject: wlcore: fixes for connection_loss_work We can't use cancel_delayed_work_sync() from functions that take the wl->mutex, since connection_loss_work also takes the mutex. This might result in a deadlock. Restructure the code so the work is synchronously canceled before taking the mutex. Avoid a bug where we would indefinitely delay the connection loss indication by re-queuing the connection loss work on consecutive beacon loss events. Cc: bartosz.markowski Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/event.c | 15 ++++++++++++--- drivers/net/wireless/ti/wlcore/main.c | 10 +++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 28e2a633c3be..4ed835799178 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -148,15 +148,24 @@ static int wl1271_event_process(struct wl1271 *wl) int delay = wl->conf.conn.synch_fail_thold * wl->conf.conn.bss_lose_timeout; wl1271_info("Beacon loss detected."); - cancel_delayed_work_sync(&wl->connection_loss_work); + + /* + * if the work is already queued, it should take place. We + * don't want to delay the connection loss indication + * any more. + */ ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work, - msecs_to_jiffies(delay)); + msecs_to_jiffies(delay)); } if (vector & REGAINED_BSS_EVENT_ID) { /* TODO: check for multi-role */ wl1271_info("Beacon regained."); - cancel_delayed_work_sync(&wl->connection_loss_work); + cancel_delayed_work(&wl->connection_loss_work); + + /* sanity check - we can't lose and gain the beacon together */ + WARN(vector & BSS_LOSE_EVENT_ID, + "Concurrent beacon loss and gain from FW"); } if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index af00dabf96b7..4be62c93808b 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3712,9 +3712,6 @@ sta_not_found: do_join = true; set_assoc = true; - /* Cancel connection_loss_work */ - cancel_delayed_work_sync(&wl->connection_loss_work); - /* * use basic rates from AP, and determine lowest rate * to use with control frames. @@ -3964,6 +3961,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x", (int)changed); + /* + * make sure to cancel pending disconnections if our association + * state changed + */ + if (!is_ap && (changed & BSS_CHANGED_ASSOC)) + cancel_delayed_work_sync(&wl->connection_loss_work); + mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) -- cgit v1.2.3 From 45b60f7ddd05e38a6835fb93e8dbcc6ef9bf12fa Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 17:08:55 +0300 Subject: wlcore: use correct link for bcast/multicast frames Multicast management frames (e.g. global deauth) should be sent out on the bcast link, rather than the global, which should be used only for pre-added stations (e.g. for auth/assoc resp). Signed-off-by: Eliad Peller Signed-off-by: Eyal Shapira Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/tx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 200d091db6f9..e8a2998408b6 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -148,10 +148,10 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif, return wl->system_hlid; hdr = (struct ieee80211_hdr *)skb->data; - if (ieee80211_is_mgmt(hdr->frame_control)) - return wlvif->ap.global_hlid; - else + if (is_multicast_ether_addr(ieee80211_get_DA(hdr))) return wlvif->ap.bcast_hlid; + else + return wlvif->ap.global_hlid; } } -- cgit v1.2.3 From b515d83a2998c991171e2c533ea06ff723d355a6 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 17:08:56 +0300 Subject: wlcore: flush before stopping AP Make sure the deauth bcast gets sent [Make sure we are AP as well before the flush - Arik] Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 4be62c93808b..1974be0ccd58 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -3968,6 +3968,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (!is_ap && (changed & BSS_CHANGED_ASSOC)) cancel_delayed_work_sync(&wl->connection_loss_work); + if (is_ap && (changed & BSS_CHANGED_BEACON_ENABLED) && + !bss_conf->enable_beacon) + wl1271_tx_flush(wl); + mutex_lock(&wl->mutex); if (unlikely(wl->state == WL1271_STATE_OFF)) -- cgit v1.2.3 From 7b052214e59450b5ed6c708903a7dd9f15f6c12d Mon Sep 17 00:00:00 2001 From: Igal Chernobelsky Date: Tue, 15 May 2012 17:08:57 +0300 Subject: wlcore: modify bss loss parameters Modify default parameters to reduce firmware BSS lose probability in congested environment. [Applied to 18xx configuration as well - Arik] Signed-off-by: Igal Chernobelsky Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 4 ++-- drivers/net/wireless/ti/wl18xx/main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index ba14acae1d59..e400d1987e6c 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -227,8 +227,8 @@ static struct wlcore_conf wl12xx_conf = { .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, }, - .synch_fail_thold = 10, - .bss_lose_timeout = 100, + .synch_fail_thold = 12, + .bss_lose_timeout = 400, .beacon_rx_timeout = 10000, .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index bbd935fb5f64..5a6c4cc9577e 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -350,8 +350,8 @@ static struct wlcore_conf wl18xx_conf = { .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, }, - .synch_fail_thold = 10, - .bss_lose_timeout = 100, + .synch_fail_thold = 12, + .bss_lose_timeout = 400, .beacon_rx_timeout = 10000, .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, -- cgit v1.2.3 From 0a15d9b5896f54f4ad164fd072891019a8a8cfb2 Mon Sep 17 00:00:00 2001 From: Assaf Azulay Date: Tue, 15 May 2012 17:08:58 +0300 Subject: wlcore: increase number of BA sessions to 3 With the new FW (sigle role X.3.8.0.108, multi role X.5.4.0.21) we are supporting 3 RX BA sessions, this change is to support this new ability. Signed-off-by: Assaf Azulay Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/acx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 8b3807b879d3..46c300d4dea4 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -726,7 +726,7 @@ struct wl1271_acx_ht_information { u8 padding[2]; } __packed; -#define RX_BA_MAX_SESSIONS 2 +#define RX_BA_MAX_SESSIONS 3 struct wl1271_acx_ba_initiator_policy { struct acx_header header; -- cgit v1.2.3 From a121a5b8aba4294b1557e1099c4eaa7c6578d7ce Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 15 May 2012 17:08:59 +0300 Subject: wl18xx: add dependency on mac80211 Add a dependency on mac80211 in Kconfig, and also replace some spaces with tabs. Reported-by: Arend van Spriel Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/Kconfig b/drivers/net/wireless/ti/wl18xx/Kconfig index 1451d1f377c9..1cfdb2548821 100644 --- a/drivers/net/wireless/ti/wl18xx/Kconfig +++ b/drivers/net/wireless/ti/wl18xx/Kconfig @@ -1,6 +1,7 @@ config WL18XX - tristate "TI wl18xx support" - select WLCORE - ---help--- + tristate "TI wl18xx support" + depends on MAC80211 + select WLCORE + ---help--- This module adds support for wireless adapters based on TI WiLink 8 chipsets. -- cgit v1.2.3 From bfb92ca1332ce0073cfba5d8a7caee214ed3a787 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 17:09:00 +0300 Subject: wlcore: set wl->ht_cap per-band Save the ht_cap IE per-band, so we can configure different params to BG and A bands (we currently don't support MIMO on A band) [Small fix for rx_highest - Arik] Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 5 ++++- drivers/net/wireless/ti/wl18xx/main.c | 36 +++++++++++++++++++++++++++------ drivers/net/wireless/ti/wlcore/main.c | 10 +++++---- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- 4 files changed, 41 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index e400d1987e6c..774a1b71e84e 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1423,7 +1423,10 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = 0; wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); - memcpy(&wl->ht_cap, &wl12xx_ht_cap, sizeof(wl12xx_ht_cap)); + memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl12xx_ht_cap, + sizeof(wl12xx_ht_cap)); + memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], &wl12xx_ht_cap, + sizeof(wl12xx_ht_cap)); wl12xx_conf_init(wl); if (!fref_param) { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 5a6c4cc9577e..6cd61186d06c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -596,7 +596,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; /* PG 1.0 has some problems with MCS_13, so disable it */ - wl->ht_cap.mcs.rx_mask[1] &= ~BIT(5); + wl->ht_cap[IEEE80211_BAND_2GHZ].mcs.rx_mask[1] &= ~BIT(5); /* TODO: need to blocksize alignment for RX/TX separately? */ break; @@ -1086,7 +1086,7 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { }; /* HT cap appropriate for MIMO rates in 20mhz channel */ -static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap = { +static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = { .cap = IEEE80211_HT_CAP_SGI_20, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, @@ -1098,6 +1098,18 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap = { }, }; +static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_5ghz = { + .cap = IEEE80211_HT_CAP_SGI_20, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, +}; + static int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -1127,13 +1139,25 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); if (!strcmp(ht_mode_param, "wide")) { - memcpy(&wl->ht_cap, &wl18xx_siso40_ht_cap, + memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], + &wl18xx_siso40_ht_cap, + sizeof(wl18xx_siso40_ht_cap)); + memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], + &wl18xx_siso40_ht_cap, sizeof(wl18xx_siso40_ht_cap)); } else if (!strcmp(ht_mode_param, "mimo")) { - memcpy(&wl->ht_cap, &wl18xx_mimo_ht_cap, - sizeof(wl18xx_mimo_ht_cap)); + memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], + &wl18xx_mimo_ht_cap_2ghz, + sizeof(wl18xx_mimo_ht_cap_2ghz)); + memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], + &wl18xx_mimo_ht_cap_5ghz, + sizeof(wl18xx_mimo_ht_cap_5ghz)); } else if (!strcmp(ht_mode_param, "siso20")) { - memcpy(&wl->ht_cap, &wl18xx_siso20_ht_cap, + memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], + &wl18xx_siso20_ht_cap, + sizeof(wl18xx_siso20_ht_cap)); + memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], + &wl18xx_siso20_ht_cap, sizeof(wl18xx_siso20_ht_cap)); } else { wl1271_error("invalid ht_mode '%s'", ht_mode_param); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1974be0ccd58..c08df334b9d5 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5031,12 +5031,14 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) */ memcpy(&wl->bands[IEEE80211_BAND_2GHZ], &wl1271_band_2ghz, sizeof(wl1271_band_2ghz)); - memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap, &wl->ht_cap, - sizeof(wl->ht_cap)); + memcpy(&wl->bands[IEEE80211_BAND_2GHZ].ht_cap, + &wl->ht_cap[IEEE80211_BAND_2GHZ], + sizeof(*wl->ht_cap)); memcpy(&wl->bands[IEEE80211_BAND_5GHZ], &wl1271_band_5ghz, sizeof(wl1271_band_5ghz)); - memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap, &wl->ht_cap, - sizeof(wl->ht_cap)); + memcpy(&wl->bands[IEEE80211_BAND_5GHZ].ht_cap, + &wl->ht_cap[IEEE80211_BAND_5GHZ], + sizeof(*wl->ht_cap)); wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl->bands[IEEE80211_BAND_2GHZ]; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 19678738a069..37a80f4bf5bb 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -368,7 +368,7 @@ struct wl1271 { u8 hw_min_ht_rate; /* HW HT (11n) capabilities */ - struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_ht_cap ht_cap[IEEE80211_NUM_BANDS]; /* size of the private FW status data */ size_t fw_status_priv_len; -- cgit v1.2.3 From 2e42c203a9a825d04da400bd45b601f94c0cf362 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Tue, 8 May 2012 14:02:11 +0300 Subject: wlcore: use psd_type indexing according to spec In ieee80211.h the uapsd bit mask is defined such that VO=BIT(0), VI=BIT(1), BK=BIT(2), BE=BIT(3). The firmware uses the indexing as defined in the ieee80211 spec, meaning that VO=3, VI=2, BK=1, BE=0. In AP mode when adding peer wlcore needs to convert the indexing accordingly. Signed-off-by: Yoni Divinsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 5b14446ece4a..e8c650dec18c 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1374,9 +1374,12 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) if (sta->wme && (sta->uapsd_queues & BIT(i))) - cmd->psd_type[i] = WL1271_PSD_UPSD_TRIGGER; + cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] = + WL1271_PSD_UPSD_TRIGGER; else - cmd->psd_type[i] = WL1271_PSD_LEGACY; + cmd->psd_type[NUM_ACCESS_CATEGORIES_COPY-1-i] = + WL1271_PSD_LEGACY; + sta_rates = sta->supp_rates[wlvif->band]; if (sta->ht_cap.ht_supported) -- cgit v1.2.3 From 4a6c789b731a095da745d513f9ea0b5531acc9c7 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Tue, 8 May 2012 14:02:12 +0300 Subject: wl12xx: set the irq polarity before loading the fw The polarity should be set before the firmware is loaded since the firmware touches the same register. Access of the firmware and driver to the same register will cause a collision since there is no exclusion scheme. Signed-off-by: Yoni Divinsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 774a1b71e84e..a5af2b1ec863 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -995,7 +995,7 @@ out: static void wl12xx_pre_upload(struct wl1271 *wl) { - u32 tmp; + u32 tmp, polarity; /* write firmware's last address (ie. it's length) to * ACX_EEPROMLESS_IND_REG */ @@ -1015,18 +1015,18 @@ static void wl12xx_pre_upload(struct wl1271 *wl) if (wl->chip.id == CHIP_ID_1283_PG20) wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); -} - -static void wl12xx_enable_interrupts(struct wl1271 *wl) -{ - u32 polarity; + /* polarity must be set before the firmware is loaded */ polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY); /* We use HIGH polarity, so unset the LOW bit */ polarity &= ~POLARITY_LOW; wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); +} + +static void wl12xx_enable_interrupts(struct wl1271 *wl) +{ wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); wlcore_enable_interrupts(wl); -- cgit v1.2.3 From feb47eb86b44fb420c7fe92726e5f359bcea4bdf Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Tue, 8 May 2012 14:02:14 +0300 Subject: wlcore: fix the CONF_TX_AC_ANY_TID to be 0xff In the enum conf_tx_ac CONF_TX_AC_ANY_TID should be 0xff to match the firmware's implementation. Signed-off-by: Yoni Divinsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 14dd361fa828..d2c4885d0d77 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -518,7 +518,7 @@ enum conf_tx_ac { CONF_TX_AC_VI = 2, /* video */ CONF_TX_AC_VO = 3, /* voice */ CONF_TX_AC_CTS2SELF = 4, /* fictitious AC, follows AC_VO */ - CONF_TX_AC_ANY_TID = 0x1f + CONF_TX_AC_ANY_TID = 0xff }; struct conf_tx_ac_category { -- cgit v1.2.3 From d1bcb53f91ed6838dccb04aa5cf71ea8a98770f2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 16:46:56 +0300 Subject: wlcore: flush tx on CHANGE_CHANNEL On CHANGE_CHANNEL indication, we should flush all the queued tx frames, so they will be sent on the correct (current) channel. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index c08df334b9d5..b0607f6e4e4e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2557,8 +2557,9 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) * frames, such as the deauth. To make sure those frames reach the air, * wait here until the TX queue is fully flushed. */ - if ((changed & IEEE80211_CONF_CHANGE_IDLE) && - (conf->flags & IEEE80211_CONF_IDLE)) + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || + ((changed & IEEE80211_CONF_CHANGE_IDLE) && + (conf->flags & IEEE80211_CONF_IDLE))) wl1271_tx_flush(wl); mutex_lock(&wl->mutex); -- cgit v1.2.3 From 0c2129843502833c6e4117db8f312b625a23ef15 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 16:46:57 +0300 Subject: wlcore: increase WL1271_EVENT_TIMEOUT In some cases, the ROC_COMPLETE event might exceed the current timeout (750 msec). Increase it to 1 sec. Signed-off-by: Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 2aafe3df0fb0..85171f2bf68e 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -192,7 +192,7 @@ enum cmd_templ { #define WL1271_COMMAND_TIMEOUT 2000 #define WL1271_CMD_TEMPL_DFLT_SIZE 252 #define WL1271_CMD_TEMPL_MAX_SIZE 512 -#define WL1271_EVENT_TIMEOUT 750 +#define WL1271_EVENT_TIMEOUT 1000 struct wl1271_cmd_header { __le16 id; -- cgit v1.2.3 From 5a344b87ce1f27dade9f320aeb08ba7401d8c984 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 15 May 2012 16:46:58 +0300 Subject: wlcore: remove duplicate BUG_ON during recovery This BUG_ON also ignored the INTENDED_FW_RECOVERY flag. This would result in a BUG() when using the bug_on_recovery module parameter during multi-role. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index b0607f6e4e4e..414fde41072c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -852,8 +852,6 @@ static void wl1271_recovery_work(struct work_struct *work) goto out_unlock; } - BUG_ON(bug_on_recovery); - /* * Advance security sequence number to overcome potential progress * in the firmware during recovery. This doens't hurt if the network is -- cgit v1.2.3 From 836d3f2058ca42eda96dcf187cd72a0ea2a5bf6c Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Thu, 10 May 2012 17:08:33 +0300 Subject: wl12xx: fix fm_coex parameters configuration Wrong fm_coex parameters were set during wl12xx init phase, fix it. Signed-off-by: Ziv Riesel Signed-off-by: Victor Goldenshtein Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index a5af2b1ec863..77e0f318aa67 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -309,8 +309,8 @@ static struct wlcore_conf wl12xx_conf = { .swallow_period = 5, .n_divider_fref_set_1 = 0xff, /* default */ .n_divider_fref_set_2 = 12, - .m_divider_fref_set_1 = 148, - .m_divider_fref_set_2 = 0xffff, /* default */ + .m_divider_fref_set_1 = 0xffff, + .m_divider_fref_set_2 = 148, /* default */ .coex_pll_stabilization_time = 0xffffffff, /* default */ .ldo_stabilization_time = 0xffff, /* default */ .fm_disturbed_band_margin = 0xff, /* default */ -- cgit v1.2.3 From 461b958fd62c62b61d27e0f567128eb2170c0cf2 Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Tue, 15 May 2012 17:15:40 +0300 Subject: wl18xx: fix fm_coex parameters configuration Wrong fm_coex parameters were set during wl18xx init phase, fix it. Signed-off-by: Ziv Riesel Signed-off-by: Victor Goldenshtein Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 6cd61186d06c..d67be3eae3d9 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -427,8 +427,8 @@ static struct wlcore_conf wl18xx_conf = { .swallow_period = 5, .n_divider_fref_set_1 = 0xff, /* default */ .n_divider_fref_set_2 = 12, - .m_divider_fref_set_1 = 148, - .m_divider_fref_set_2 = 0xffff, /* default */ + .m_divider_fref_set_1 = 0xffff, + .m_divider_fref_set_2 = 148, /* default */ .coex_pll_stabilization_time = 0xffffffff, /* default */ .ldo_stabilization_time = 0xffff, /* default */ .fm_disturbed_band_margin = 0xff, /* default */ -- cgit v1.2.3 From e832837bbb91ae8610fa0a0f3d243d42823ec815 Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Mon, 14 May 2012 13:55:47 +0300 Subject: wlcore: don't enable BET for high basic rates The beacon early termination is not relevant for high basic rates, which doesn't contribute anything to the PS and only adds unnecessary FW work. Enable BET only if the basic rate is less than 9 Mbps. Signed-off-by: Ziv Riesel Signed-off-by: Victor Goldenshtein Signed-off-by: Igal Chernobelsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/ps.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 958535dee9f3..47e81b32f7da 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -193,8 +193,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); - /* enable beacon early termination. Not relevant for 5GHz */ - if (wlvif->band == IEEE80211_BAND_2GHZ) { + /* + * enable beacon early termination. + * Not relevant for 5GHz and for high rates. + */ + if ((wlvif->band == IEEE80211_BAND_2GHZ) && + (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) { ret = wl1271_acx_bet_enable(wl, wlvif, true); if (ret < 0) return ret; @@ -204,7 +208,8 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_debug(DEBUG_PSM, "leaving psm"); /* disable beacon early termination */ - if (wlvif->band == IEEE80211_BAND_2GHZ) { + if ((wlvif->band == IEEE80211_BAND_2GHZ) && + (wlvif->basic_rate < CONF_HW_BIT_RATE_9MBPS)) { ret = wl1271_acx_bet_enable(wl, wlvif, false); if (ret < 0) return ret; -- cgit v1.2.3 From a4203c6453afcd21b3a6f0be592bf74951e80c72 Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Mon, 14 May 2012 16:07:38 +0300 Subject: wlcore: wait for roc complete only for the first roc command In some multi role scenarios the driver might send multi roc requests without sending a croc first, the fw queues those requests and starts service the next roc request as soon as the driver sends a croc for the previous one. So, if the fw rocs on channel X and driver asks to roc also on channel Y, the fw will not start service Y (and will not send roc complete event for this request) until the driver releases the fw with croc X. Signed-off-by: Victor Goldenshtein Signed-off-by: Igal Chernobelsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index e8c650dec18c..df8d672b1f06 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1606,19 +1606,25 @@ out: int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id) { int ret = 0; + bool is_first_roc; if (WARN_ON(test_bit(role_id, wl->roc_map))) return 0; + is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= + WL12XX_MAX_ROLES); + ret = wl12xx_cmd_roc(wl, wlvif, role_id); if (ret < 0) goto out; - ret = wl1271_cmd_wait_for_event(wl, - REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID); - if (ret < 0) { - wl1271_error("cmd roc event completion error"); - goto out; + if (is_first_roc) { + ret = wl1271_cmd_wait_for_event(wl, + REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID); + if (ret < 0) { + wl1271_error("cmd roc event completion error"); + goto out; + } } __set_bit(role_id, wl->roc_map); -- cgit v1.2.3 From 8fd9d059af12786341dec5a688e607bcdb372238 Mon Sep 17 00:00:00 2001 From: Albert Pool Date: Mon, 14 May 2012 18:08:32 +0200 Subject: rt2800usb: 2001:3c17 is an RT3370 device D-Link DWA-123 rev A1 Signed-off-by: Albert Pool Cc: Acked-by: Gertjan van Wingerde Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index bf78317a6adb..20a504072895 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1137,6 +1137,8 @@ static struct usb_device_id rt2800usb_device_table[] = { #ifdef CONFIG_RT2800USB_RT33XX /* Belkin */ { USB_DEVICE(0x050d, 0x945b) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c17) }, /* Panasonic */ { USB_DEVICE(0x083a, 0xb511) }, /* Philips */ @@ -1237,7 +1239,6 @@ static struct usb_device_id rt2800usb_device_table[] = { /* D-Link */ { USB_DEVICE(0x07d1, 0x3c0b) }, { USB_DEVICE(0x07d1, 0x3c17) }, - { USB_DEVICE(0x2001, 0x3c17) }, /* Encore */ { USB_DEVICE(0x203d, 0x14a1) }, /* Gemtek */ -- cgit v1.2.3 From 9be02923371718265ea050a5415dc4fefc4e6a64 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 14 May 2012 23:57:29 +0300 Subject: brcm80211: remove an unneeded NULL check This code causes a static checker warning because "pi" gets dereferenced before it is checked. The dereference is inside the write_phy_reg() function which is called from wlc_phy_write_txmacreg_nphy(). This code is only called from wlc_phy_init_nphy() and "pi" is a valid pointer so we can remove the check for NULL. Signed-off-by: Dan Carpenter Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c index 13b261517cce..366718146418 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c @@ -14358,7 +14358,7 @@ void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs) wlc_phy_write_txmacreg_nphy(pi, holdoff, delay); - if (pi && pi->sh && (pi->sh->_rifs_phy != rifs)) + if (pi->sh && (pi->sh->_rifs_phy != rifs)) pi->sh->_rifs_phy = rifs; } -- cgit v1.2.3 From 1dacd1980d5ba732de1b69999fd8995b3b2b724b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 15 May 2012 12:00:27 +0300 Subject: brcm80211: brcmu_pkt_buf_free_skb() should handle NULL This is potentially called with NULL pointers, for example, look at brcmf_c_prec_enq(). Since it's a free() function, probably people expect it to handle NULL pointers. Signed-off-by: Dan Carpenter Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmutil/utils.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c index b45ab34cdfdc..3e6405e06ac0 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c @@ -43,6 +43,8 @@ EXPORT_SYMBOL(brcmu_pkt_buf_get_skb); /* Free the driver packet. Free the tag if present */ void brcmu_pkt_buf_free_skb(struct sk_buff *skb) { + if (!skb) + return; WARN_ON(skb->next); if (skb->destructor) /* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if -- cgit v1.2.3 From d25360b19008e59585defb16823804c40d4723ab Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 15 May 2012 15:24:47 +0530 Subject: ath9k: Fix modal EEPROM dump we provide excess buffer size for 'simple_read_from_buffer' for modal EEPROM dump. This results in trailing NULL bytes at the end of EEPROM dump, fix this. Cc: Rajkumar Manoharan Reported-by: Holger Schurig Signed-off-by: Mohammed Shafi Shajakhan Tested-by: Holger Schurig Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 4 ++-- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom_9287.c | 2 +- drivers/net/wireless/ath/ath9k/eeprom_def.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index dfb0441f406c..f794828c08e2 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3412,11 +3412,11 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); - len += ar9003_dump_modal_eeprom(buf, len, size, + len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader2G); len += snprintf(buf + len, size - len, "%20s :\n", "5GHz modal Header"); - len += ar9003_dump_modal_eeprom(buf, len, size, + len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader5G); goto out; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 4322ac80c203..a850f44fa767 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -135,7 +135,7 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); - len += ath9k_dump_4k_modal_eeprom(buf, len, size, + len = ath9k_dump_4k_modal_eeprom(buf, len, size, &eep->modalHeader); goto out; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index aa614767adff..cd742fb944c2 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -132,7 +132,7 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); - len += ar9287_dump_modal_eeprom(buf, len, size, + len = ar9287_dump_modal_eeprom(buf, len, size, &eep->modalHeader); goto out; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index b5fba8b18b8b..56290f318520 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -211,11 +211,11 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, if (!dump_base_hdr) { len += snprintf(buf + len, size - len, "%20s :\n", "2GHz modal Header"); - len += ath9k_def_dump_modal_eeprom(buf, len, size, + len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[0]); len += snprintf(buf + len, size - len, "%20s :\n", "5GHz modal Header"); - len += ath9k_def_dump_modal_eeprom(buf, len, size, + len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[1]); goto out; } -- cgit v1.2.3 From b90bd9d14f866318497acee571f79e621a735d69 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 15 May 2012 15:33:25 -0700 Subject: ath9k: Support ethtool getstats api. This returns many of the values that formerly could only be obtained from debugfs. This should be an improvement when trying to access these counters programatically. Currently this support is only enabled when DEBUGFS is enabled because otherwise these stats are not accumulated. Signed-off-by: Ben Greear Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 134 ++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 4de4473776ac..cfbcc047c61e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2389,6 +2389,134 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) return 0; } +#ifdef CONFIG_ATH9K_DEBUGFS + +/* Ethtool support for get-stats */ + +#define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" +static const char ath9k_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_pkts_nic", + "tx_bytes_nic", + "rx_pkts_nic", + "rx_bytes_nic", + AMKSTR(d_tx_pkts), + AMKSTR(d_tx_bytes), + AMKSTR(d_tx_mpdus_queued), + AMKSTR(d_tx_mpdus_completed), + AMKSTR(d_tx_mpdu_xretries), + AMKSTR(d_tx_aggregates), + AMKSTR(d_tx_ampdus_queued_hw), + AMKSTR(d_tx_ampdus_queued_sw), + AMKSTR(d_tx_ampdus_completed), + AMKSTR(d_tx_ampdu_retries), + AMKSTR(d_tx_ampdu_xretries), + AMKSTR(d_tx_fifo_underrun), + AMKSTR(d_tx_op_exceeded), + AMKSTR(d_tx_timer_expiry), + AMKSTR(d_tx_desc_cfg_err), + AMKSTR(d_tx_data_underrun), + AMKSTR(d_tx_delim_underrun), + + "d_rx_decrypt_crc_err", + "d_rx_phy_err", + "d_rx_mic_err", + "d_rx_pre_delim_crc_err", + "d_rx_post_delim_crc_err", + "d_rx_decrypt_busy_err", + + "d_rx_phyerr_radar", + "d_rx_phyerr_ofdm_timing", + "d_rx_phyerr_cck_timing", + +}; +#define ATH9K_SSTATS_LEN ARRAY_SIZE(ath9k_gstrings_stats) + +static void ath9k_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, *ath9k_gstrings_stats, + sizeof(ath9k_gstrings_stats)); +} + +static int ath9k_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return ATH9K_SSTATS_LEN; + return 0; +} + +#define PR_QNUM(_n) (sc->tx.txq_map[_n]->axq_qnum) +#define AWDATA(elem) \ + do { \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem; \ + } while (0) + +#define AWDATA_RX(elem) \ + do { \ + data[i++] = sc->debug.stats.rxstats.elem; \ + } while (0) + +static void ath9k_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct ath_softc *sc = hw->priv; + int i = 0; + + data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_pkts_all); + data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_bytes_all); + AWDATA_RX(rx_pkts_all); + AWDATA_RX(rx_bytes_all); + + AWDATA(tx_pkts_all); + AWDATA(tx_bytes_all); + AWDATA(queued); + AWDATA(completed); + AWDATA(xretries); + AWDATA(a_aggr); + AWDATA(a_queued_hw); + AWDATA(a_queued_sw); + AWDATA(a_completed); + AWDATA(a_retries); + AWDATA(a_xretries); + AWDATA(fifo_underrun); + AWDATA(xtxop); + AWDATA(timer_exp); + AWDATA(desc_cfg_err); + AWDATA(data_underrun); + AWDATA(delim_underrun); + + AWDATA_RX(decrypt_crc_err); + AWDATA_RX(phy_err); + AWDATA_RX(mic_err); + AWDATA_RX(pre_delim_crc_err); + AWDATA_RX(post_delim_crc_err); + AWDATA_RX(decrypt_busy_err); + + AWDATA_RX(phy_err_stats[ATH9K_PHYERR_RADAR]); + AWDATA_RX(phy_err_stats[ATH9K_PHYERR_OFDM_TIMING]); + AWDATA_RX(phy_err_stats[ATH9K_PHYERR_CCK_TIMING]); + + WARN_ON(i != ATH9K_SSTATS_LEN); +} + +/* End of ethtool get-stats functions */ + +#endif + + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, @@ -2417,4 +2545,10 @@ struct ieee80211_ops ath9k_ops = { .get_stats = ath9k_get_stats, .set_antenna = ath9k_set_antenna, .get_antenna = ath9k_get_antenna, + +#ifdef CONFIG_ATH9K_DEBUGFS + .get_et_sset_count = ath9k_get_et_sset_count, + .get_et_stats = ath9k_get_et_stats, + .get_et_strings = ath9k_get_et_strings, +#endif }; -- cgit v1.2.3 From ccf91bd678d74e1e98a153539cd07a62991d6610 Mon Sep 17 00:00:00 2001 From: "villacis@palosanto.com" Date: Wed, 16 May 2012 21:07:12 +0200 Subject: rt2x00: Add support for RT5360 based PCI devices. This chip is used at least by the D-Link DWA-525 adapter. Signed-off-by: Alex Villacs Lasso Signed-off-by: Gertjan van Wingerde Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800.h | 2 ++ drivers/net/wireless/rt2x00/rt2800lib.c | 5 +++++ drivers/net/wireless/rt2x00/rt2800pci.c | 1 + 3 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index 9348521e0832..6403f49da419 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -51,6 +51,7 @@ * RF3320 2.4G 1T1R(RT3350/RT3370/RT3390) * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) + * RF5360 2.4G 1T1R * RF5370 2.4G 1T1R * RF5390 2.4G 1T1R */ @@ -67,6 +68,7 @@ #define RF3320 0x000b #define RF3322 0x000c #define RF3053 0x000d +#define RF5360 0x5360 #define RF5370 0x5370 #define RF5372 0x5372 #define RF5390 0x5390 diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index dfc90d34be6d..11488e743f08 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -2060,6 +2060,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF3052: rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); break; + case RF5360: case RF5370: case RF5372: case RF5390: @@ -2549,6 +2550,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); break; + case RF5360: case RF5370: case RF5372: case RF5390: @@ -4263,6 +4265,7 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF3022: case RF3052: case RF3320: + case RF5360: case RF5370: case RF5372: case RF5390: @@ -4577,6 +4580,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_rf(rt2x00dev, RF3021) || rt2x00_rf(rt2x00dev, RF3022) || rt2x00_rf(rt2x00dev, RF3320) || + rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || rt2x00_rf(rt2x00dev, RF5372) || rt2x00_rf(rt2x00dev, RF5390)) { @@ -4662,6 +4666,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3022: case RF3320: case RF3052: + case RF5360: case RF5370: case RF5372: case RF5390: diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index cad25bfebd7a..206158b67426 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -1188,6 +1188,7 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { { PCI_DEVICE(0x1814, 0x3593) }, #endif #ifdef CONFIG_RT2800PCI_RT53XX + { PCI_DEVICE(0x1814, 0x5360) }, { PCI_DEVICE(0x1814, 0x5362) }, { PCI_DEVICE(0x1814, 0x5390) }, { PCI_DEVICE(0x1814, 0x5392) }, -- cgit v1.2.3 From 726f23fdeb003eee1d2b0eb14503591175746c70 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 16 May 2012 22:40:49 +0200 Subject: iwlwifi: fix prints in iwl_rx_handle The debug prints were wrong and buggy. The HW pointer wasn't printed correctly, it was mixed up with the pointer to the rxbuf. Signed-off-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 08517d3c80bb..1a7eb2a5dec3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -497,7 +497,7 @@ static void iwl_rx_handle(struct iwl_trans *trans) /* Rx interrupt, but nothing sent from uCode */ if (i == r) - IWL_DEBUG_RX(trans, "r = %d, i = %d\n", r, i); + IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); /* calculate total frames need to be restock after handling RX */ total_empty = r - rxq->write_actual; @@ -513,8 +513,8 @@ static void iwl_rx_handle(struct iwl_trans *trans) rxb = rxq->queue[i]; rxq->queue[i] = NULL; - IWL_DEBUG_RX(trans, "rxbuf: r = %d, i = %d (%p)\n", rxb); - + IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", + r, i, rxb); iwl_rx_handle_rxbuf(trans, rxb); i = (i + 1) & RX_QUEUE_MASK; -- cgit v1.2.3 From a35e270881a5db1ec9ac8bc6d61ebc3e85c14f33 Mon Sep 17 00:00:00 2001 From: Meenakshi Venkataraman Date: Wed, 16 May 2012 22:40:50 +0200 Subject: iwlwifi: fix debug print in iwl_sta_calc_ht_flags We missed passing an argument to the debug print. Fix it. Cc: stable@kernel.org Signed-off-by: Meenakshi Venkataraman Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-sta.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c index aea07aab3c9e..36055ed1c069 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c @@ -236,6 +236,7 @@ static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", + sta->addr, (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? "static" : (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? -- cgit v1.2.3 From 142fb5b733d377dedd930ae53332b6a6ccb622f5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 23:05:09 +0200 Subject: iwlwifi: add __printf argument checking Joe Perches suggested adding the __printf attribute to the __iwl_dbg function to check arguments; add it to all of the logging functions (err, warn, info, dbg and crit.) Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-debug.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 8376b842bdba..f6bf91c8f773 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -38,10 +38,10 @@ static inline bool iwl_have_debug_level(u32 level) } void __iwl_err(struct device *dev, bool rfkill_prefix, bool only_trace, - const char *fmt, ...); -void __iwl_warn(struct device *dev, const char *fmt, ...); -void __iwl_info(struct device *dev, const char *fmt, ...); -void __iwl_crit(struct device *dev, const char *fmt, ...); + const char *fmt, ...) __printf(4, 5); +void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3); +void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3); +void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); /* No matter what is m (priv, bus, trans), this will work */ #define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a) @@ -52,9 +52,9 @@ void __iwl_crit(struct device *dev, const char *fmt, ...); #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, u32 level, bool limit, const char *function, - const char *fmt, ...); + const char *fmt, ...) __printf(5, 6); #else -static inline void +__printf(5, 6) static inline void __iwl_dbg(struct device *dev, u32 level, bool limit, const char *function, const char *fmt, ...) -- cgit v1.2.3 From bc2529c337fe7ea1a37cd5cdc17586f7ef73c4f1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 16 May 2012 22:54:22 +0200 Subject: iwlwifi: s/iwlagn_txq_free_tfd/iwl_txq_free_tfd Signed-off-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | 4 ++-- drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 8 ++++---- drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h index e959207c630a..ccb97b9da4c3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h @@ -346,8 +346,8 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, int tx_fifo_id, bool active); void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); -void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - enum dma_data_direction dma_dir); +void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, + enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, struct sk_buff_head *skbs); int iwl_queue_space(const struct iwl_queue *q); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index a8750238ee09..cf8f3cfff1e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c @@ -209,7 +209,7 @@ static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, } /** - * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] + * iwl_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] * @trans - transport private data * @txq - tx queue * @dma_dir - the direction of the DMA mapping @@ -217,8 +217,8 @@ static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ -void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - enum dma_data_direction dma_dir) +void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, + enum dma_data_direction dma_dir) { struct iwl_tfd *tfd_tmp = txq->tfds; @@ -979,7 +979,7 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, iwlagn_txq_inval_byte_cnt_tbl(trans, txq); - iwlagn_txq_free_tfd(trans, txq, DMA_TO_DEVICE); + iwl_txq_free_tfd(trans, txq, DMA_TO_DEVICE); freed++; } diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index ec6fb395b84d..c11911864e62 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -435,7 +435,7 @@ static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) spin_lock_bh(&txq->lock); while (q->write_ptr != q->read_ptr) { - iwlagn_txq_free_tfd(trans, txq, dma_dir); + iwl_txq_free_tfd(trans, txq, dma_dir); q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); } spin_unlock_bh(&txq->lock); -- cgit v1.2.3 From eec373f026f445191e6cc4a3fda7eb6b291144b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 16 May 2012 22:54:23 +0200 Subject: iwlwifi: s/iwlagn_unmap_tfd/iwl_unmap_tfd Signed-off-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index cf8f3cfff1e1..e727c4f506c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c @@ -178,8 +178,8 @@ static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) return tfd->num_tbs & 0x1f; } -static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, - struct iwl_tfd *tfd, enum dma_data_direction dma_dir) +static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, + struct iwl_tfd *tfd, enum dma_data_direction dma_dir) { int i; int num_tbs; @@ -229,8 +229,8 @@ void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, lockdep_assert_held(&txq->lock); /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ - iwlagn_unmap_tfd(trans, &txq->entries[idx].meta, - &tfd_tmp[rd_ptr], dma_dir); + iwl_unmap_tfd(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], + dma_dir); /* free SKB */ if (txq->entries) { @@ -647,9 +647,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) (void *)cmd->data[i], cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { - iwlagn_unmap_tfd(trans, out_meta, - &txq->tfds[q->write_ptr], - DMA_BIDIRECTIONAL); + iwl_unmap_tfd(trans, out_meta, + &txq->tfds[q->write_ptr], + DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } @@ -784,8 +784,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; - iwlagn_unmap_tfd(trans, meta, &txq->tfds[index], - DMA_BIDIRECTIONAL); + iwl_unmap_tfd(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { -- cgit v1.2.3 From ff110c8f93cb7e2bd83f52a176e33c3573163f0a Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 16 May 2012 22:54:24 +0200 Subject: iwlwifi: Move IWL_MASK macro to transport. This macro is needed by other transports besides PCIe, thus moving to a common location. Signed-off-by: Gregory Greenman Reviewed-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 2 -- drivers/net/wireless/iwlwifi/iwl-trans.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index c11911864e62..12d84e9a2d64 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -78,8 +78,6 @@ /* FIXME: need to abstract out TX command (once we know what it looks like) */ #include "iwl-commands.h" -#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) - #define SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie) \ (((1<cfg->base_params->num_of_queues) - 1) &\ (~(1<<(trans_pcie)->cmd_queue))) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 79a1e7ae4995..78b89f1f4b08 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -280,6 +280,8 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) #define MAX_NO_RECLAIM_CMDS 6 +#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) + /* * Maximum number of HW queues the transport layer * currently supports -- cgit v1.2.3 From 76ed2edd5b2143856915560accf54ca9c1a4c792 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 22:54:25 +0200 Subject: iwlwifi: properly handle device_set_wakeup_enable This is intended to be set whenever wakeup will be needed upon suspend, not only when suspending, so use the new callbacks to set it then. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-mac80211.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index ab2f4d7500a4..ff66200b8eaa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -417,8 +417,6 @@ int iwlagn_mac_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) if (ret) goto error; - device_set_wakeup_enable(priv->trans->dev, true); - iwl_trans_wowlan_suspend(priv->trans); goto out; @@ -485,8 +483,6 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) priv->wowlan = false; - device_set_wakeup_enable(priv->trans->dev, false); - iwlagn_prepare_restart(priv); memset((void *)&ctx->active, 0, sizeof(ctx->active)); @@ -501,6 +497,12 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) return 1; } +static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + device_set_wakeup_enable(priv->trans->dev, enabled); +} #endif void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) @@ -1581,6 +1583,7 @@ struct ieee80211_ops iwlagn_hw_ops = { #ifdef CONFIG_PM_SLEEP .suspend = iwlagn_mac_suspend, .resume = iwlagn_mac_resume, + .set_wakeup = iwlagn_mac_set_wakeup, #endif .add_interface = iwlagn_mac_add_interface, .remove_interface = iwlagn_mac_remove_interface, -- cgit v1.2.3 From c08ce20c7eedb9406c55a1c59acc2c84812c8e82 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 22:54:26 +0200 Subject: iwlwifi: move IWL_CMD_FAILED_MSK This is common, not uCode API specific, so move it to the transport together with the command header struct definition. Reviewed-by: Meenakshi Venkataraman Reviewed-by: Guy Cohen Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-commands.h | 3 --- drivers/net/wireless/iwlwifi/iwl-trans.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 9af6a239b384..b9f7361d2426 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -197,9 +197,6 @@ enum { * *****************************************************************************/ -/* iwl_cmd_header flags value */ -#define IWL_CMD_FAILED_MSK 0x40 - /** * iwlagn rate_n_flags bit fields * diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 78b89f1f4b08..0aeeb7ce91c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -154,6 +154,9 @@ struct iwl_cmd_header { __le16 sequence; } __packed; +/* iwl_cmd_header flags value */ +#define IWL_CMD_FAILED_MSK 0x40 + #define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ #define FH_RSCSR_FRAME_INVALID 0x55550000 -- cgit v1.2.3 From cc5f7e39761382d3a44be70bb665c2c78ae15dac Mon Sep 17 00:00:00 2001 From: Don Fry Date: Wed, 16 May 2012 22:54:27 +0200 Subject: iwlwifi: implement dynamic opmode loading This is the next step in splitting up the driver, making the uCode API dependent pieces of it live in separate modules. Right now there's only one so it's not user-selectable, but we're actively working on more. Reviewed-by: Emmanuel Grumbach Signed-off-by: Don Fry Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/Kconfig | 5 ++ drivers/net/wireless/iwlwifi/Makefile | 32 ++++---- drivers/net/wireless/iwlwifi/iwl-agn.c | 16 ++-- drivers/net/wireless/iwlwifi/iwl-debug.c | 6 ++ drivers/net/wireless/iwlwifi/iwl-devtrace.c | 5 ++ drivers/net/wireless/iwlwifi/iwl-drv.c | 112 +++++++++++++++++++++++++++- drivers/net/wireless/iwlwifi/iwl-io.c | 19 +++++ drivers/net/wireless/iwlwifi/iwl-op-mode.h | 3 + 8 files changed, 172 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 2463c0626438..727fbb5db9da 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -6,6 +6,7 @@ config IWLWIFI select LEDS_CLASS select LEDS_TRIGGERS select MAC80211_LEDS + select IWLDVM ---help--- Select to build the driver supporting the: @@ -41,6 +42,10 @@ config IWLWIFI say M here and read . The module will be called iwlwifi. +config IWLDVM + tristate "Intel Wireless WiFi" + depends on IWLWIFI + menu "Debugging Options" depends on IWLWIFI diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index d615eacbf050..f284ea850bb2 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,27 +1,31 @@ +# DVM +obj-$(CONFIG_IWLDVM) += iwldvm.o +iwldvm-objs := iwl-agn.o iwl-agn-rs.o iwl-mac80211.o +iwldvm-objs += iwl-ucode.o iwl-agn-tx.o +iwldvm-objs += iwl-agn-lib.o iwl-agn-calib.o +iwldvm-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o +iwldvm-objs += iwl-eeprom.o iwl-power.o +iwldvm-objs += iwl-scan.o iwl-led.o +iwldvm-objs += iwl-agn-rxon.o iwl-agn-devices.o +iwldvm-objs += iwl-notif-wait.o + +iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o +iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o + +CFLAGS_iwl-devtrace.o := -I$(src) + # WIFI obj-$(CONFIG_IWLWIFI) += iwlwifi.o -iwlwifi-objs := iwl-agn.o iwl-agn-rs.o iwl-mac80211.o -iwlwifi-objs += iwl-ucode.o iwl-agn-tx.o iwl-debug.o -iwlwifi-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o -iwlwifi-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o - -iwlwifi-objs += iwl-eeprom.o iwl-power.o -iwlwifi-objs += iwl-scan.o iwl-led.o -iwlwifi-objs += iwl-agn-rxon.o iwl-agn-devices.o iwlwifi-objs += iwl-5000.o iwlwifi-objs += iwl-6000.o iwlwifi-objs += iwl-1000.o iwlwifi-objs += iwl-2000.o +iwlwifi-objs += iwl-io.o iwlwifi-objs += iwl-pci.o iwlwifi-objs += iwl-drv.o -iwlwifi-objs += iwl-notif-wait.o +iwlwifi-objs += iwl-debug.o iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o - -iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o -iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o - -CFLAGS_iwl-devtrace.o := -I$(src) ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index ec36e2b020b6..5149e6f72945 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -78,7 +78,6 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); -MODULE_ALIAS("iwlagn"); void iwl_update_chain_flags(struct iwl_priv *priv) { @@ -2344,24 +2343,25 @@ static int __init iwl_init(void) goto error_rc_register; } - ret = iwl_pci_register_driver(); - if (ret) - goto error_pci_register; + ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); + if (ret) { + pr_err("Unable to register op_mode: %d\n", ret); + goto error_opmode_register; + } return ret; -error_pci_register: +error_opmode_register: iwlagn_rate_control_unregister(); error_rc_register: kmem_cache_destroy(iwl_tx_cmd_pool); return ret; } +module_init(iwl_init); static void __exit iwl_exit(void) { - iwl_pci_unregister_driver(); + iwl_opmode_deregister("iwldvm"); iwlagn_rate_control_unregister(); kmem_cache_destroy(iwl_tx_cmd_pool); } - module_exit(iwl_exit); -module_init(iwl_init); diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c index 2d1b42847b9b..0f8fcd1d4fe2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -62,6 +62,7 @@ *****************************************************************************/ #include +#include #include "iwl-debug.h" #include "iwl-devtrace.h" @@ -81,8 +82,11 @@ void __iwl_ ##fn(struct device *dev, const char *fmt, ...) \ } __iwl_fn(warn) +EXPORT_SYMBOL_GPL(__iwl_warn); __iwl_fn(info) +EXPORT_SYMBOL_GPL(__iwl_info); __iwl_fn(crit) +EXPORT_SYMBOL_GPL(__iwl_crit); void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, const char *fmt, ...) @@ -103,6 +107,7 @@ void __iwl_err(struct device *dev, bool rfkill_prefix, bool trace_only, trace_iwlwifi_err(&vaf); va_end(args); } +EXPORT_SYMBOL_GPL(__iwl_err); #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, @@ -125,4 +130,5 @@ void __iwl_dbg(struct device *dev, trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); va_end(args); } +EXPORT_SYMBOL_GPL(__iwl_dbg); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c index 91f45e71e0a2..70191ddbd8f6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -42,4 +42,9 @@ EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_error); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_cont_event); EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dev_ucode_wrap_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_info); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_warn); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_crit); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_err); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_dbg); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index d742900969ea..cdfdfaec395e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -77,8 +77,33 @@ /* private includes */ #include "iwl-fw-file.h" +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* + * module name, copyright, version, etc. + */ +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" + +#ifdef CONFIG_IWLWIFI_DEBUG +#define VD "d" +#else +#define VD +#endif + +#define DRV_VERSION IWLWIFI_VERSION VD + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + /** * struct iwl_drv - drv common data + * @list: list of drv structures using this opmode * @fw: the iwl_fw structure * @op_mode: the running op_mode * @trans: transport layer @@ -89,6 +114,7 @@ * @request_firmware_complete: the firmware has been obtained from user space */ struct iwl_drv { + struct list_head list; struct iwl_fw fw; struct iwl_op_mode *op_mode; @@ -102,7 +128,17 @@ struct iwl_drv { struct completion request_firmware_complete; }; +#define DVM_OP_MODE 0 +#define MVM_OP_MODE 1 +static struct iwlwifi_opmode_table { + const char *name; /* name: iwldvm, iwlmvm, etc */ + const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ + struct list_head drv; /* list of devices using this op_mode */ +} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ + { .name = "iwldvm", .ops = NULL }, + { .name = "iwlmvm", .ops = NULL }, +}; /* * struct fw_sec: Just for the image parsing proccess. @@ -721,7 +757,6 @@ static int validate_sec_sizes(struct iwl_drv *drv, return 0; } - /** * iwl_ucode_callback - callback when firmware was loaded * @@ -733,6 +768,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) struct iwl_drv *drv = context; struct iwl_fw *fw = &drv->fw; struct iwl_ucode_header *ucode; + struct iwlwifi_opmode_table *op; int err; struct iwl_firmware_pieces pieces; const unsigned int api_max = drv->cfg->ucode_api_max; @@ -863,10 +899,17 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) release_firmware(ucode_raw); complete(&drv->request_firmware_complete); - drv->op_mode = iwl_dvm_ops.start(drv->trans, drv->cfg, &drv->fw); + op = &iwlwifi_opmode_table[DVM_OP_MODE]; - if (!drv->op_mode) - goto out_free_fw; + /* add this device to the list of devices using this op_mode */ + list_add_tail(&drv->list, &op->drv); + + if (op->ops) { + const struct iwl_op_mode_ops *ops = op->ops; + drv->op_mode = ops->start(drv->trans, drv->cfg, &drv->fw); + } else { + request_module_nowait("%s", op->name); + } return; @@ -938,6 +981,67 @@ struct iwl_mod_params iwlwifi_mod_params = { .auto_agg = true, /* the rest are 0 by default */ }; +EXPORT_SYMBOL_GPL(iwlwifi_mod_params); + +int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) +{ + int i; + struct iwl_drv *drv; + + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { + if (strcmp(iwlwifi_opmode_table[i].name, name)) + continue; + iwlwifi_opmode_table[i].ops = ops; + list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) + drv->op_mode = ops->start(drv->trans, drv->cfg, + &drv->fw); + return 0; + } + return -EIO; +} +EXPORT_SYMBOL_GPL(iwl_opmode_register); + +void iwl_opmode_deregister(const char *name) +{ + int i; + struct iwl_drv *drv; + + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { + if (strcmp(iwlwifi_opmode_table[i].name, name)) + continue; + iwlwifi_opmode_table[i].ops = NULL; + + /* call the stop routine for all devices */ + list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) { + if (drv->op_mode) { + iwl_op_mode_stop(drv->op_mode); + drv->op_mode = NULL; + } + } + return; + } +} +EXPORT_SYMBOL_GPL(iwl_opmode_deregister); + +static int __init iwl_drv_init(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) + INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); + + pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_COPYRIGHT "\n"); + + return iwl_pci_register_driver(); +} +module_init(iwl_drv_init); + +static void __exit iwl_drv_exit(void) +{ + iwl_pci_unregister_driver(); +} +module_exit(iwl_drv_exit); #ifdef CONFIG_IWLWIFI_DEBUG module_param_named(debug, iwlwifi_mod_params.debug_level, uint, diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 081dd34d2387..ee93274214d6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -27,6 +27,7 @@ *****************************************************************************/ #include #include +#include #include "iwl-io.h" #include"iwl-csr.h" @@ -52,6 +53,7 @@ void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask) __iwl_set_bit(trans, reg, mask); spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_set_bit); void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) { @@ -61,6 +63,7 @@ void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) __iwl_clear_bit(trans, reg, mask); spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_clear_bit); int iwl_poll_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout) @@ -76,6 +79,7 @@ int iwl_poll_bit(struct iwl_trans *trans, u32 addr, return -ETIMEDOUT; } +EXPORT_SYMBOL_GPL(iwl_poll_bit); int iwl_grab_nic_access_silent(struct iwl_trans *trans) { @@ -117,6 +121,7 @@ int iwl_grab_nic_access_silent(struct iwl_trans *trans) return 0; } +EXPORT_SYMBOL_GPL(iwl_grab_nic_access_silent); bool iwl_grab_nic_access(struct iwl_trans *trans) { @@ -130,6 +135,7 @@ bool iwl_grab_nic_access(struct iwl_trans *trans) return true; } +EXPORT_SYMBOL_GPL(iwl_grab_nic_access); void iwl_release_nic_access(struct iwl_trans *trans) { @@ -144,6 +150,7 @@ void iwl_release_nic_access(struct iwl_trans *trans) */ mmiowb(); } +EXPORT_SYMBOL_GPL(iwl_release_nic_access); u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) { @@ -158,6 +165,7 @@ u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) return value; } +EXPORT_SYMBOL_GPL(iwl_read_direct32); void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) { @@ -170,6 +178,7 @@ void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) } spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_write_direct32); int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, int timeout) @@ -185,6 +194,7 @@ int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, return -ETIMEDOUT; } +EXPORT_SYMBOL_GPL(iwl_poll_direct_bit); static inline u32 __iwl_read_prph(struct iwl_trans *trans, u32 reg) { @@ -211,6 +221,7 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 reg) spin_unlock_irqrestore(&trans->reg_lock, flags); return val; } +EXPORT_SYMBOL_GPL(iwl_read_prph); void iwl_write_prph(struct iwl_trans *trans, u32 addr, u32 val) { @@ -223,6 +234,7 @@ void iwl_write_prph(struct iwl_trans *trans, u32 addr, u32 val) } spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_write_prph); void iwl_set_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) { @@ -236,6 +248,7 @@ void iwl_set_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) } spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_set_bits_prph); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 reg, u32 bits, u32 mask) @@ -250,6 +263,7 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 reg, } spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_set_bits_mask_prph); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) { @@ -264,6 +278,7 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) } spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(iwl_clear_bits_prph); void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr, void *buf, int words) @@ -281,6 +296,7 @@ void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr, } spin_unlock_irqrestore(&trans->reg_lock, flags); } +EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_words); u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr) { @@ -290,6 +306,7 @@ u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr) return value; } +EXPORT_SYMBOL_GPL(iwl_read_targ_mem); int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr, void *buf, int words) @@ -310,8 +327,10 @@ int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr, return result; } +EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_words); int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val) { return _iwl_write_targ_mem_words(trans, addr, &val, 1); } +EXPORT_SYMBOL_GPL(iwl_write_targ_mem); diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 4ef742b28e08..cec133c87ad8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -145,6 +145,9 @@ struct iwl_op_mode_ops { void (*wimax_active)(struct iwl_op_mode *op_mode); }; +int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops); +void iwl_opmode_deregister(const char *name); + /** * struct iwl_op_mode - operational mode * -- cgit v1.2.3 From ac91f910455f0ff323d965123b76d112afb49dd6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 22:54:28 +0200 Subject: iwlwifi: move notification wait into core This code is a library to be used by multiple opmodes, so move it into the iwlwifi module. Reviewed-by: Donald H Fry Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/Makefile | 2 +- drivers/net/wireless/iwlwifi/iwl-notif-wait.c | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index f284ea850bb2..931002738c9f 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -7,7 +7,6 @@ iwldvm-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o iwldvm-objs += iwl-eeprom.o iwl-power.o iwldvm-objs += iwl-scan.o iwl-led.o iwldvm-objs += iwl-agn-rxon.o iwl-agn-devices.o -iwldvm-objs += iwl-notif-wait.o iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o @@ -24,6 +23,7 @@ iwlwifi-objs += iwl-io.o iwlwifi-objs += iwl-pci.o iwlwifi-objs += iwl-drv.o iwlwifi-objs += iwl-debug.o +iwlwifi-objs += iwl-notif-wait.o iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c index 0066b899fe5c..5cfed29b1b18 100644 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c +++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c @@ -61,6 +61,7 @@ * *****************************************************************************/ #include +#include #include "iwl-notif-wait.h" @@ -71,6 +72,7 @@ void iwl_notification_wait_init(struct iwl_notif_wait_data *notif_wait) INIT_LIST_HEAD(¬if_wait->notif_waits); init_waitqueue_head(¬if_wait->notif_waitq); } +EXPORT_SYMBOL_GPL(iwl_notification_wait_init); void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt) @@ -115,6 +117,7 @@ void iwl_notification_wait_notify(struct iwl_notif_wait_data *notif_wait, if (triggered) wake_up_all(¬if_wait->notif_waitq); } +EXPORT_SYMBOL_GPL(iwl_notification_wait_notify); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) { @@ -128,7 +131,7 @@ void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) wake_up_all(¬if_wait->notif_waitq); } - +EXPORT_SYMBOL_GPL(iwl_abort_notification_waits); void iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, @@ -152,6 +155,7 @@ iwl_init_notification_wait(struct iwl_notif_wait_data *notif_wait, list_add(&wait_entry->list, ¬if_wait->notif_waits); spin_unlock_bh(¬if_wait->notif_wait_lock); } +EXPORT_SYMBOL_GPL(iwl_init_notification_wait); int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry, @@ -175,6 +179,7 @@ int iwl_wait_notification(struct iwl_notif_wait_data *notif_wait, return -ETIMEDOUT; return 0; } +EXPORT_SYMBOL_GPL(iwl_wait_notification); void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, struct iwl_notification_wait *wait_entry) @@ -183,3 +188,4 @@ void iwl_remove_notification(struct iwl_notif_wait_data *notif_wait, list_del(&wait_entry->list); spin_unlock_bh(¬if_wait->notif_wait_lock); } +EXPORT_SYMBOL_GPL(iwl_remove_notification); -- cgit v1.2.3 From 20d3b6475226fbde372b1ce51f26b5379e340759 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 22:54:29 +0200 Subject: iwlwifi: clean up coding style in PCIe transport Mostly clean up indentation around parentheses after if, function calls, etc. and also a few unneeded line breaks and some other things. Reviewed-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | 8 +- drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c | 67 +++---- drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 68 ++++---- drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 203 +++++++++++----------- 4 files changed, 161 insertions(+), 185 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h index ccb97b9da4c3..f027769933d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h @@ -313,7 +313,7 @@ void iwl_bg_rx_replenish(struct work_struct *data); void iwl_irq_tasklet(struct iwl_trans *trans); void iwlagn_rx_replenish(struct iwl_trans *trans); void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, - struct iwl_rx_queue *q); + struct iwl_rx_queue *q); /***************************************************** * ICT @@ -328,7 +328,7 @@ irqreturn_t iwl_isr_ict(int irq, void *data); * TX / HCMD ******************************************************/ void iwl_txq_update_write_ptr(struct iwl_trans *trans, - struct iwl_tx_queue *txq); + struct iwl_tx_queue *txq); int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, dma_addr_t addr, u16 len, u8 reset); @@ -337,8 +337,8 @@ int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status); void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - u16 byte_cnt); + struct iwl_tx_queue *txq, + u16 byte_cnt); void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int queue); void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 1a7eb2a5dec3..98605fc7ad37 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -130,7 +130,7 @@ static int iwl_rx_queue_space(const struct iwl_rx_queue *q) * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue */ void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, - struct iwl_rx_queue *q) + struct iwl_rx_queue *q) { unsigned long flags; u32 reg; @@ -201,9 +201,7 @@ static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) */ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; @@ -253,9 +251,7 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) */ static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct list_head *element; struct iwl_rx_mem_buffer *rxb; @@ -278,8 +274,7 @@ static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) gfp_mask |= __GFP_COMP; /* Alloc a new receive buffer */ - page = alloc_pages(gfp_mask, - trans_pcie->rx_page_order); + page = alloc_pages(gfp_mask, trans_pcie->rx_page_order); if (!page) { if (net_ratelimit()) IWL_DEBUG_INFO(trans, "alloc_pages failed, " @@ -315,9 +310,10 @@ static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) BUG_ON(rxb->page); rxb->page = page; /* Get physical address of the RB */ - rxb->page_dma = dma_map_page(trans->dev, page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); + rxb->page_dma = + dma_map_page(trans->dev, page, 0, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); /* dma address must be no more than 36 bits */ BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); /* and also 256 byte aligned! */ @@ -465,8 +461,8 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, if (rxb->page != NULL) { rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; } else @@ -546,12 +542,12 @@ static void iwl_irq_handle_error(struct iwl_trans *trans) /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ if (trans->cfg->internal_wimax_coex && (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & - APMS_CLK_VAL_MRB_FUNC_MODE) || + APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & - APMG_PS_CTRL_VAL_RESET_REQ))) { - struct iwl_trans_pcie *trans_pcie; + APMG_PS_CTRL_VAL_RESET_REQ))) { + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); iwl_op_mode_wimax_active(trans->op_mode); wake_up(&trans->wait_command_queue); @@ -567,6 +563,8 @@ static void iwl_irq_handle_error(struct iwl_trans *trans) /* tasklet for iwlagn interrupt */ void iwl_irq_tasklet(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; u32 inta = 0; u32 handled = 0; unsigned long flags; @@ -575,10 +573,6 @@ void iwl_irq_tasklet(struct iwl_trans *trans) u32 inta_mask; #endif - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); /* Ack/clear/reset pending uCode interrupts. @@ -593,7 +587,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) * interrupt coalescing can still be achieved. */ iwl_write32(trans, CSR_INT, - trans_pcie->inta | ~trans_pcie->inta_mask); + trans_pcie->inta | ~trans_pcie->inta_mask); inta = trans_pcie->inta; @@ -602,7 +596,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) /* just for debug */ inta_mask = iwl_read32(trans, CSR_INT_MASK); IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", - inta, inta_mask); + inta, inta_mask); } #endif @@ -651,7 +645,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) hw_rfkill = iwl_is_rfkill_set(trans); IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", - hw_rfkill ? "disable radio" : "enable radio"); + hw_rfkill ? "disable radio" : "enable radio"); isr_stats->rfkill++; @@ -693,7 +687,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) * Rx "responses" (frame-received notification), and other * notifications from uCode come through here*/ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX | - CSR_INT_BIT_RX_PERIODIC)) { + CSR_INT_BIT_RX_PERIODIC)) { IWL_DEBUG_ISR(trans, "Rx interrupt\n"); if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); @@ -733,7 +727,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) */ if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) iwl_write8(trans, CSR_INT_PERIODIC_REG, - CSR_INT_PERIODIC_ENA); + CSR_INT_PERIODIC_ENA); isr_stats->rx++; } @@ -782,8 +776,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) /* Free dram table */ void iwl_free_isr_ict(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (trans_pcie->ict_tbl) { dma_free_coherent(trans->dev, ICT_SIZE, @@ -802,8 +795,7 @@ void iwl_free_isr_ict(struct iwl_trans *trans) */ int iwl_alloc_isr_ict(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie->ict_tbl = dma_alloc_coherent(trans->dev, ICT_SIZE, @@ -837,10 +829,9 @@ int iwl_alloc_isr_ict(struct iwl_trans *trans) */ void iwl_reset_ict(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 val; unsigned long flags; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); if (!trans_pcie->ict_tbl) return; @@ -868,9 +859,7 @@ void iwl_reset_ict(struct iwl_trans *trans) /* Device is going down disable ict interrupt usage */ void iwl_disable_ict(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; spin_lock_irqsave(&trans_pcie->irq_lock, flags); @@ -934,7 +923,7 @@ static irqreturn_t iwl_isr(int irq, void *data) if (likely(inta)) tasklet_schedule(&trans_pcie->irq_tasklet); else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) + !trans_pcie->inta) iwl_enable_interrupts(trans); unplugged: @@ -945,7 +934,7 @@ static irqreturn_t iwl_isr(int irq, void *data) /* re-enable interrupts here since we don't have anything to service. */ /* only Re-enable if disabled by irq and no schedules tasklet. */ if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) + !trans_pcie->inta) iwl_enable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); @@ -1036,7 +1025,7 @@ irqreturn_t iwl_isr_ict(int irq, void *data) inta = (0xff & val) | ((0xff00 & val) << 16); IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", - inta, inta_mask, val); + inta, inta_mask, val); inta &= trans_pcie->inta_mask; trans_pcie->inta |= inta; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index e727c4f506c7..6f601cd05a94 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c @@ -47,12 +47,11 @@ * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - u16 byte_cnt) + struct iwl_tx_queue *txq, + u16 byte_cnt) { struct iwlagn_scd_bc_tbl *scd_bc_tbl; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int write_ptr = txq->q.write_ptr; int txq_id = txq->q.id; u8 sec_ctl = 0; @@ -270,7 +269,7 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, /* Each TFD can point to a maximum 20 Tx buffers */ if (num_tbs >= IWL_NUM_OF_TBS) { IWL_ERR(trans, "Error can not send more than %d chunks\n", - IWL_NUM_OF_TBS); + IWL_NUM_OF_TBS); return -EINVAL; } @@ -279,7 +278,7 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, if (unlikely(addr & ~IWL_TX_DMA_MASK)) IWL_ERR(trans, "Unaligned address = %llx\n", - (unsigned long long)addr); + (unsigned long long)addr); iwl_tfd_set_tb(tfd, num_tbs, addr, len); @@ -383,15 +382,13 @@ static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, } static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid, - u16 txq_id) + u16 txq_id) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 tbl_dw_addr; u32 tbl_dw; u16 scd_q2ratid; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK; tbl_dw_addr = trans_pcie->scd_base_addr + @@ -419,12 +416,11 @@ static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id) (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } -void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, - int txq_id, u32 index) +void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index) { IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d\n", txq_id, index & 0xff); iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (index & 0xff) | (txq_id << 8)); + (index & 0xff) | (txq_id << 8)); iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), index); } @@ -615,13 +611,13 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } IWL_DEBUG_HC(trans, - "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", - trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), - out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, - q->write_ptr, idx, trans_pcie->cmd_queue); + "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", + trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), + out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), + cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, - DMA_BIDIRECTIONAL); + DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { idx = -ENOMEM; goto out; @@ -630,8 +626,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, copy_size); - iwlagn_txq_attach_buf_to_tfd(trans, txq, - phys_addr, copy_size, 1); + iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); #ifdef CONFIG_IWLWIFI_DEVICE_TRACING trace_bufs[0] = &out_cmd->hdr; trace_lens[0] = copy_size; @@ -643,8 +638,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) continue; if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) continue; - phys_addr = dma_map_single(trans->dev, - (void *)cmd->data[i], + phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { iwl_unmap_tfd(trans, out_meta, @@ -723,9 +717,10 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, lockdep_assert_held(&txq->lock); if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) { - IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), " - "index %d is out of range [0-%d] %d %d.\n", __func__, - txq_id, idx, q->n_bd, q->write_ptr, q->read_ptr); + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, idx, q->n_bd, + q->write_ptr, q->read_ptr); return; } @@ -733,8 +728,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { if (nfreed++ > 0) { - IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx, - q->write_ptr, q->read_ptr); + IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", + idx, q->write_ptr, q->read_ptr); iwl_op_mode_nic_error(trans->op_mode); } @@ -771,9 +766,9 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, * in the queue management code. */ if (WARN(txq_id != trans_pcie->cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, trans_pcie->cmd_queue, sequence, - trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, - trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { + txq_id, trans_pcie->cmd_queue, sequence, + trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, + trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { iwl_print_hex_error(trans, pkt, 32); return; } @@ -869,8 +864,9 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } ret = wait_event_timeout(trans->wait_command_queue, - !test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status), - HOST_COMPLETE_TIMEOUT); + !test_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status), + HOST_COMPLETE_TIMEOUT); if (!ret) { if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { struct iwl_tx_queue *txq = @@ -955,10 +951,10 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, if ((index >= q->n_bd) || (iwl_queue_used(q, last_to_free) == 0)) { - IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), " - "last_to_free %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, last_to_free, q->n_bd, - q->write_ptr, q->read_ptr); + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, last_to_free, q->n_bd, + q->write_ptr, q->read_ptr); return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 12d84e9a2d64..02ef48c64011 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -84,8 +84,7 @@ static int iwl_trans_rx_alloc(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; struct device *dev = trans->dev; @@ -112,7 +111,7 @@ static int iwl_trans_rx_alloc(struct iwl_trans *trans) err_rb_stts: dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); + rxq->bd, rxq->bd_dma); memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); rxq->bd = NULL; err_bd: @@ -121,8 +120,7 @@ err_bd: static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; int i; @@ -132,8 +130,8 @@ static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) * to an SKB, so we need to unmap and free potential storage */ if (rxq->pool[i].page != NULL) { dma_unmap_page(trans->dev, rxq->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); __free_pages(rxq->pool[i].page, trans_pcie->rx_page_order); rxq->pool[i].page = NULL; @@ -191,8 +189,7 @@ static void iwl_trans_rx_hw_init(struct iwl_trans *trans, static int iwl_rx_init(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; int i, err; @@ -234,10 +231,8 @@ static int iwl_rx_init(struct iwl_trans *trans) static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; - unsigned long flags; /*if rxq->bd is NULL, it means that nothing has been allocated, @@ -272,11 +267,11 @@ static int iwl_trans_rx_stop(struct iwl_trans *trans) /* stop Rx DMA */ iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, - FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); } -static inline int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr, size_t size) +static int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size) { if (WARN_ON(ptr->addr)) return -EINVAL; @@ -289,8 +284,8 @@ static inline int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, return 0; } -static inline void iwlagn_free_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr) +static void iwlagn_free_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr) { if (unlikely(!ptr->addr)) return; @@ -327,12 +322,12 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) } static int iwl_trans_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue *txq, int slots_num, - u32 txq_id) + struct iwl_tx_queue *txq, int slots_num, + u32 txq_id) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; int i; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (WARN_ON(txq->entries || txq->tfds)) return -EINVAL; @@ -453,6 +448,7 @@ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; struct device *dev = trans->dev; int i; + if (WARN_ON(!txq)) return; @@ -572,11 +568,11 @@ error: } static int iwl_tx_init(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; int txq_id, slots_num; unsigned long flags; bool alloc = false; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (!trans_pcie->txq) { ret = iwl_trans_tx_alloc(trans); @@ -641,10 +637,9 @@ static void iwl_set_pwr_vmain(struct iwl_trans *trans) static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int pos; u16 pci_lnk_ctl; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); struct pci_dev *pci_dev = trans_pcie->pci_dev; @@ -698,14 +693,14 @@ static int iwl_apm_init(struct iwl_trans *trans) /* Disable L0S exit timer (platform NMI Work/Around) */ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); /* * Disable L0s without affecting L1; * don't wait for ICH L0s (ICH bug W/A) */ iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); /* Set FH wait threshold to maximum (HW error during stress W/A) */ iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); @@ -715,7 +710,7 @@ static int iwl_apm_init(struct iwl_trans *trans) * wake device's PCI Express link L1a -> L0s */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); iwl_apm_config(trans); @@ -736,8 +731,8 @@ static int iwl_apm_init(struct iwl_trans *trans) * and accesses to uCode SRAM. */ ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) { IWL_DEBUG_INFO(trans, "Failed to init the card\n"); goto out; @@ -771,8 +766,8 @@ static int iwl_apm_stop_master(struct iwl_trans *trans) iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); ret = iwl_poll_bit(trans, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + CSR_RESET_REG_FLAG_MASTER_DISABLED, + CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); if (ret) IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); @@ -814,8 +809,7 @@ static int iwl_nic_init(struct iwl_trans *trans) iwl_apm_init(trans); /* Set interrupt coalescing calibration timer to default (512 usecs) */ - iwl_write8(trans, CSR_INT_COALESCING, - IWL_HOST_INT_CALIB_TIMEOUT_DEF); + iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); @@ -834,8 +828,7 @@ static int iwl_nic_init(struct iwl_trans *trans) if (trans->cfg->base_params->shadow_reg_enable) { /* enable shadow regs in HW */ - iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, - 0x800FFFFF); + iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); } return 0; @@ -849,13 +842,13 @@ static int iwl_set_hw_ready(struct iwl_trans *trans) int ret; iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); /* See if we got it */ ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - HW_READY_TIMEOUT); + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + HW_READY_TIMEOUT); IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); return ret; @@ -875,11 +868,11 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans) /* If HW is not ready, prepare the conditions to check again */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); + CSR_HW_IF_CONFIG_REG_PREPARE); ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, - CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, + CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); if (ret < 0) return ret; @@ -906,32 +899,33 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, trans_pcie->ucode_write_complete = false; iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); + FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); iwl_write_direct32(trans, - FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), dst_addr); + FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), + dst_addr); iwl_write_direct32(trans, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); iwl_write_direct32(trans, - FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), - (iwl_get_dma_hi_addr(phy_addr) - << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), + (iwl_get_dma_hi_addr(phy_addr) + << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | - FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); + FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), + 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | + 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | + FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | - FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); + FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | + FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", section_num); @@ -1068,7 +1062,7 @@ static void iwl_tx_start(struct iwl_trans *trans) reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); iwl_write_prph(trans, SCD_QUEUECHAIN_SEL, - SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie)); + SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie)); iwl_write_prph(trans, SCD_AGGR_SEL, 0); /* initiate the queues */ @@ -1089,7 +1083,7 @@ static void iwl_tx_start(struct iwl_trans *trans) } iwl_write_prph(trans, SCD_INTERRUPT_MASK, - IWL_MASK(0, trans->cfg->base_params->num_of_queues)); + IWL_MASK(0, trans->cfg->base_params->num_of_queues)); /* Activate all Tx DMA/FIFO channels */ iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); @@ -1113,7 +1107,7 @@ static void iwl_tx_start(struct iwl_trans *trans) /* Enable L1-Active */ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) @@ -1127,9 +1121,9 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) */ static int iwl_trans_tx_stop(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ch, txq_id, ret; unsigned long flags; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); /* Turn off all Tx DMA fifos */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); @@ -1141,13 +1135,13 @@ static int iwl_trans_tx_stop(struct iwl_trans *trans) iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), - 1000); + FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); if (ret < 0) - IWL_ERR(trans, "Failing on timeout while stopping" - " DMA channel %d [0x%08x]", ch, - iwl_read_direct32(trans, - FH_TSSR_TX_STATUS_REG)); + IWL_ERR(trans, + "Failing on timeout while stopping DMA channel %d [0x%08x]", + ch, + iwl_read_direct32(trans, + FH_TSSR_TX_STATUS_REG)); } spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); @@ -1166,8 +1160,8 @@ static int iwl_trans_tx_stop(struct iwl_trans *trans) static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) { - unsigned long flags; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; /* tell the device to stop sending interrupts */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); @@ -1197,7 +1191,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) /* Make sure (redundant) we've released our request to stay awake */ iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ iwl_apm_stop(trans); @@ -1271,8 +1265,9 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, txq->entries[q->write_ptr].cmd = dev_cmd; dev_cmd->hdr.cmd = REPLY_TX; - dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | - INDEX_TO_SEQ(q->write_ptr))); + dev_cmd->hdr.sequence = + cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_meta = &txq->entries[q->write_ptr].meta; @@ -1337,7 +1332,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, /* take back ownership of DMA buffer to enable update */ dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); + DMA_BIDIRECTIONAL); tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); @@ -1349,7 +1344,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); + DMA_BIDIRECTIONAL); trace_iwlwifi_dev_tx(trans->dev, &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], @@ -1388,8 +1383,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int err; bool hw_rfkill; @@ -1402,7 +1396,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) iwl_alloc_isr_ict(trans); err = request_irq(trans_pcie->irq, iwl_isr_ict, IRQF_SHARED, - DRV_NAME, trans); + DRV_NAME, trans); if (err) { IWL_ERR(trans, "Error allocating IRQ %d\n", trans_pcie->irq); @@ -1440,9 +1434,9 @@ error: static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, bool op_mode_leaving) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill; unsigned long flags; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_apm_stop(trans); @@ -1546,8 +1540,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, void iwl_trans_pcie_free(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_trans_pcie_tx_free(trans); #ifndef CONFIG_IWLWIFI_IDI @@ -1809,8 +1802,8 @@ static const struct file_operations iwl_dbgfs_##name##_ops = { \ }; static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) + char __user *user_buf, + size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1846,11 +1839,11 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, } static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { + char __user *user_buf, + size_t count, loff_t *ppos) +{ struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; char buf[256]; int pos = 0; @@ -1874,11 +1867,10 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, static ssize_t iwl_dbgfs_interrupt_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) { - + size_t count, loff_t *ppos) +{ struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; int pos = 0; @@ -1936,8 +1928,7 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; char buf[8]; @@ -1957,8 +1948,8 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file, } static ssize_t iwl_dbgfs_csr_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) + const char __user *user_buf, + size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; char buf[8]; @@ -1978,8 +1969,8 @@ static ssize_t iwl_dbgfs_csr_write(struct file *file, } static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) + char __user *user_buf, + size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; char *buf; @@ -2022,7 +2013,7 @@ DEBUGFS_WRITE_FILE_OPS(fw_restart); * */ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) + struct dentry *dir) { DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); @@ -2034,9 +2025,10 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, } #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ return 0; } - + struct dentry *dir) +{ + return 0; +} #endif /*CONFIG_IWLWIFI_DEBUGFS */ static const struct iwl_trans_ops trans_ops_pcie = { @@ -2081,7 +2073,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, int err; trans = kzalloc(sizeof(struct iwl_trans) + - sizeof(struct iwl_trans_pcie), GFP_KERNEL); + sizeof(struct iwl_trans_pcie), GFP_KERNEL); if (WARN_ON(!trans)) return NULL; @@ -2097,7 +2089,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, /* W/A - seems to solve weird behavior. We need to remove this if we * don't want to stay in L1 all the time. This wastes a lot of power */ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); + PCIE_LINK_STATE_CLKPM); if (pci_enable_device(pdev)) { err = -ENODEV; @@ -2113,7 +2105,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (!err) err = pci_set_consistent_dma_mask(pdev, - DMA_BIT_MASK(32)); + DMA_BIT_MASK(32)); /* both attempts failed: */ if (err) { dev_printk(KERN_ERR, &pdev->dev, @@ -2136,13 +2128,13 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_len = 0x%08llx\n", - (unsigned long long) pci_resource_len(pdev, 0)); + "pci_resource_len = 0x%08llx\n", + (unsigned long long) pci_resource_len(pdev, 0)); dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_base = %p\n", trans_pcie->hw_base); + "pci_resource_base = %p\n", trans_pcie->hw_base); dev_printk(KERN_INFO, &pdev->dev, - "HW Revision ID = 0x%X\n", pdev->revision); + "HW Revision ID = 0x%X\n", pdev->revision); /* We disable the RETRY_TIMEOUT register (0x41) to keep * PCI Tx retries from interfering with C3 CPU state */ @@ -2151,7 +2143,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, err = pci_enable_msi(pdev); if (err) dev_printk(KERN_ERR, &pdev->dev, - "pci_enable_msi failed(0X%x)", err); + "pci_enable_msi failed(0X%x)", err); trans->dev = &pdev->dev; trans_pcie->irq = pdev->irq; @@ -2183,4 +2175,3 @@ out_no_pci: kfree(trans); return NULL; } - -- cgit v1.2.3 From d38069d1e3989f0358109ee4d4f72bcaf555813e Mon Sep 17 00:00:00 2001 From: Meenakshi Venkataraman Date: Wed, 16 May 2012 22:54:30 +0200 Subject: iwlwifi: add debugging to shadow registers and fix typo Add a debugging log when using shadow registers. Also fix a minor typo in this connection. Signed-off-by: Meenakshi Venkataraman Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-config.h | 2 +- drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 67b28aa7f9be..a52818bbfe98 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -143,7 +143,7 @@ enum iwl_led_mode { * @chain_noise_scale: default chain noise scale used for gain computation * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging - * @shadow_reg_enable: HW shadhow register bit + * @shadow_reg_enable: HW shadow register support * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up * @no_idle_support: do not support idle mode */ diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 02ef48c64011..1e50401023e0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -829,6 +829,7 @@ static int iwl_nic_init(struct iwl_trans *trans) if (trans->cfg->base_params->shadow_reg_enable) { /* enable shadow regs in HW */ iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); + IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); } return 0; -- cgit v1.2.3 From aa430da41019c1694f6a8e3b8bef1d12ed52b0ad Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 23:50:18 +0200 Subject: cfg80211: provide channel to start_ap function Instead of setting the channel first and then starting the AP, let cfg80211 store the channel and provide it as one of the AP settings. This means that now you have to set the channel before you can start an AP interface, but since hostapd/wpa_supplicant always do that we're OK with this change. Alternatively, it's now possible to give the channel as an attribute to the start-ap nl80211 command, overriding any preset channel. Cc: Kalle Valo Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 36 +++--------------------------- drivers/net/wireless/ath/ath6kl/core.h | 3 --- drivers/net/wireless/ath/ath6kl/main.c | 1 - 3 files changed, 3 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index b869a358ce43..f27e9732951d 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2585,35 +2585,6 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif, return 0; } -static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) -{ - struct ath6kl_vif *vif; - - /* - * 'dev' could be NULL if a channel change is required for the hardware - * device itself, instead of a particular VIF. - * - * FIXME: To be handled properly when monitor mode is supported. - */ - if (!dev) - return -EBUSY; - - vif = netdev_priv(dev); - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", - __func__, chan->center_freq, chan->hw_value); - vif->next_chan = chan->center_freq; - vif->next_ch_type = channel_type; - vif->next_ch_band = chan->band; - - return 0; -} - static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon, u8 *rsn_capab) { @@ -2791,7 +2762,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.ssid_len = vif->ssid_len; memcpy(p.ssid, vif->ssid, vif->ssid_len); p.dot11_auth_mode = vif->dot11_auth_mode; - p.ch = cpu_to_le16(vif->next_chan); + p.ch = cpu_to_le16(info->channel->center_freq); /* Enable uAPSD support by default */ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); @@ -2815,8 +2786,8 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, return res; } - if (ath6kl_set_htcap(vif, vif->next_ch_band, - vif->next_ch_type != NL80211_CHAN_NO_HT)) + if (ath6kl_set_htcap(vif, info->channel->band, + info->channel_type != NL80211_CHAN_NO_HT)) return -EIO; /* @@ -3271,7 +3242,6 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .suspend = __ath6kl_cfg80211_suspend, .resume = __ath6kl_cfg80211_resume, #endif - .set_channel = ath6kl_set_channel, .start_ap = ath6kl_start_ap, .change_beacon = ath6kl_change_beacon, .stop_ap = ath6kl_stop_ap, diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 4d9c6f142698..8443b2a4133e 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -553,9 +553,6 @@ struct ath6kl_vif { u32 last_cancel_roc_id; u32 send_action_id; bool probe_req_report; - u16 next_chan; - enum nl80211_channel_type next_ch_type; - enum ieee80211_band next_ch_band; u16 assoc_bss_beacon_int; u16 listen_intvl_t; u16 bmiss_time_t; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index e5524470529c..b836f2795114 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -598,7 +598,6 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) struct ath6kl *ar = vif->ar; - vif->next_chan = channel; vif->profile.ch = cpu_to_le16(channel); switch (vif->nw_type) { -- cgit v1.2.3 From aa4bbbe44880a625ef065f0ebbbcc6adf34e03a6 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Wed, 16 May 2012 21:24:54 -0700 Subject: mwifiex: invalidate bss config before setting channel for uAP Mark bss_config parameters as invalid before setting AP channel. This prevents from setting invalid parameters while setting AP channel to FW. Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/uap_cmd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 76dfbc42a732..e9482c97e779 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -416,6 +416,7 @@ int mwifiex_uap_set_channel(struct mwifiex_private *priv, int channel) if (!bss_cfg) return -ENOMEM; + mwifiex_set_sys_config_invalid_data(bss_cfg); bss_cfg->band_cfg = BAND_CONFIG_MANUAL; bss_cfg->channel = channel; -- cgit v1.2.3 From 605b73af3273336b2465962d99a937069710e104 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Wed, 16 May 2012 21:24:55 -0700 Subject: mwifiex: support NL80211_HIDDEN_SSID_ZERO_LEN for uAP mwifiex uAP supports NL80211_HIDDEN_SSID_ZERO_LEN type of hidden SSID only. NL80211_HIDDEN_SSID_ZERO_CONTENTS is not supported. Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 13 +++++++++++++ drivers/net/wireless/mwifiex/fw.h | 6 ++++++ drivers/net/wireless/mwifiex/uap_cmd.c | 9 +++++++++ 3 files changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 87671446e24b..015fec3371a0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -948,6 +948,19 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, bss_cfg->ssid.ssid_len = params->ssid_len; } + switch (params->hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + bss_cfg->bcast_ssid_ctl = 1; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + bss_cfg->bcast_ssid_ctl = 0; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + /* firmware doesn't support this type of hidden SSID */ + default: + return -EINVAL; + } + if (mwifiex_set_secure_params(priv, bss_cfg, params)) { kfree(bss_cfg); wiphy_err(wiphy, "Failed to parse secuirty parameters!\n"); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 9f674bbebe65..561452a5c818 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -122,6 +122,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) #define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) #define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) +#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) #define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) #define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) #define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) @@ -1209,6 +1210,11 @@ struct host_cmd_tlv_ssid { u8 ssid[0]; } __packed; +struct host_cmd_tlv_bcast_ssid { + struct host_cmd_tlv tlv; + u8 bcast_ctl; +} __packed; + struct host_cmd_tlv_beacon_period { struct host_cmd_tlv tlv; __le16 period; diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index e9482c97e779..8173ab66066d 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -132,6 +132,7 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) struct host_cmd_tlv_dtim_period *dtim_period; struct host_cmd_tlv_beacon_period *beacon_period; struct host_cmd_tlv_ssid *ssid; + struct host_cmd_tlv_bcast_ssid *bcast_ssid; struct host_cmd_tlv_channel_band *chan_band; struct host_cmd_tlv_frag_threshold *frag_threshold; struct host_cmd_tlv_rts_threshold *rts_threshold; @@ -153,6 +154,14 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) cmd_size += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len; tlv += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len; + + bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; + bcast_ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); + bcast_ssid->tlv.len = + cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); + bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; + cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); + tlv += sizeof(struct host_cmd_tlv_bcast_ssid); } if (bss_cfg->channel && bss_cfg->channel <= MAX_CHANNEL_BAND_BG) { chan_band = (struct host_cmd_tlv_channel_band *)tlv; -- cgit v1.2.3 From fdbfff73408f99799724f583cbc2a0ce3263c6a7 Mon Sep 17 00:00:00 2001 From: Stanislav Yakovlev Date: Thu, 17 May 2012 15:56:35 -0400 Subject: net/wireless: ipw2200: introduce ipw_read_eeprom function We separate reading of device's eeprom content from writing it back to the device's sram. Signed-off-by: Stanislav Yakovlev Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2x00/ipw2200.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 0036737fe8e3..254b89223276 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -2701,6 +2701,20 @@ static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6); } +static void ipw_read_eeprom(struct ipw_priv *priv) +{ + int i; + __le16 *eeprom = (__le16 *) priv->eeprom; + + IPW_DEBUG_TRACE(">>\n"); + + /* read entire contents of eeprom into private buffer */ + for (i = 0; i < 128; i++) + eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i)); + + IPW_DEBUG_TRACE("<<\n"); +} + /* * Either the device driver (i.e. the host) or the firmware can * load eeprom data into the designated region in SRAM. If neither @@ -2712,14 +2726,9 @@ static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) static void ipw_eeprom_init_sram(struct ipw_priv *priv) { int i; - __le16 *eeprom = (__le16 *) priv->eeprom; IPW_DEBUG_TRACE(">>\n"); - /* read entire contents of eeprom into private buffer */ - for (i = 0; i < 128; i++) - eeprom[i] = cpu_to_le16(eeprom_read_u16(priv, (u8) i)); - /* If the data looks correct, then copy it to our private copy. Otherwise let the firmware know to perform the operation @@ -3643,8 +3652,10 @@ static int ipw_load(struct ipw_priv *priv) /* ack fw init done interrupt */ ipw_write32(priv, IPW_INTA_RW, IPW_INTA_BIT_FW_INITIALIZATION_DONE); - /* read eeprom data and initialize the eeprom region of sram */ + /* read eeprom data */ priv->eeprom_delay = 1; + ipw_read_eeprom(priv); + /* initialize the eeprom region of sram */ ipw_eeprom_init_sram(priv); /* enable interrupts */ -- cgit v1.2.3 From 2281a0f3346ae891e3c2216ad05359e0b7934bf0 Mon Sep 17 00:00:00 2001 From: Jan Ceuleers Date: Tue, 5 Jun 2012 03:42:11 +0000 Subject: gianfar: whitespace cleanup - pointers and multiplications Signed-off-by: Jan Ceuleers Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 0741aded9eb0..5ca7b9eaa84a 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -266,8 +266,8 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) tx_queue->tx_bd_dma_base = addr; tx_queue->dev = ndev; /* enet DMA only understands physical addresses */ - addr += sizeof(struct txbd8) *tx_queue->tx_ring_size; - vaddr += sizeof(struct txbd8) *tx_queue->tx_ring_size; + addr += sizeof(struct txbd8) * tx_queue->tx_ring_size; + vaddr += sizeof(struct txbd8) * tx_queue->tx_ring_size; } /* Start the rx descriptor ring where the tx ring leaves off */ @@ -276,8 +276,8 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) rx_queue->rx_bd_base = vaddr; rx_queue->rx_bd_dma_base = addr; rx_queue->dev = ndev; - addr += sizeof (struct rxbd8) * rx_queue->rx_ring_size; - vaddr += sizeof (struct rxbd8) * rx_queue->rx_ring_size; + addr += sizeof(struct rxbd8) * rx_queue->rx_ring_size; + vaddr += sizeof(struct rxbd8) * rx_queue->rx_ring_size; } /* Setup the skbuff rings */ @@ -2590,7 +2590,7 @@ static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp, gfar_init_rxbdp(rx_queue, bdp, buf); } -static struct sk_buff * gfar_alloc_skb(struct net_device *dev) +static struct sk_buff *gfar_alloc_skb(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct sk_buff *skb = NULL; @@ -2604,7 +2604,7 @@ static struct sk_buff * gfar_alloc_skb(struct net_device *dev) return skb; } -struct sk_buff * gfar_new_skb(struct net_device *dev) +struct sk_buff *gfar_new_skb(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct sk_buff *skb = NULL; @@ -2728,8 +2728,8 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, } /* gfar_clean_rx_ring() -- Processes each frame in the rx ring - * until the budget/quota has been reached. Returns the number - * of frames handled + * until the budget/quota has been reached. Returns the number + * of frames handled */ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) { -- cgit v1.2.3 From 0977f817df4d0f629952b4c31d650640188b4e45 Mon Sep 17 00:00:00 2001 From: Jan Ceuleers Date: Tue, 5 Jun 2012 03:42:12 +0000 Subject: gianfar: comment cleanup Signed-off-by: Jan Ceuleers Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 155 +++++++++++++++++-------------- 1 file changed, 83 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 5ca7b9eaa84a..314456000335 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1,5 +1,4 @@ -/* - * drivers/net/ethernet/freescale/gianfar.c +/* drivers/net/ethernet/freescale/gianfar.c * * Gianfar Ethernet Driver * This driver is designed for the non-CPM ethernet controllers @@ -405,7 +404,8 @@ static void gfar_init_mac(struct net_device *ndev) gfar_write(®s->attreli, attrs); /* Start with defaults, and add stashing or locking - * depending on the approprate variables */ + * depending on the approprate variables + */ attrs = ATTR_INIT_SETTINGS; if (priv->bd_stash_en) @@ -652,7 +652,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) priv->num_rx_queues = num_rx_qs; priv->num_grps = 0x0; - /* Init Rx queue filer rule set linked list*/ + /* Init Rx queue filer rule set linked list */ INIT_LIST_HEAD(&priv->rx_list.list); priv->rx_list.count = 0; mutex_init(&priv->rx_queue_access); @@ -960,7 +960,8 @@ static void gfar_detect_errata(struct gfar_private *priv) } /* Set up the ethernet device structure, private data, - * and anything else we need before we start */ + * and anything else we need before we start + */ static int gfar_probe(struct platform_device *ofdev) { u32 tempval; @@ -991,8 +992,9 @@ static int gfar_probe(struct platform_device *ofdev) gfar_detect_errata(priv); - /* Stop the DMA engine now, in case it was running before */ - /* (The firmware could have used it, and left it running). */ + /* Stop the DMA engine now, in case it was running before + * (The firmware could have used it, and left it running). + */ gfar_halt(dev); /* Reset MAC layer */ @@ -1098,7 +1100,8 @@ static int gfar_probe(struct platform_device *ofdev) /* Need to reverse the bit maps as bit_map's MSB is q0 * but, for_each_set_bit parses from right to left, which - * basically reverses the queue numbers */ + * basically reverses the queue numbers + */ for (i = 0; i< priv->num_grps; i++) { priv->gfargrp[i].tx_bit_map = reverse_bitmap( priv->gfargrp[i].tx_bit_map, MAX_TX_QS); @@ -1107,7 +1110,8 @@ static int gfar_probe(struct platform_device *ofdev) } /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values, - * also assign queues to groups */ + * also assign queues to groups + */ for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) { priv->gfargrp[grp_idx].num_rx_queues = 0x0; for_each_set_bit(i, &priv->gfargrp[grp_idx].rx_bit_map, @@ -1149,7 +1153,7 @@ static int gfar_probe(struct platform_device *ofdev) priv->rx_queue[i]->rxic = DEFAULT_RXIC; } - /* always enable rx filer*/ + /* always enable rx filer */ priv->rx_filer_enable = 1; /* Enable most messages by default */ priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; @@ -1189,8 +1193,9 @@ static int gfar_probe(struct platform_device *ofdev) /* Print out the device info */ netdev_info(dev, "mac: %pM\n", dev->dev_addr); - /* Even more device info helps when determining which kernel */ - /* provided which set of benchmarks. */ + /* Even more device info helps when determining which kernel + * provided which set of benchmarks. + */ netdev_info(dev, "Running with NAPI enabled\n"); for (i = 0; i < priv->num_rx_queues; i++) netdev_info(dev, "RX BD ring size for Q[%d]: %d\n", @@ -1398,8 +1403,7 @@ static phy_interface_t gfar_get_interface(struct net_device *dev) else { phy_interface_t interface = priv->interface; - /* - * This isn't autodetected right now, so it must + /* This isn't autodetected right now, so it must * be set by the device tree or platform code. */ if (interface == PHY_INTERFACE_MODE_RGMII_ID) @@ -1453,8 +1457,7 @@ static int init_phy(struct net_device *dev) return 0; } -/* - * Initialize TBI PHY interface for communicating with the +/* Initialize TBI PHY interface for communicating with the * SERDES lynx PHY on the chip. We communicate with this PHY * through the MDIO bus on each controller, treating it as a * "normal" PHY at the address found in the TBIPA register. We assume @@ -1479,8 +1482,7 @@ static void gfar_configure_serdes(struct net_device *dev) return; } - /* - * If the link is already up, we must already be ok, and don't need to + /* If the link is already up, we must already be ok, and don't need to * configure and reset the TBI<->SerDes link. Maybe U-Boot configured * everything for us? Resetting it takes the link down and requires * several seconds for it to come back. @@ -1554,15 +1556,13 @@ static int __gfar_is_rx_idle(struct gfar_private *priv) { u32 res; - /* - * Normaly TSEC should not hang on GRS commands, so we should + /* Normaly TSEC should not hang on GRS commands, so we should * actually wait for IEVENT_GRSC flag. */ if (likely(!gfar_has_errata(priv, GFAR_ERRATA_A002))) return 0; - /* - * Read the eTSEC register at offset 0xD1C. If bits 7-14 are + /* Read the eTSEC register at offset 0xD1C. If bits 7-14 are * the same as bits 23-30, the eTSEC Rx is assumed to be idle * and the Rx can be safely reset. */ @@ -1718,7 +1718,8 @@ static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue) } /* If there are any tx skbs or rx skbs still around, free them. - * Then free tx_skbuff and rx_skbuff */ + * Then free tx_skbuff and rx_skbuff + */ static void free_skb_resources(struct gfar_private *priv) { struct gfar_priv_tx_q *tx_queue = NULL; @@ -1827,10 +1828,12 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) int err; /* If the device has multiple interrupts, register for - * them. Otherwise, only register for the one */ + * them. Otherwise, only register for the one + */ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { /* Install our interrupt handlers for Error, - * Transmit, and Receive */ + * Transmit, and Receive + */ if ((err = request_irq(grp->interruptError, gfar_error, 0, grp->int_name_er,grp)) < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", @@ -1914,8 +1917,9 @@ irq_fail: return err; } -/* Called when something needs to use the ethernet device */ -/* Returns 0 for success. */ +/* Called when something needs to use the ethernet device + * Returns 0 for success. + */ static int gfar_enet_open(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); @@ -1970,8 +1974,9 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb, */ flags = TXFCB_DEFAULT; - /* Tell the controller what the protocol is */ - /* And provide the already calculated phcs */ + /* Tell the controller what the protocol is + * And provide the already calculated phcs + */ if (ip_hdr(skb)->protocol == IPPROTO_UDP) { flags |= TXFCB_UDP; fcb->phcs = udp_hdr(skb)->check; @@ -1981,7 +1986,8 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb, /* l3os is the distance between the start of the * frame (skb->data) and the start of the IP hdr. * l4os is the distance between the start of the - * l3 hdr and the l4 hdr */ + * l3 hdr and the l4 hdr + */ fcb->l3os = (u16)(skb_network_offset(skb) - fcb_length); fcb->l4os = skb_network_header_len(skb); @@ -2008,8 +2014,9 @@ static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base, return skip_txbd(bdp, 1, base, ring_size); } -/* This is called by the kernel when a frame is ready for transmission. */ -/* It is pointed to by the dev->hard_start_xmit function pointer */ +/* This is called by the kernel when a frame is ready for transmission. + * It is pointed to by the dev->hard_start_xmit function pointer + */ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); @@ -2024,8 +2031,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) unsigned long flags; unsigned int nr_frags, nr_txbds, length, fcb_length = GMAC_FCB_LEN; - /* - * TOE=1 frames larger than 2500 bytes may see excess delays + /* TOE=1 frames larger than 2500 bytes may see excess delays * before start of transmission. */ if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_76) && @@ -2177,8 +2183,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) txbdp_start->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - /* - * If time stamping is requested one additional TxBD must be set up. The + /* If time stamping is requested one additional TxBD must be set up. The * first TxBD points to the FCB and must have a data length of * GMAC_FCB_LEN. The second TxBD points to the actual frame data with * the full frame length. @@ -2194,8 +2199,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) netdev_tx_sent_queue(txq, skb->len); - /* - * We can work in parallel with gfar_clean_tx_ring(), except + /* We can work in parallel with gfar_clean_tx_ring(), except * when modifying num_txbdfree. Note that we didn't grab the lock * when we were reading the num_txbdfree and checking for available * space, that's because outside of this function it can only grow, @@ -2208,8 +2212,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) */ spin_lock_irqsave(&tx_queue->txlock, flags); - /* - * The powerpc-specific eieio() is used, as wmb() has too strong + /* The powerpc-specific eieio() is used, as wmb() has too strong * semantics (it requires synchronization between cacheable and * uncacheable mappings, which eieio doesn't provide and which we * don't need), thus requiring a more expensive sync instruction. At @@ -2225,7 +2228,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_queue->tx_skbuff[tx_queue->skb_curtx] = skb; /* Update the current skb pointer to the next entry we will use - * (wrapping if necessary) */ + * (wrapping if necessary) + */ tx_queue->skb_curtx = (tx_queue->skb_curtx + 1) & TX_RING_MOD_MASK(tx_queue->tx_ring_size); @@ -2235,7 +2239,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_queue->num_txbdfree -= (nr_txbds); /* If the next BD still needs to be cleaned up, then the bds - are full. We need to tell the kernel to stop sending us stuff. */ + * are full. We need to tell the kernel to stop sending us stuff. + */ if (!tx_queue->num_txbdfree) { netif_tx_stop_queue(txq); @@ -2365,7 +2370,8 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) INCREMENTAL_BUFFER_SIZE; /* Only stop and start the controller if it isn't already - * stopped, and we changed something */ + * stopped, and we changed something + */ if ((oldsize != tempsize) && (dev->flags & IFF_UP)) stop_gfar(dev); @@ -2378,7 +2384,8 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) /* If the mtu is larger than the max size for standard * ethernet frames (ie, a jumbo frame), then set maccfg2 - * to allow huge frames, and to check the length */ + * to allow huge frames, and to check the length + */ tempval = gfar_read(®s->maccfg2); if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE || @@ -2464,8 +2471,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) frags = skb_shinfo(skb)->nr_frags; - /* - * When time stamping, one additional TxBD must be freed. + /* When time stamping, one additional TxBD must be freed. * Also, we need to dma_unmap_single() the TxPAL. */ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) @@ -2516,8 +2522,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) bytes_sent += skb->len; - /* - * If there's room in the queue (limit it to rx_buffer_size) + /* If there's room in the queue (limit it to rx_buffer_size) * we add this skb back into the pool, if it's the right size */ if (skb_queue_len(&priv->rx_recycle) < rx_queue->rx_ring_size && @@ -2561,8 +2566,7 @@ static void gfar_schedule_cleanup(struct gfar_priv_grp *gfargrp) gfar_write(&gfargrp->regs->imask, IMASK_RTX_DISABLED); __napi_schedule(&gfargrp->napi); } else { - /* - * Clear IEVENT, so interrupts aren't called again + /* Clear IEVENT, so interrupts aren't called again * because of the packets that have already arrived. */ gfar_write(&gfargrp->regs->ievent, IEVENT_RTX_MASK); @@ -2622,8 +2626,7 @@ static inline void count_errors(unsigned short status, struct net_device *dev) struct net_device_stats *stats = &dev->stats; struct gfar_extra_stats *estats = &priv->extra_stats; - /* If the packet was truncated, none of the other errors - * matter */ + /* If the packet was truncated, none of the other errors matter */ if (status & RXBD_TRUNCATED) { stats->rx_length_errors++; @@ -2664,7 +2667,8 @@ static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb) { /* If valid headers were found, and valid sums * were verified, then we tell the kernel that no - * checksumming is necessary. Otherwise, it is */ + * checksumming is necessary. Otherwise, it is [FIXME] + */ if ((fcb->flags & RXFCB_CSUM_MASK) == (RXFCB_CIP | RXFCB_CTU)) skb->ip_summed = CHECKSUM_UNNECESSARY; else @@ -2672,8 +2676,7 @@ static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb) } -/* gfar_process_frame() -- handle one incoming packet if skb - * isn't NULL. */ +/* gfar_process_frame() -- handle one incoming packet if skb isn't NULL. */ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int amount_pull, struct napi_struct *napi) { @@ -2685,8 +2688,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, /* fcb is at the beginning if exists */ fcb = (struct rxfcb *)skb->data; - /* Remove the FCB from the skb */ - /* Remove the padded bytes, if there are any */ + /* Remove the FCB from the skb + * Remove the padded bytes, if there are any + */ if (amount_pull) { skb_record_rx_queue(skb, fcb->rq); skb_pull(skb, amount_pull); @@ -2709,8 +2713,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, /* Tell the skb what kind of packet this is */ skb->protocol = eth_type_trans(skb, dev); - /* - * There's need to check for NETIF_F_HW_VLAN_RX here. + /* There's need to check for NETIF_F_HW_VLAN_RX here. * Even if vlan rx accel is disabled, on some chips * RXFCB_VLN is pseudo randomly set. */ @@ -2831,7 +2834,8 @@ static int gfar_poll(struct napi_struct *napi, int budget) budget_per_queue = budget/num_queues; /* Clear IEVENT, so interrupts aren't called again - * because of the packets that have already arrived */ + * because of the packets that have already arrived + */ gfar_write(®s->ievent, IEVENT_RTX_MASK); while (num_queues && left_over_budget) { @@ -2869,8 +2873,9 @@ static int gfar_poll(struct napi_struct *napi, int budget) gfar_write(®s->imask, IMASK_DEFAULT); - /* If we are coalescing interrupts, update the timer */ - /* Otherwise, clear it */ + /* If we are coalescing interrupts, update the timer + * Otherwise, clear it + */ gfar_configure_coalescing(priv, gfargrp->rx_bit_map, gfargrp->tx_bit_map); } @@ -2879,8 +2884,7 @@ static int gfar_poll(struct napi_struct *napi, int budget) } #ifdef CONFIG_NET_POLL_CONTROLLER -/* - * Polling 'interrupt' - used by things like netconsole to send skbs +/* Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. */ @@ -2957,7 +2961,8 @@ static void adjust_link(struct net_device *dev) u32 ecntrl = gfar_read(®s->ecntrl); /* Now we make sure that we can be in full duplex mode. - * If not, we operate in half-duplex mode. */ + * If not, we operate in half-duplex mode. + */ if (phydev->duplex != priv->oldduplex) { new_state = 1; if (!(phydev->duplex)) @@ -2983,7 +2988,8 @@ static void adjust_link(struct net_device *dev) ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII); /* Reduced mode distinguishes - * between 10 and 100 */ + * between 10 and 100 + */ if (phydev->speed == SPEED_100) ecntrl |= ECNTRL_R100; else @@ -3022,7 +3028,8 @@ static void adjust_link(struct net_device *dev) /* Update the hash table based on the current list of multicast * addresses we subscribe to. Also, change the promiscuity of * the device based on the flags (this function is called - * whenever dev->flags is changed */ + * whenever dev->flags is changed + */ static void gfar_set_multi(struct net_device *dev) { struct netdev_hw_addr *ha; @@ -3084,7 +3091,8 @@ static void gfar_set_multi(struct net_device *dev) /* If we have extended hash tables, we need to * clear the exact match registers to prepare for - * setting them */ + * setting them + */ if (priv->extended_hash) { em_num = GFAR_EM_NUM + 1; gfar_clear_exact_match(dev); @@ -3110,7 +3118,8 @@ static void gfar_set_multi(struct net_device *dev) /* Clears each of the exact match registers to zero, so they - * don't interfere with normal reception */ + * don't interfere with normal reception + */ static void gfar_clear_exact_match(struct net_device *dev) { int idx; @@ -3132,7 +3141,8 @@ static void gfar_clear_exact_match(struct net_device *dev) * hash index which gaddr register to use, and the 5 other bits * indicate which bit (assuming an IBM numbering scheme, which * for PowerPC (tm) is usually the case) in the register holds - * the entry. */ + * the entry. + */ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr) { u32 tempval; @@ -3164,8 +3174,9 @@ static void gfar_set_mac_for_addr(struct net_device *dev, int num, macptr += num*2; - /* Now copy it into the mac registers backwards, cuz */ - /* little endian is silly */ + /* Now copy it into the mac registers backwards, cuz + * little endian is silly + */ for (idx = 0; idx < ETH_ALEN; idx++) tmpbuf[ETH_ALEN - 1 - idx] = addr[idx]; -- cgit v1.2.3 From bc4598bc076fcafa662c82b8ad3ace2d1b5fbdc1 Mon Sep 17 00:00:00 2001 From: Jan Ceuleers Date: Tue, 5 Jun 2012 03:42:13 +0000 Subject: gianfar: various coding style and whitespace cleanups Signed-off-by: Jan Ceuleers Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 298 ++++++++++++++++--------------- 1 file changed, 154 insertions(+), 144 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 314456000335..ed0b1369a5d9 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -113,7 +113,7 @@ static void gfar_timeout(struct net_device *dev); static int gfar_close(struct net_device *dev); struct sk_buff *gfar_new_skb(struct net_device *dev); static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp, - struct sk_buff *skb); + struct sk_buff *skb); static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); static irqreturn_t gfar_error(int irq, void *dev_id); @@ -265,8 +265,8 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) tx_queue->tx_bd_dma_base = addr; tx_queue->dev = ndev; /* enet DMA only understands physical addresses */ - addr += sizeof(struct txbd8) * tx_queue->tx_ring_size; - vaddr += sizeof(struct txbd8) * tx_queue->tx_ring_size; + addr += sizeof(struct txbd8) * tx_queue->tx_ring_size; + vaddr += sizeof(struct txbd8) * tx_queue->tx_ring_size; } /* Start the rx descriptor ring where the tx ring leaves off */ @@ -275,15 +275,16 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) rx_queue->rx_bd_base = vaddr; rx_queue->rx_bd_dma_base = addr; rx_queue->dev = ndev; - addr += sizeof(struct rxbd8) * rx_queue->rx_ring_size; - vaddr += sizeof(struct rxbd8) * rx_queue->rx_ring_size; + addr += sizeof(struct rxbd8) * rx_queue->rx_ring_size; + vaddr += sizeof(struct rxbd8) * rx_queue->rx_ring_size; } /* Setup the skbuff rings */ for (i = 0; i < priv->num_tx_queues; i++) { tx_queue = priv->tx_queue[i]; tx_queue->tx_skbuff = kmalloc(sizeof(*tx_queue->tx_skbuff) * - tx_queue->tx_ring_size, GFP_KERNEL); + tx_queue->tx_ring_size, + GFP_KERNEL); if (!tx_queue->tx_skbuff) { netif_err(priv, ifup, ndev, "Could not allocate tx_skbuff\n"); @@ -297,7 +298,8 @@ static int gfar_alloc_skb_resources(struct net_device *ndev) for (i = 0; i < priv->num_rx_queues; i++) { rx_queue = priv->rx_queue[i]; rx_queue->rx_skbuff = kmalloc(sizeof(*rx_queue->rx_skbuff) * - rx_queue->rx_ring_size, GFP_KERNEL); + rx_queue->rx_ring_size, + GFP_KERNEL); if (!rx_queue->rx_skbuff) { netif_err(priv, ifup, ndev, @@ -326,15 +328,15 @@ static void gfar_init_tx_rx_base(struct gfar_private *priv) int i; baddr = ®s->tbase0; - for(i = 0; i < priv->num_tx_queues; i++) { + for (i = 0; i < priv->num_tx_queues; i++) { gfar_write(baddr, priv->tx_queue[i]->tx_bd_dma_base); - baddr += 2; + baddr += 2; } baddr = ®s->rbase0; - for(i = 0; i < priv->num_rx_queues; i++) { + for (i = 0; i < priv->num_rx_queues; i++) { gfar_write(baddr, priv->rx_queue[i]->rx_bd_dma_base); - baddr += 2; + baddr += 2; } } @@ -430,12 +432,12 @@ static struct net_device_stats *gfar_get_stats(struct net_device *dev) for (i = 0; i < priv->num_rx_queues; i++) { rx_packets += priv->rx_queue[i]->stats.rx_packets; - rx_bytes += priv->rx_queue[i]->stats.rx_bytes; + rx_bytes += priv->rx_queue[i]->stats.rx_bytes; rx_dropped += priv->rx_queue[i]->stats.rx_dropped; } dev->stats.rx_packets = rx_packets; - dev->stats.rx_bytes = rx_bytes; + dev->stats.rx_bytes = rx_bytes; dev->stats.rx_dropped = rx_dropped; for (i = 0; i < priv->num_tx_queues; i++) { @@ -443,7 +445,7 @@ static struct net_device_stats *gfar_get_stats(struct net_device *dev) tx_packets += priv->tx_queue[i]->stats.tx_packets; } - dev->stats.tx_bytes = tx_bytes; + dev->stats.tx_bytes = tx_bytes; dev->stats.tx_packets = tx_packets; return &dev->stats; @@ -508,8 +510,8 @@ static bool gfar_is_vlan_on(struct gfar_private *priv) static inline int gfar_uses_fcb(struct gfar_private *priv) { return gfar_is_vlan_on(priv) || - (priv->ndev->features & NETIF_F_RXCSUM) || - (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER); + (priv->ndev->features & NETIF_F_RXCSUM) || + (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER); } static void free_tx_pointers(struct gfar_private *priv) @@ -554,7 +556,7 @@ static void enable_napi(struct gfar_private *priv) } static int gfar_parse_group(struct device_node *np, - struct gfar_private *priv, const char *model) + struct gfar_private *priv, const char *model) { u32 *queue_mask; @@ -580,15 +582,13 @@ static int gfar_parse_group(struct device_node *np, priv->gfargrp[priv->num_grps].grp_id = priv->num_grps; priv->gfargrp[priv->num_grps].priv = priv; spin_lock_init(&priv->gfargrp[priv->num_grps].grplock); - if(priv->mode == MQ_MG_MODE) { - queue_mask = (u32 *)of_get_property(np, - "fsl,rx-bit-map", NULL); - priv->gfargrp[priv->num_grps].rx_bit_map = - queue_mask ? *queue_mask :(DEFAULT_MAPPING >> priv->num_grps); - queue_mask = (u32 *)of_get_property(np, - "fsl,tx-bit-map", NULL); - priv->gfargrp[priv->num_grps].tx_bit_map = - queue_mask ? *queue_mask : (DEFAULT_MAPPING >> priv->num_grps); + if (priv->mode == MQ_MG_MODE) { + queue_mask = (u32 *)of_get_property(np, "fsl,rx-bit-map", NULL); + priv->gfargrp[priv->num_grps].rx_bit_map = queue_mask ? + *queue_mask : (DEFAULT_MAPPING >> priv->num_grps); + queue_mask = (u32 *)of_get_property(np, "fsl,tx-bit-map", NULL); + priv->gfargrp[priv->num_grps].tx_bit_map = queue_mask ? + *queue_mask : (DEFAULT_MAPPING >> priv->num_grps); } else { priv->gfargrp[priv->num_grps].rx_bit_map = 0xFF; priv->gfargrp[priv->num_grps].tx_bit_map = 0xFF; @@ -673,7 +673,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) } else { priv->mode = SQ_SG_MODE; err = gfar_parse_group(np, priv, model); - if(err) + if (err) goto err_grp_init; } @@ -730,27 +730,27 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) priv->device_flags |= FSL_GIANFAR_DEV_HAS_BUF_STASHING; mac_addr = of_get_mac_address(np); + if (mac_addr) memcpy(dev->dev_addr, mac_addr, ETH_ALEN); if (model && !strcasecmp(model, "TSEC")) - priv->device_flags = - FSL_GIANFAR_DEV_HAS_GIGABIT | - FSL_GIANFAR_DEV_HAS_COALESCE | - FSL_GIANFAR_DEV_HAS_RMON | - FSL_GIANFAR_DEV_HAS_MULTI_INTR; + priv->device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | + FSL_GIANFAR_DEV_HAS_COALESCE | + FSL_GIANFAR_DEV_HAS_RMON | + FSL_GIANFAR_DEV_HAS_MULTI_INTR; + if (model && !strcasecmp(model, "eTSEC")) - priv->device_flags = - FSL_GIANFAR_DEV_HAS_GIGABIT | - FSL_GIANFAR_DEV_HAS_COALESCE | - FSL_GIANFAR_DEV_HAS_RMON | - FSL_GIANFAR_DEV_HAS_MULTI_INTR | - FSL_GIANFAR_DEV_HAS_PADDING | - FSL_GIANFAR_DEV_HAS_CSUM | - FSL_GIANFAR_DEV_HAS_VLAN | - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET | - FSL_GIANFAR_DEV_HAS_EXTENDED_HASH | - FSL_GIANFAR_DEV_HAS_TIMER; + priv->device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | + FSL_GIANFAR_DEV_HAS_COALESCE | + FSL_GIANFAR_DEV_HAS_RMON | + FSL_GIANFAR_DEV_HAS_MULTI_INTR | + FSL_GIANFAR_DEV_HAS_PADDING | + FSL_GIANFAR_DEV_HAS_CSUM | + FSL_GIANFAR_DEV_HAS_VLAN | + FSL_GIANFAR_DEV_HAS_MAGIC_PACKET | + FSL_GIANFAR_DEV_HAS_EXTENDED_HASH | + FSL_GIANFAR_DEV_HAS_TIMER; ctype = of_get_property(np, "phy-connection-type", NULL); @@ -781,7 +781,7 @@ err_grp_init: } static int gfar_hwtstamp_ioctl(struct net_device *netdev, - struct ifreq *ifr, int cmd) + struct ifreq *ifr, int cmd) { struct hwtstamp_config config; struct gfar_private *priv = netdev_priv(netdev); @@ -851,6 +851,7 @@ static unsigned int reverse_bitmap(unsigned int bit_map, unsigned int max_qs) { unsigned int new_bit_map = 0x0; int mask = 0x1 << (max_qs - 1), i; + for (i = 0; i < max_qs; i++) { if (bit_map & mask) new_bit_map = new_bit_map + (1 << i); @@ -936,22 +937,22 @@ static void gfar_detect_errata(struct gfar_private *priv) /* MPC8313 Rev 2.0 and higher; All MPC837x */ if ((pvr == 0x80850010 && mod == 0x80b0 && rev >= 0x0020) || - (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) + (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) priv->errata |= GFAR_ERRATA_74; /* MPC8313 and MPC837x all rev */ if ((pvr == 0x80850010 && mod == 0x80b0) || - (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) + (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) priv->errata |= GFAR_ERRATA_76; /* MPC8313 and MPC837x all rev */ if ((pvr == 0x80850010 && mod == 0x80b0) || - (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) + (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0)) priv->errata |= GFAR_ERRATA_A002; /* MPC8313 Rev < 2.0, MPC8548 rev 2.0 */ if ((pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020) || - (pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020)) + (pvr == 0x80210020 && mod == 0x8030 && rev == 0x0020)) priv->errata |= GFAR_ERRATA_12; if (priv->errata) @@ -1028,13 +1029,14 @@ static int gfar_probe(struct platform_device *ofdev) /* Register for napi ...We are registering NAPI for each grp */ for (i = 0; i < priv->num_grps; i++) - netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, GFAR_DEV_WEIGHT); + netif_napi_add(dev, &priv->gfargrp[i].napi, gfar_poll, + GFAR_DEV_WEIGHT); if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) { dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | - NETIF_F_RXCSUM; + NETIF_F_RXCSUM; dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG | - NETIF_F_RXCSUM | NETIF_F_HIGHDMA; + NETIF_F_RXCSUM | NETIF_F_HIGHDMA; } if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) { @@ -1083,7 +1085,7 @@ static int gfar_probe(struct platform_device *ofdev) priv->padding = 0; if (dev->features & NETIF_F_IP_CSUM || - priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) + priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) dev->needed_headroom = GMAC_FCB_LEN; /* Program the isrg regs only if number of grps > 1 */ @@ -1103,10 +1105,10 @@ static int gfar_probe(struct platform_device *ofdev) * basically reverses the queue numbers */ for (i = 0; i< priv->num_grps; i++) { - priv->gfargrp[i].tx_bit_map = reverse_bitmap( - priv->gfargrp[i].tx_bit_map, MAX_TX_QS); - priv->gfargrp[i].rx_bit_map = reverse_bitmap( - priv->gfargrp[i].rx_bit_map, MAX_RX_QS); + priv->gfargrp[i].tx_bit_map = + reverse_bitmap(priv->gfargrp[i].tx_bit_map, MAX_TX_QS); + priv->gfargrp[i].rx_bit_map = + reverse_bitmap(priv->gfargrp[i].rx_bit_map, MAX_RX_QS); } /* Calculate RSTAT, TSTAT, RQUEUE and TQUEUE values, @@ -1114,16 +1116,18 @@ static int gfar_probe(struct platform_device *ofdev) */ for (grp_idx = 0; grp_idx < priv->num_grps; grp_idx++) { priv->gfargrp[grp_idx].num_rx_queues = 0x0; + for_each_set_bit(i, &priv->gfargrp[grp_idx].rx_bit_map, - priv->num_rx_queues) { + priv->num_rx_queues) { priv->gfargrp[grp_idx].num_rx_queues++; priv->rx_queue[i]->grp = &priv->gfargrp[grp_idx]; rstat = rstat | (RSTAT_CLEAR_RHALT >> i); rqueue = rqueue | ((RQUEUE_EN0 | RQUEUE_EX0) >> i); } priv->gfargrp[grp_idx].num_tx_queues = 0x0; + for_each_set_bit(i, &priv->gfargrp[grp_idx].tx_bit_map, - priv->num_tx_queues) { + priv->num_tx_queues) { priv->gfargrp[grp_idx].num_tx_queues++; priv->tx_queue[i]->grp = &priv->gfargrp[grp_idx]; tstat = tstat | (TSTAT_CLEAR_THALT >> i); @@ -1169,7 +1173,8 @@ static int gfar_probe(struct platform_device *ofdev) } device_init_wakeup(&dev->dev, - priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + priv->device_flags & + FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); /* fill out IRQ number and name fields */ for (i = 0; i < priv->num_grps; i++) { @@ -1200,7 +1205,7 @@ static int gfar_probe(struct platform_device *ofdev) for (i = 0; i < priv->num_rx_queues; i++) netdev_info(dev, "RX BD ring size for Q[%d]: %d\n", i, priv->rx_queue[i]->rx_ring_size); - for(i = 0; i < priv->num_tx_queues; i++) + for (i = 0; i < priv->num_tx_queues; i++) netdev_info(dev, "TX BD ring size for Q[%d]: %d\n", i, priv->tx_queue[i]->tx_ring_size); @@ -1247,7 +1252,8 @@ static int gfar_suspend(struct device *dev) u32 tempval; int magic_packet = priv->wol_en && - (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + (priv->device_flags & + FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); netif_device_detach(ndev); @@ -1299,7 +1305,8 @@ static int gfar_resume(struct device *dev) unsigned long flags; u32 tempval; int magic_packet = priv->wol_en && - (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + (priv->device_flags & + FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); if (!netif_running(ndev)) { netif_device_attach(ndev); @@ -1398,8 +1405,9 @@ static phy_interface_t gfar_get_interface(struct net_device *dev) } if (ecntrl & ECNTRL_REDUCED_MODE) { - if (ecntrl & ECNTRL_REDUCED_MII_MODE) + if (ecntrl & ECNTRL_REDUCED_MII_MODE) { return PHY_INTERFACE_MODE_RMII; + } else { phy_interface_t interface = priv->interface; @@ -1494,11 +1502,12 @@ static void gfar_configure_serdes(struct net_device *dev) phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT); phy_write(tbiphy, MII_ADVERTISE, - ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | - ADVERTISE_1000XPSE_ASYM); + ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE | + ADVERTISE_1000XPSE_ASYM); - phy_write(tbiphy, MII_BMCR, BMCR_ANENABLE | - BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000); + phy_write(tbiphy, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | + BMCR_SPEED1000); } static void init_registers(struct net_device *dev) @@ -1594,8 +1603,8 @@ static void gfar_halt_nodisable(struct net_device *dev) regs = priv->gfargrp[0].regs; /* Stop the DMA, and wait for it to stop */ tempval = gfar_read(®s->dmactrl); - if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) - != (DMACTRL_GRS | DMACTRL_GTS)) { + if ((tempval & (DMACTRL_GRS | DMACTRL_GTS)) != + (DMACTRL_GRS | DMACTRL_GTS)) { int ret; tempval |= (DMACTRL_GRS | DMACTRL_GTS); @@ -1660,7 +1669,7 @@ void stop_gfar(struct net_device *dev) } else { for (i = 0; i < priv->num_grps; i++) free_irq(priv->gfargrp[i].interruptTransmit, - &priv->gfargrp[i]); + &priv->gfargrp[i]); } free_skb_resources(priv); @@ -1679,13 +1688,13 @@ static void free_skb_tx_queue(struct gfar_priv_tx_q *tx_queue) continue; dma_unmap_single(&priv->ofdev->dev, txbdp->bufPtr, - txbdp->length, DMA_TO_DEVICE); + txbdp->length, DMA_TO_DEVICE); txbdp->lstatus = 0; for (j = 0; j < skb_shinfo(tx_queue->tx_skbuff[i])->nr_frags; - j++) { + j++) { txbdp++; dma_unmap_page(&priv->ofdev->dev, txbdp->bufPtr, - txbdp->length, DMA_TO_DEVICE); + txbdp->length, DMA_TO_DEVICE); } txbdp++; dev_kfree_skb_any(tx_queue->tx_skbuff[i]); @@ -1705,8 +1714,8 @@ static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue) for (i = 0; i < rx_queue->rx_ring_size; i++) { if (rx_queue->rx_skbuff[i]) { dma_unmap_single(&priv->ofdev->dev, - rxbdp->bufPtr, priv->rx_buffer_size, - DMA_FROM_DEVICE); + rxbdp->bufPtr, priv->rx_buffer_size, + DMA_FROM_DEVICE); dev_kfree_skb_any(rx_queue->rx_skbuff[i]); rx_queue->rx_skbuff[i] = NULL; } @@ -1729,24 +1738,25 @@ static void free_skb_resources(struct gfar_private *priv) /* Go through all the buffer descriptors and free their data buffers */ for (i = 0; i < priv->num_tx_queues; i++) { struct netdev_queue *txq; + tx_queue = priv->tx_queue[i]; txq = netdev_get_tx_queue(tx_queue->dev, tx_queue->qindex); - if(tx_queue->tx_skbuff) + if (tx_queue->tx_skbuff) free_skb_tx_queue(tx_queue); netdev_tx_reset_queue(txq); } for (i = 0; i < priv->num_rx_queues; i++) { rx_queue = priv->rx_queue[i]; - if(rx_queue->rx_skbuff) + if (rx_queue->rx_skbuff) free_skb_rx_queue(rx_queue); } dma_free_coherent(&priv->ofdev->dev, - sizeof(struct txbd8) * priv->total_tx_ring_size + - sizeof(struct rxbd8) * priv->total_rx_ring_size, - priv->tx_queue[0]->tx_bd_base, - priv->tx_queue[0]->tx_bd_dma_base); + sizeof(struct txbd8) * priv->total_tx_ring_size + + sizeof(struct rxbd8) * priv->total_rx_ring_size, + priv->tx_queue[0]->tx_bd_base, + priv->tx_queue[0]->tx_bd_dma_base); skb_queue_purge(&priv->rx_recycle); } @@ -1785,7 +1795,7 @@ void gfar_start(struct net_device *dev) } void gfar_configure_coalescing(struct gfar_private *priv, - unsigned long tx_mask, unsigned long rx_mask) + unsigned long tx_mask, unsigned long rx_mask) { struct gfar __iomem *regs = priv->gfargrp[0].regs; u32 __iomem *baddr; @@ -1795,11 +1805,11 @@ void gfar_configure_coalescing(struct gfar_private *priv, * multiple queues, there's only single reg to program */ gfar_write(®s->txic, 0); - if(likely(priv->tx_queue[0]->txcoalescing)) + if (likely(priv->tx_queue[0]->txcoalescing)) gfar_write(®s->txic, priv->tx_queue[0]->txic); gfar_write(®s->rxic, 0); - if(unlikely(priv->rx_queue[0]->rxcoalescing)) + if (unlikely(priv->rx_queue[0]->rxcoalescing)) gfar_write(®s->rxic, priv->rx_queue[0]->rxic); if (priv->mode == MQ_MG_MODE) { @@ -1834,8 +1844,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) /* Install our interrupt handlers for Error, * Transmit, and Receive */ - if ((err = request_irq(grp->interruptError, gfar_error, 0, - grp->int_name_er,grp)) < 0) { + if ((err = request_irq(grp->interruptError, gfar_error, + 0, grp->int_name_er, grp)) < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", grp->interruptError); @@ -1843,21 +1853,21 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) } if ((err = request_irq(grp->interruptTransmit, gfar_transmit, - 0, grp->int_name_tx, grp)) < 0) { + 0, grp->int_name_tx, grp)) < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", grp->interruptTransmit); goto tx_irq_fail; } - if ((err = request_irq(grp->interruptReceive, gfar_receive, 0, - grp->int_name_rx, grp)) < 0) { + if ((err = request_irq(grp->interruptReceive, gfar_receive, + 0, grp->int_name_rx, grp)) < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", grp->interruptReceive); goto rx_irq_fail; } } else { - if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, 0, - grp->int_name_tx, grp)) < 0) { + if ((err = request_irq(grp->interruptTransmit, gfar_interrupt, + 0, grp->int_name_tx, grp)) < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", grp->interruptTransmit); goto err_irq_fail; @@ -1964,7 +1974,7 @@ static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb) } static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb, - int fcb_length) + int fcb_length) { u8 flags = 0; @@ -2001,7 +2011,7 @@ void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) } static inline struct txbd8 *skip_txbd(struct txbd8 *bdp, int stride, - struct txbd8 *base, int ring_size) + struct txbd8 *base, int ring_size) { struct txbd8 *new_bd = bdp + stride; @@ -2009,7 +2019,7 @@ static inline struct txbd8 *skip_txbd(struct txbd8 *bdp, int stride, } static inline struct txbd8 *next_txbd(struct txbd8 *bdp, struct txbd8 *base, - int ring_size) + int ring_size) { return skip_txbd(bdp, 1, base, ring_size); } @@ -2035,8 +2045,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) * before start of transmission. */ if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_76) && - skb->ip_summed == CHECKSUM_PARTIAL && - skb->len > 2500)) { + skb->ip_summed == CHECKSUM_PARTIAL && + skb->len > 2500)) { int ret; ret = skb_checksum_help(skb); @@ -2052,16 +2062,16 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* check if time stamp should be generated */ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP && - priv->hwts_tx_en)) { + priv->hwts_tx_en)) { do_tstamp = 1; fcb_length = GMAC_FCB_LEN + GMAC_TXPAL_LEN; } /* make space for additional header when fcb is needed */ if (((skb->ip_summed == CHECKSUM_PARTIAL) || - vlan_tx_tag_present(skb) || - unlikely(do_tstamp)) && - (skb_headroom(skb) < fcb_length)) { + vlan_tx_tag_present(skb) || + unlikely(do_tstamp)) && + (skb_headroom(skb) < fcb_length)) { struct sk_buff *skb_new; skb_new = skb_realloc_headroom(skb, fcb_length); @@ -2105,12 +2115,12 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Time stamp insertion requires one additional TxBD */ if (unlikely(do_tstamp)) txbdp_tstamp = txbdp = next_txbd(txbdp, base, - tx_queue->tx_ring_size); + tx_queue->tx_ring_size); if (nr_frags == 0) { if (unlikely(do_tstamp)) txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_LAST | - TXBD_INTERRUPT); + TXBD_INTERRUPT); else lstatus |= BD_LFLAG(TXBD_LAST | TXBD_INTERRUPT); } else { @@ -2122,7 +2132,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) length = skb_shinfo(skb)->frags[i].size; lstatus = txbdp->lstatus | length | - BD_LFLAG(TXBD_READY); + BD_LFLAG(TXBD_READY); /* Handle the last BD specially */ if (i == nr_frags - 1) @@ -2152,8 +2162,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) if (CHECKSUM_PARTIAL == skb->ip_summed) { fcb = gfar_add_fcb(skb); /* as specified by errata */ - if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_12) - && ((unsigned long)fcb % 0x20) > 0x18)) { + if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_12) && + ((unsigned long)fcb % 0x20) > 0x18)) { __skb_pull(skb, GMAC_FCB_LEN); skb_checksum_help(skb); } else { @@ -2181,7 +2191,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) } txbdp_start->bufPtr = dma_map_single(&priv->ofdev->dev, skb->data, - skb_headlen(skb), DMA_TO_DEVICE); + skb_headlen(skb), DMA_TO_DEVICE); /* If time stamping is requested one additional TxBD must be set up. The * first TxBD points to the FCB and must have a data length of @@ -2191,7 +2201,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(do_tstamp)) { txbdp_tstamp->bufPtr = txbdp_start->bufPtr + fcb_length; txbdp_tstamp->lstatus |= BD_LFLAG(TXBD_READY) | - (skb_headlen(skb) - fcb_length); + (skb_headlen(skb) - fcb_length); lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | GMAC_FCB_LEN; } else { lstatus |= BD_LFLAG(TXBD_CRC | TXBD_READY) | skb_headlen(skb); @@ -2231,7 +2241,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) * (wrapping if necessary) */ tx_queue->skb_curtx = (tx_queue->skb_curtx + 1) & - TX_RING_MOD_MASK(tx_queue->tx_ring_size); + TX_RING_MOD_MASK(tx_queue->tx_ring_size); tx_queue->cur_tx = next_txbd(txbdp, base, tx_queue->tx_ring_size); @@ -2365,9 +2375,8 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) frame_size += priv->padding; - tempsize = - (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) + - INCREMENTAL_BUFFER_SIZE; + tempsize = (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) + + INCREMENTAL_BUFFER_SIZE; /* Only stop and start the controller if it isn't already * stopped, and we changed something @@ -2389,7 +2398,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) tempval = gfar_read(®s->maccfg2); if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE || - gfar_has_errata(priv, GFAR_ERRATA_74)) + gfar_has_errata(priv, GFAR_ERRATA_74)) tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK); else tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK); @@ -2410,7 +2419,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) static void gfar_reset_task(struct work_struct *work) { struct gfar_private *priv = container_of(work, struct gfar_private, - reset_task); + reset_task); struct net_device *dev = priv->ndev; if (dev->flags & IFF_UP) { @@ -2437,7 +2446,7 @@ static void gfar_align_skb(struct sk_buff *skb) * as many bytes as needed to align the data properly */ skb_reserve(skb, RXBUF_ALIGNMENT - - (((unsigned long) skb->data) & (RXBUF_ALIGNMENT - 1))); + (((unsigned long) skb->data) & (RXBUF_ALIGNMENT - 1))); } /* Interrupt Handler for Transmit complete */ @@ -2485,7 +2494,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) /* Only clean completed frames */ if ((lstatus & BD_LFLAG(TXBD_READY)) && - (lstatus & BD_LENGTH_MASK)) + (lstatus & BD_LENGTH_MASK)) break; if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { @@ -2495,11 +2504,12 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) buflen = bdp->length; dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr, - buflen, DMA_TO_DEVICE); + buflen, DMA_TO_DEVICE); if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) { struct skb_shared_hwtstamps shhwtstamps; u64 *ns = (u64*) (((u32)skb->data + 0x10) & ~0x7); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); shhwtstamps.hwtstamp = ns_to_ktime(*ns); skb_pull(skb, GMAC_FCB_LEN + GMAC_TXPAL_LEN); @@ -2512,10 +2522,8 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) bdp = next_txbd(bdp, base, tx_ring_size); for (i = 0; i < frags; i++) { - dma_unmap_page(&priv->ofdev->dev, - bdp->bufPtr, - bdp->length, - DMA_TO_DEVICE); + dma_unmap_page(&priv->ofdev->dev, bdp->bufPtr, + bdp->length, DMA_TO_DEVICE); bdp->lstatus &= BD_LFLAG(TXBD_WRAP); bdp = next_txbd(bdp, base, tx_ring_size); } @@ -2526,8 +2534,8 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) * we add this skb back into the pool, if it's the right size */ if (skb_queue_len(&priv->rx_recycle) < rx_queue->rx_ring_size && - skb_recycle_check(skb, priv->rx_buffer_size + - RXBUF_ALIGNMENT)) { + skb_recycle_check(skb, priv->rx_buffer_size + + RXBUF_ALIGNMENT)) { gfar_align_skb(skb); skb_queue_head(&priv->rx_recycle, skb); } else @@ -2536,7 +2544,7 @@ static int gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue) tx_queue->tx_skbuff[skb_dirtytx] = NULL; skb_dirtytx = (skb_dirtytx + 1) & - TX_RING_MOD_MASK(tx_ring_size); + TX_RING_MOD_MASK(tx_ring_size); howmany++; spin_lock_irqsave(&tx_queue->txlock, flags); @@ -2583,7 +2591,7 @@ static irqreturn_t gfar_transmit(int irq, void *grp_id) } static void gfar_new_rxbdp(struct gfar_priv_rx_q *rx_queue, struct rxbd8 *bdp, - struct sk_buff *skb) + struct sk_buff *skb) { struct net_device *dev = rx_queue->dev; struct gfar_private *priv = netdev_priv(dev); @@ -2700,6 +2708,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, if (priv->hwts_rx_en) { struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); u64 *ns = (u64 *) skb->data; + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); shhwtstamps->hwtstamp = ns_to_ktime(*ns); } @@ -2752,6 +2761,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) { struct sk_buff *newskb; + rmb(); /* Add another skb for the future */ @@ -2760,15 +2770,15 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) skb = rx_queue->rx_skbuff[rx_queue->skb_currx]; dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr, - priv->rx_buffer_size, DMA_FROM_DEVICE); + priv->rx_buffer_size, DMA_FROM_DEVICE); if (unlikely(!(bdp->status & RXBD_ERR) && - bdp->length > priv->rx_buffer_size)) + bdp->length > priv->rx_buffer_size)) bdp->status = RXBD_LARGE; /* We drop the frame if we failed to allocate a new buffer */ if (unlikely(!newskb || !(bdp->status & RXBD_LAST) || - bdp->status & RXBD_ERR)) { + bdp->status & RXBD_ERR)) { count_errors(bdp->status, dev); if (unlikely(!newskb)) @@ -2787,7 +2797,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) rx_queue->stats.rx_bytes += pkt_len; skb_record_rx_queue(skb, rx_queue->qindex); gfar_process_frame(dev, skb, amount_pull, - &rx_queue->grp->napi); + &rx_queue->grp->napi); } else { netif_warn(priv, rx_err, dev, "Missing skb!\n"); @@ -2806,9 +2816,8 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) bdp = next_bd(bdp, base, rx_queue->rx_ring_size); /* update to point at the next skb */ - rx_queue->skb_currx = - (rx_queue->skb_currx + 1) & - RX_RING_MOD_MASK(rx_queue->rx_ring_size); + rx_queue->skb_currx = (rx_queue->skb_currx + 1) & + RX_RING_MOD_MASK(rx_queue->rx_ring_size); } /* Update the current rxbd pointer to be the next one */ @@ -2819,8 +2828,8 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit) static int gfar_poll(struct napi_struct *napi, int budget) { - struct gfar_priv_grp *gfargrp = container_of(napi, - struct gfar_priv_grp, napi); + struct gfar_priv_grp *gfargrp = + container_of(napi, struct gfar_priv_grp, napi); struct gfar_private *priv = gfargrp->priv; struct gfar __iomem *regs = gfargrp->regs; struct gfar_priv_tx_q *tx_queue = NULL; @@ -2839,7 +2848,6 @@ static int gfar_poll(struct napi_struct *napi, int budget) gfar_write(®s->ievent, IEVENT_RTX_MASK); while (num_queues && left_over_budget) { - budget_per_queue = left_over_budget/num_queues; left_over_budget = 0; @@ -2850,12 +2858,13 @@ static int gfar_poll(struct napi_struct *napi, int budget) tx_queue = priv->tx_queue[rx_queue->qindex]; tx_cleaned += gfar_clean_tx_ring(tx_queue); - rx_cleaned_per_queue = gfar_clean_rx_ring(rx_queue, - budget_per_queue); + rx_cleaned_per_queue = + gfar_clean_rx_ring(rx_queue, budget_per_queue); rx_cleaned += rx_cleaned_per_queue; - if(rx_cleaned_per_queue < budget_per_queue) { + if (rx_cleaned_per_queue < budget_per_queue) { left_over_budget = left_over_budget + - (budget_per_queue - rx_cleaned_per_queue); + (budget_per_queue - + rx_cleaned_per_queue); set_bit(i, &serviced_queues); num_queues--; } @@ -2876,8 +2885,8 @@ static int gfar_poll(struct napi_struct *napi, int budget) /* If we are coalescing interrupts, update the timer * Otherwise, clear it */ - gfar_configure_coalescing(priv, - gfargrp->rx_bit_map, gfargrp->tx_bit_map); + gfar_configure_coalescing(priv, gfargrp->rx_bit_map, + gfargrp->tx_bit_map); } return rx_cleaned; @@ -2900,7 +2909,7 @@ static void gfar_netpoll(struct net_device *dev) disable_irq(priv->gfargrp[i].interruptReceive); disable_irq(priv->gfargrp[i].interruptError); gfar_interrupt(priv->gfargrp[i].interruptTransmit, - &priv->gfargrp[i]); + &priv->gfargrp[i]); enable_irq(priv->gfargrp[i].interruptError); enable_irq(priv->gfargrp[i].interruptReceive); enable_irq(priv->gfargrp[i].interruptTransmit); @@ -2909,7 +2918,7 @@ static void gfar_netpoll(struct net_device *dev) for (i = 0; i < priv->num_grps; i++) { disable_irq(priv->gfargrp[i].interruptTransmit); gfar_interrupt(priv->gfargrp[i].interruptTransmit, - &priv->gfargrp[i]); + &priv->gfargrp[i]); enable_irq(priv->gfargrp[i].interruptTransmit); } } @@ -3125,7 +3134,7 @@ static void gfar_clear_exact_match(struct net_device *dev) int idx; static const u8 zero_arr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; - for(idx = 1;idx < GFAR_EM_NUM + 1;idx++) + for (idx = 1; idx < GFAR_EM_NUM + 1; idx++) gfar_set_mac_for_addr(dev, idx, zero_arr); } @@ -3208,7 +3217,8 @@ static irqreturn_t gfar_error(int irq, void *grp_id) /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) - netdev_dbg(dev, "error interrupt (ievent=0x%08x imask=0x%08x)\n", + netdev_dbg(dev, + "error interrupt (ievent=0x%08x imask=0x%08x)\n", events, gfar_read(®s->imask)); /* Update the error counters */ -- cgit v1.2.3 From 3a2e16c816017d952ed5f29ca9d6f5136ced5f21 Mon Sep 17 00:00:00 2001 From: Jan Ceuleers Date: Tue, 5 Jun 2012 03:42:14 +0000 Subject: gianfar: Remove superfluous initialisations Signed-off-by: Jan Ceuleers Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index ed0b1369a5d9..f00a095f37b4 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -428,7 +428,7 @@ static struct net_device_stats *gfar_get_stats(struct net_device *dev) struct gfar_private *priv = netdev_priv(dev); unsigned long rx_packets = 0, rx_bytes = 0, rx_dropped = 0; unsigned long tx_packets = 0, tx_bytes = 0; - int i = 0; + int i; for (i = 0; i < priv->num_rx_queues; i++) { rx_packets += priv->rx_queue[i]->stats.rx_packets; @@ -470,7 +470,7 @@ static const struct net_device_ops gfar_netdev_ops = { void lock_rx_qs(struct gfar_private *priv) { - int i = 0x0; + int i; for (i = 0; i < priv->num_rx_queues; i++) spin_lock(&priv->rx_queue[i]->rxlock); @@ -478,7 +478,7 @@ void lock_rx_qs(struct gfar_private *priv) void lock_tx_qs(struct gfar_private *priv) { - int i = 0x0; + int i; for (i = 0; i < priv->num_tx_queues; i++) spin_lock(&priv->tx_queue[i]->txlock); @@ -486,7 +486,7 @@ void lock_tx_qs(struct gfar_private *priv) void unlock_rx_qs(struct gfar_private *priv) { - int i = 0x0; + int i; for (i = 0; i < priv->num_rx_queues; i++) spin_unlock(&priv->rx_queue[i]->rxlock); @@ -494,7 +494,7 @@ void unlock_rx_qs(struct gfar_private *priv) void unlock_tx_qs(struct gfar_private *priv) { - int i = 0x0; + int i; for (i = 0; i < priv->num_tx_queues; i++) spin_unlock(&priv->tx_queue[i]->txlock); @@ -516,7 +516,7 @@ static inline int gfar_uses_fcb(struct gfar_private *priv) static void free_tx_pointers(struct gfar_private *priv) { - int i = 0; + int i; for (i = 0; i < priv->num_tx_queues; i++) kfree(priv->tx_queue[i]); @@ -524,7 +524,7 @@ static void free_tx_pointers(struct gfar_private *priv) static void free_rx_pointers(struct gfar_private *priv) { - int i = 0; + int i; for (i = 0; i < priv->num_rx_queues; i++) kfree(priv->rx_queue[i]); @@ -532,7 +532,7 @@ static void free_rx_pointers(struct gfar_private *priv) static void unmap_group_regs(struct gfar_private *priv) { - int i = 0; + int i; for (i = 0; i < MAXGROUPS; i++) if (priv->gfargrp[i].regs) @@ -541,7 +541,7 @@ static void unmap_group_regs(struct gfar_private *priv) static void disable_napi(struct gfar_private *priv) { - int i = 0; + int i; for (i = 0; i < priv->num_grps; i++) napi_disable(&priv->gfargrp[i].napi); @@ -549,7 +549,7 @@ static void disable_napi(struct gfar_private *priv) static void enable_napi(struct gfar_private *priv) { - int i = 0; + int i; for (i = 0; i < priv->num_grps; i++) napi_enable(&priv->gfargrp[i].napi); @@ -1514,7 +1514,7 @@ static void init_registers(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = NULL; - int i = 0; + int i; for (i = 0; i < priv->num_grps; i++) { regs = priv->gfargrp[i].regs; @@ -1589,7 +1589,7 @@ static void gfar_halt_nodisable(struct net_device *dev) struct gfar_private *priv = netdev_priv(dev); struct gfar __iomem *regs = NULL; u32 tempval; - int i = 0; + int i; for (i = 0; i < priv->num_grps; i++) { regs = priv->gfargrp[i].regs; @@ -1976,13 +1976,11 @@ static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb) static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb, int fcb_length) { - u8 flags = 0; - /* If we're here, it's a IP packet with a TCP or UDP * payload. We set it to checksum, using a pseudo-header * we provide */ - flags = TXFCB_DEFAULT; + u8 flags = TXFCB_DEFAULT; /* Tell the controller what the protocol is * And provide the already calculated phcs @@ -2900,7 +2898,7 @@ static int gfar_poll(struct napi_struct *napi, int budget) static void gfar_netpoll(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); - int i = 0; + int i; /* If the device has multiple interrupts, run tx/rx */ if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { -- cgit v1.2.3 From cbfc60717b69fb479861f336d42bde640f650ea0 Mon Sep 17 00:00:00 2001 From: Jan Ceuleers Date: Tue, 5 Jun 2012 03:42:15 +0000 Subject: gianfar_ethtool: coding style and whitespace cleanups Signed-off-by: Jan Ceuleers Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar_ethtool.c | 420 ++++++++++++----------- 1 file changed, 224 insertions(+), 196 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 8a025570d97e..8971921cc1c8 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -46,18 +46,24 @@ #include "gianfar.h" extern void gfar_start(struct net_device *dev); -extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit); +extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, + int rx_work_limit); #define GFAR_MAX_COAL_USECS 0xffff #define GFAR_MAX_COAL_FRAMES 0xff static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, - u64 * buf); + u64 *buf); static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf); -static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); -static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals); -static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals); -static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals); -static void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo); +static int gfar_gcoalesce(struct net_device *dev, + struct ethtool_coalesce *cvals); +static int gfar_scoalesce(struct net_device *dev, + struct ethtool_coalesce *cvals); +static void gfar_gringparam(struct net_device *dev, + struct ethtool_ringparam *rvals); +static int gfar_sringparam(struct net_device *dev, + struct ethtool_ringparam *rvals); +static void gfar_gdrvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo); static const char stat_gstrings[][ETH_GSTRING_LEN] = { "rx-dropped-by-kernel", @@ -130,14 +136,15 @@ static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN); else memcpy(buf, stat_gstrings, - GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); + GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN); } /* Fill in an array of 64-bit statistics from various sources. * This array will be appended to the end of the ethtool_stats * structure, and returned to user space */ -static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf) +static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, + u64 *buf) { int i; struct gfar_private *priv = netdev_priv(dev); @@ -174,8 +181,8 @@ static int gfar_sset_count(struct net_device *dev, int sset) } /* Fills in the drvinfo structure with some basic info */ -static void gfar_gdrvinfo(struct net_device *dev, struct - ethtool_drvinfo *drvinfo) +static void gfar_gdrvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) { strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN); strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN); @@ -226,7 +233,8 @@ static int gfar_reglen(struct net_device *dev) } /* Return a dump of the GFAR register space */ -static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) +static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *regbuf) { int i; struct gfar_private *priv = netdev_priv(dev); @@ -239,7 +247,8 @@ static void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, voi /* Convert microseconds to ethernet clock ticks, which changes * depending on what speed the controller is running at */ -static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs) +static unsigned int gfar_usecs2ticks(struct gfar_private *priv, + unsigned int usecs) { unsigned int count; @@ -263,7 +272,8 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int use } /* Convert ethernet clock ticks to microseconds */ -static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks) +static unsigned int gfar_ticks2usecs(struct gfar_private *priv, + unsigned int ticks) { unsigned int count; @@ -288,7 +298,8 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic /* Get the coalescing parameters, and put them in the cvals * structure. */ -static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) +static int gfar_gcoalesce(struct net_device *dev, + struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); struct gfar_priv_rx_q *rx_queue = NULL; @@ -353,7 +364,8 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals * Both cvals->*_usecs and cvals->*_frames have to be > 0 * in order for coalescing to be active */ -static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) +static int gfar_scoalesce(struct net_device *dev, + struct ethtool_coalesce *cvals) { struct gfar_private *priv = netdev_priv(dev); int i = 0; @@ -364,7 +376,8 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals /* Set up rx coalescing */ /* As of now, we will enable/disable coalescing for all * queues together in case of eTSEC2, this will be modified - * along with the ethtool interface */ + * along with the ethtool interface + */ if ((cvals->rx_coalesce_usecs == 0) || (cvals->rx_max_coalesced_frames == 0)) { for (i = 0; i < priv->num_rx_queues; i++) @@ -433,7 +446,8 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals /* Fills in rvals with the current ring parameters. Currently, * rx, rx_mini, and rx_jumbo rings are the same size, as mini and * jumbo are ignored by the driver */ -static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) +static void gfar_gringparam(struct net_device *dev, + struct ethtool_ringparam *rvals) { struct gfar_private *priv = netdev_priv(dev); struct gfar_priv_tx_q *tx_queue = NULL; @@ -459,8 +473,10 @@ static void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rv /* Change the current ring parameters, stopping the controller if * necessary so that we don't mess things up while we're in * motion. We wait for the ring to be clean before reallocating - * the rings. */ -static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) + * the rings. + */ +static int gfar_sringparam(struct net_device *dev, + struct ethtool_ringparam *rvals) { struct gfar_private *priv = netdev_priv(dev); int err = 0, i = 0; @@ -486,7 +502,8 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva unsigned long flags; /* Halt TX and RX, and process the frames which - * have already been received */ + * have already been received + */ local_irq_save(flags); lock_tx_qs(priv); lock_rx_qs(priv); @@ -499,7 +516,7 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva for (i = 0; i < priv->num_rx_queues; i++) gfar_clean_rx_ring(priv->rx_queue[i], - priv->rx_queue[i]->rx_ring_size); + priv->rx_queue[i]->rx_ring_size); /* Now we take down the rings to rebuild them */ stop_gfar(dev); @@ -509,7 +526,8 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva for (i = 0; i < priv->num_rx_queues; i++) { priv->rx_queue[i]->rx_ring_size = rvals->rx_pending; priv->tx_queue[i]->tx_ring_size = rvals->tx_pending; - priv->tx_queue[i]->num_txbdfree = priv->tx_queue[i]->tx_ring_size; + priv->tx_queue[i]->num_txbdfree = + priv->tx_queue[i]->tx_ring_size; } /* Rebuild the rings with the new size */ @@ -535,7 +553,8 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) if (dev->flags & IFF_UP) { /* Halt TX and RX, and process the frames which - * have already been received */ + * have already been received + */ local_irq_save(flags); lock_tx_qs(priv); lock_rx_qs(priv); @@ -548,7 +567,7 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) for (i = 0; i < priv->num_rx_queues; i++) gfar_clean_rx_ring(priv->rx_queue[i], - priv->rx_queue[i]->rx_ring_size); + priv->rx_queue[i]->rx_ring_size); /* Now we take down the rings to rebuild them */ stop_gfar(dev); @@ -564,12 +583,14 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) static uint32_t gfar_get_msglevel(struct net_device *dev) { struct gfar_private *priv = netdev_priv(dev); + return priv->msg_enable; } static void gfar_set_msglevel(struct net_device *dev, uint32_t data) { struct gfar_private *priv = netdev_priv(dev); + priv->msg_enable = data; } @@ -614,14 +635,14 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & RXH_L2DA) { fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH | - RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); priv->cur_filer_idx = priv->cur_filer_idx - 1; fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH | - RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); @@ -630,7 +651,7 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & RXH_VLAN) { fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH | - RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_AND | RQFCR_HASHTBL_0; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; @@ -639,7 +660,7 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & RXH_IP_SRC) { fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | - RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); @@ -648,7 +669,7 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & (RXH_IP_DST)) { fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH | - RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); @@ -657,7 +678,7 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & RXH_L3_PROTO) { fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH | - RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); @@ -666,7 +687,7 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & RXH_L4_B_0_1) { fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | - RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); @@ -675,7 +696,7 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) if (ethflow & RXH_L4_B_2_3) { fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH | - RQFCR_AND | RQFCR_HASHTBL_0; + RQFCR_AND | RQFCR_HASHTBL_0; priv->ftp_rqfpr[priv->cur_filer_idx] = fpr; priv->ftp_rqfcr[priv->cur_filer_idx] = fcr; gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr); @@ -683,7 +704,8 @@ static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow) } } -static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class) +static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, + u64 class) { unsigned int last_rule_idx = priv->cur_filer_idx; unsigned int cmp_rqfpr; @@ -694,9 +716,9 @@ static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u int ret = 1; local_rqfpr = kmalloc(sizeof(unsigned int) * (MAX_FILER_IDX + 1), - GFP_KERNEL); + GFP_KERNEL); local_rqfcr = kmalloc(sizeof(unsigned int) * (MAX_FILER_IDX + 1), - GFP_KERNEL); + GFP_KERNEL); if (!local_rqfpr || !local_rqfcr) { pr_err("Out of memory\n"); ret = 0; @@ -726,9 +748,9 @@ static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u local_rqfpr[j] = priv->ftp_rqfpr[i]; local_rqfcr[j] = priv->ftp_rqfcr[i]; j--; - if ((priv->ftp_rqfcr[i] == (RQFCR_PID_PARSE | - RQFCR_CLE |RQFCR_AND)) && - (priv->ftp_rqfpr[i] == cmp_rqfpr)) + if ((priv->ftp_rqfcr[i] == + (RQFCR_PID_PARSE | RQFCR_CLE | RQFCR_AND)) && + (priv->ftp_rqfpr[i] == cmp_rqfpr)) break; } @@ -743,12 +765,12 @@ static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u */ for (l = i+1; l < MAX_FILER_IDX; l++) { if ((priv->ftp_rqfcr[l] & RQFCR_CLE) && - !(priv->ftp_rqfcr[l] & RQFCR_AND)) { + !(priv->ftp_rqfcr[l] & RQFCR_AND)) { priv->ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT | - RQFCR_HASHTBL_0 | RQFCR_PID_MASK; + RQFCR_HASHTBL_0 | RQFCR_PID_MASK; priv->ftp_rqfpr[l] = FPR_FILER_MASK; gfar_write_filer(priv, l, priv->ftp_rqfcr[l], - priv->ftp_rqfpr[l]); + priv->ftp_rqfpr[l]); break; } @@ -773,7 +795,7 @@ static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u priv->ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k]; priv->ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k]; gfar_write_filer(priv, priv->cur_filer_idx, - local_rqfcr[k], local_rqfpr[k]); + local_rqfcr[k], local_rqfpr[k]); if (!priv->cur_filer_idx) break; priv->cur_filer_idx = priv->cur_filer_idx - 1; @@ -785,7 +807,8 @@ err: return ret; } -static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd) +static int gfar_set_hash_opts(struct gfar_private *priv, + struct ethtool_rxnfc *cmd) { /* write the filer rules here */ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type)) @@ -810,10 +833,10 @@ static int gfar_check_filer_hardware(struct gfar_private *priv) i &= RCTRL_PRSDEP_MASK | RCTRL_PRSFM; if (i == (RCTRL_PRSDEP_MASK | RCTRL_PRSFM)) { netdev_info(priv->ndev, - "Receive Queue Filtering enabled\n"); + "Receive Queue Filtering enabled\n"); } else { netdev_warn(priv->ndev, - "Receive Queue Filtering disabled\n"); + "Receive Queue Filtering disabled\n"); return -EOPNOTSUPP; } } @@ -823,16 +846,17 @@ static int gfar_check_filer_hardware(struct gfar_private *priv) i &= RCTRL_PRSDEP_MASK; if (i == RCTRL_PRSDEP_MASK) { netdev_info(priv->ndev, - "Receive Queue Filtering enabled\n"); + "Receive Queue Filtering enabled\n"); } else { netdev_warn(priv->ndev, - "Receive Queue Filtering disabled\n"); + "Receive Queue Filtering disabled\n"); return -EOPNOTSUPP; } } /* Sets the properties for arbitrary filer rule - * to the first 4 Layer 4 Bytes */ + * to the first 4 Layer 4 Bytes + */ regs->rbifx = 0xC0C1C2C3; return 0; } @@ -870,14 +894,14 @@ static void gfar_set_mask(u32 mask, struct filer_table *tab) static void gfar_set_parse_bits(u32 value, u32 mask, struct filer_table *tab) { gfar_set_mask(mask, tab); - tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_PID_PARSE - | RQFCR_AND; + tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_PID_PARSE | + RQFCR_AND; tab->fe[tab->index].prop = value; tab->index++; } static void gfar_set_general_attribute(u32 value, u32 mask, u32 flag, - struct filer_table *tab) + struct filer_table *tab) { gfar_set_mask(mask, tab); tab->fe[tab->index].ctrl = RQFCR_CMP_EXACT | RQFCR_AND | flag; @@ -885,8 +909,7 @@ static void gfar_set_general_attribute(u32 value, u32 mask, u32 flag, tab->index++; } -/* - * For setting a tuple of value and mask of type flag +/* For setting a tuple of value and mask of type flag * Example: * IP-Src = 10.0.0.0/255.0.0.0 * value: 0x0A000000 mask: FF000000 flag: RQFPR_IPV4 @@ -901,7 +924,7 @@ static void gfar_set_general_attribute(u32 value, u32 mask, u32 flag, * Further the all masks are one-padded for better hardware efficiency. */ static void gfar_set_attribute(u32 value, u32 mask, u32 flag, - struct filer_table *tab) + struct filer_table *tab) { switch (flag) { /* 3bit */ @@ -959,7 +982,8 @@ static void gfar_set_attribute(u32 value, u32 mask, u32 flag, /* Translates value and mask for UDP, TCP or SCTP */ static void gfar_set_basic_ip(struct ethtool_tcpip4_spec *value, - struct ethtool_tcpip4_spec *mask, struct filer_table *tab) + struct ethtool_tcpip4_spec *mask, + struct filer_table *tab) { gfar_set_attribute(value->ip4src, mask->ip4src, RQFCR_PID_SIA, tab); gfar_set_attribute(value->ip4dst, mask->ip4dst, RQFCR_PID_DIA, tab); @@ -970,97 +994,92 @@ static void gfar_set_basic_ip(struct ethtool_tcpip4_spec *value, /* Translates value and mask for RAW-IP4 */ static void gfar_set_user_ip(struct ethtool_usrip4_spec *value, - struct ethtool_usrip4_spec *mask, struct filer_table *tab) + struct ethtool_usrip4_spec *mask, + struct filer_table *tab) { gfar_set_attribute(value->ip4src, mask->ip4src, RQFCR_PID_SIA, tab); gfar_set_attribute(value->ip4dst, mask->ip4dst, RQFCR_PID_DIA, tab); gfar_set_attribute(value->tos, mask->tos, RQFCR_PID_TOS, tab); gfar_set_attribute(value->proto, mask->proto, RQFCR_PID_L4P, tab); gfar_set_attribute(value->l4_4_bytes, mask->l4_4_bytes, RQFCR_PID_ARB, - tab); + tab); } /* Translates value and mask for ETHER spec */ static void gfar_set_ether(struct ethhdr *value, struct ethhdr *mask, - struct filer_table *tab) + struct filer_table *tab) { u32 upper_temp_mask = 0; u32 lower_temp_mask = 0; + /* Source address */ if (!is_broadcast_ether_addr(mask->h_source)) { - if (is_zero_ether_addr(mask->h_source)) { upper_temp_mask = 0xFFFFFFFF; lower_temp_mask = 0xFFFFFFFF; } else { - upper_temp_mask = mask->h_source[0] << 16 - | mask->h_source[1] << 8 - | mask->h_source[2]; - lower_temp_mask = mask->h_source[3] << 16 - | mask->h_source[4] << 8 - | mask->h_source[5]; + upper_temp_mask = mask->h_source[0] << 16 | + mask->h_source[1] << 8 | + mask->h_source[2]; + lower_temp_mask = mask->h_source[3] << 16 | + mask->h_source[4] << 8 | + mask->h_source[5]; } /* Upper 24bit */ - gfar_set_attribute( - value->h_source[0] << 16 | value->h_source[1] - << 8 | value->h_source[2], - upper_temp_mask, RQFCR_PID_SAH, tab); + gfar_set_attribute(value->h_source[0] << 16 | + value->h_source[1] << 8 | + value->h_source[2], + upper_temp_mask, RQFCR_PID_SAH, tab); /* And the same for the lower part */ - gfar_set_attribute( - value->h_source[3] << 16 | value->h_source[4] - << 8 | value->h_source[5], - lower_temp_mask, RQFCR_PID_SAL, tab); + gfar_set_attribute(value->h_source[3] << 16 | + value->h_source[4] << 8 | + value->h_source[5], + lower_temp_mask, RQFCR_PID_SAL, tab); } /* Destination address */ if (!is_broadcast_ether_addr(mask->h_dest)) { - /* Special for destination is limited broadcast */ - if ((is_broadcast_ether_addr(value->h_dest) - && is_zero_ether_addr(mask->h_dest))) { + if ((is_broadcast_ether_addr(value->h_dest) && + is_zero_ether_addr(mask->h_dest))) { gfar_set_parse_bits(RQFPR_EBC, RQFPR_EBC, tab); } else { - if (is_zero_ether_addr(mask->h_dest)) { upper_temp_mask = 0xFFFFFFFF; lower_temp_mask = 0xFFFFFFFF; } else { - upper_temp_mask = mask->h_dest[0] << 16 - | mask->h_dest[1] << 8 - | mask->h_dest[2]; - lower_temp_mask = mask->h_dest[3] << 16 - | mask->h_dest[4] << 8 - | mask->h_dest[5]; + upper_temp_mask = mask->h_dest[0] << 16 | + mask->h_dest[1] << 8 | + mask->h_dest[2]; + lower_temp_mask = mask->h_dest[3] << 16 | + mask->h_dest[4] << 8 | + mask->h_dest[5]; } /* Upper 24bit */ - gfar_set_attribute( - value->h_dest[0] << 16 - | value->h_dest[1] << 8 - | value->h_dest[2], - upper_temp_mask, RQFCR_PID_DAH, tab); + gfar_set_attribute(value->h_dest[0] << 16 | + value->h_dest[1] << 8 | + value->h_dest[2], + upper_temp_mask, RQFCR_PID_DAH, tab); /* And the same for the lower part */ - gfar_set_attribute( - value->h_dest[3] << 16 - | value->h_dest[4] << 8 - | value->h_dest[5], - lower_temp_mask, RQFCR_PID_DAL, tab); + gfar_set_attribute(value->h_dest[3] << 16 | + value->h_dest[4] << 8 | + value->h_dest[5], + lower_temp_mask, RQFCR_PID_DAL, tab); } } gfar_set_attribute(value->h_proto, mask->h_proto, RQFCR_PID_ETY, tab); - } /* Convert a rule to binary filter format of gianfar */ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, - struct filer_table *tab) + struct filer_table *tab) { u32 vlan = 0, vlan_mask = 0; u32 id = 0, id_mask = 0; u32 cfi = 0, cfi_mask = 0; u32 prio = 0, prio_mask = 0; - u32 old_index = tab->index; /* Check if vlan is wanted */ @@ -1076,13 +1095,16 @@ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, id_mask = rule->m_ext.vlan_tci & VLAN_VID_MASK; cfi = rule->h_ext.vlan_tci & VLAN_CFI_MASK; cfi_mask = rule->m_ext.vlan_tci & VLAN_CFI_MASK; - prio = (rule->h_ext.vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; - prio_mask = (rule->m_ext.vlan_tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; + prio = (rule->h_ext.vlan_tci & VLAN_PRIO_MASK) >> + VLAN_PRIO_SHIFT; + prio_mask = (rule->m_ext.vlan_tci & VLAN_PRIO_MASK) >> + VLAN_PRIO_SHIFT; if (cfi == VLAN_TAG_PRESENT && cfi_mask == VLAN_TAG_PRESENT) { vlan |= RQFPR_CFI; vlan_mask |= RQFPR_CFI; - } else if (cfi != VLAN_TAG_PRESENT && cfi_mask == VLAN_TAG_PRESENT) { + } else if (cfi != VLAN_TAG_PRESENT && + cfi_mask == VLAN_TAG_PRESENT) { vlan_mask |= RQFPR_CFI; } } @@ -1090,34 +1112,36 @@ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, switch (rule->flow_type & ~FLOW_EXT) { case TCP_V4_FLOW: gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_TCP | vlan, - RQFPR_IPV4 | RQFPR_TCP | vlan_mask, tab); + RQFPR_IPV4 | RQFPR_TCP | vlan_mask, tab); gfar_set_basic_ip(&rule->h_u.tcp_ip4_spec, - &rule->m_u.tcp_ip4_spec, tab); + &rule->m_u.tcp_ip4_spec, tab); break; case UDP_V4_FLOW: gfar_set_parse_bits(RQFPR_IPV4 | RQFPR_UDP | vlan, - RQFPR_IPV4 | RQFPR_UDP | vlan_mask, tab); + RQFPR_IPV4 | RQFPR_UDP | vlan_mask, tab); gfar_set_basic_ip(&rule->h_u.udp_ip4_spec, - &rule->m_u.udp_ip4_spec, tab); + &rule->m_u.udp_ip4_spec, tab); break; case SCTP_V4_FLOW: gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask, - tab); + tab); gfar_set_attribute(132, 0, RQFCR_PID_L4P, tab); - gfar_set_basic_ip((struct ethtool_tcpip4_spec *) &rule->h_u, - (struct ethtool_tcpip4_spec *) &rule->m_u, tab); + gfar_set_basic_ip((struct ethtool_tcpip4_spec *)&rule->h_u, + (struct ethtool_tcpip4_spec *)&rule->m_u, + tab); break; case IP_USER_FLOW: gfar_set_parse_bits(RQFPR_IPV4 | vlan, RQFPR_IPV4 | vlan_mask, - tab); + tab); gfar_set_user_ip((struct ethtool_usrip4_spec *) &rule->h_u, - (struct ethtool_usrip4_spec *) &rule->m_u, tab); + (struct ethtool_usrip4_spec *) &rule->m_u, + tab); break; case ETHER_FLOW: if (vlan) gfar_set_parse_bits(vlan, vlan_mask, tab); gfar_set_ether((struct ethhdr *) &rule->h_u, - (struct ethhdr *) &rule->m_u, tab); + (struct ethhdr *) &rule->m_u, tab); break; default: return -1; @@ -1152,7 +1176,9 @@ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, tab->fe[tab->index - 1].ctrl |= RQFCR_CLE; } - /* In rare cases the cache can be full while there is free space in hw */ + /* In rare cases the cache can be full while there is + * free space in hw + */ if (tab->index > MAX_FILER_CACHE_IDX - 1) return -EBUSY; @@ -1161,7 +1187,7 @@ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, /* Copy size filer entries */ static void gfar_copy_filer_entries(struct gfar_filer_entry dst[0], - struct gfar_filer_entry src[0], s32 size) + struct gfar_filer_entry src[0], s32 size) { while (size > 0) { size--; @@ -1171,10 +1197,12 @@ static void gfar_copy_filer_entries(struct gfar_filer_entry dst[0], } /* Delete the contents of the filer-table between start and end - * and collapse them */ + * and collapse them + */ static int gfar_trim_filer_entries(u32 begin, u32 end, struct filer_table *tab) { int length; + if (end > MAX_FILER_CACHE_IDX || end < begin) return -EINVAL; @@ -1200,14 +1228,14 @@ static int gfar_trim_filer_entries(u32 begin, u32 end, struct filer_table *tab) /* Make space on the wanted location */ static int gfar_expand_filer_entries(u32 begin, u32 length, - struct filer_table *tab) + struct filer_table *tab) { - if (length == 0 || length + tab->index > MAX_FILER_CACHE_IDX || begin - > MAX_FILER_CACHE_IDX) + if (length == 0 || length + tab->index > MAX_FILER_CACHE_IDX || + begin > MAX_FILER_CACHE_IDX) return -EINVAL; gfar_copy_filer_entries(&(tab->fe[begin + length]), &(tab->fe[begin]), - tab->index - length + 1); + tab->index - length + 1); tab->index += length; return 0; @@ -1215,9 +1243,10 @@ static int gfar_expand_filer_entries(u32 begin, u32 length, static int gfar_get_next_cluster_start(int start, struct filer_table *tab) { - for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); start++) { - if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE)) - == (RQFCR_AND | RQFCR_CLE)) + for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); + start++) { + if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE)) == + (RQFCR_AND | RQFCR_CLE)) return start; } return -1; @@ -1225,16 +1254,16 @@ static int gfar_get_next_cluster_start(int start, struct filer_table *tab) static int gfar_get_next_cluster_end(int start, struct filer_table *tab) { - for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); start++) { - if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE)) - == (RQFCR_CLE)) + for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); + start++) { + if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE)) == + (RQFCR_CLE)) return start; } return -1; } -/* - * Uses hardwares clustering option to reduce +/* Uses hardwares clustering option to reduce * the number of filer table entries */ static void gfar_cluster_filer(struct filer_table *tab) @@ -1244,8 +1273,7 @@ static void gfar_cluster_filer(struct filer_table *tab) while ((i = gfar_get_next_cluster_start(++i, tab)) != -1) { j = i; while ((j = gfar_get_next_cluster_start(++j, tab)) != -1) { - /* - * The cluster entries self and the previous one + /* The cluster entries self and the previous one * (a mask) must be identical! */ if (tab->fe[i].ctrl != tab->fe[j].ctrl) @@ -1260,21 +1288,21 @@ static void gfar_cluster_filer(struct filer_table *tab) jend = gfar_get_next_cluster_end(j, tab); if (jend == -1 || iend == -1) break; - /* - * First we make some free space, where our cluster + + /* First we make some free space, where our cluster * element should be. Then we copy it there and finally * delete in from its old location. */ - - if (gfar_expand_filer_entries(iend, (jend - j), tab) - == -EINVAL) + if (gfar_expand_filer_entries(iend, (jend - j), tab) == + -EINVAL) break; gfar_copy_filer_entries(&(tab->fe[iend + 1]), - &(tab->fe[jend + 1]), jend - j); + &(tab->fe[jend + 1]), jend - j); if (gfar_trim_filer_entries(jend - 1, - jend + (jend - j), tab) == -EINVAL) + jend + (jend - j), + tab) == -EINVAL) return; /* Mask out cluster bit */ @@ -1285,8 +1313,9 @@ static void gfar_cluster_filer(struct filer_table *tab) /* Swaps the masked bits of a1<>a2 and b1<>b2 */ static void gfar_swap_bits(struct gfar_filer_entry *a1, - struct gfar_filer_entry *a2, struct gfar_filer_entry *b1, - struct gfar_filer_entry *b2, u32 mask) + struct gfar_filer_entry *a2, + struct gfar_filer_entry *b1, + struct gfar_filer_entry *b2, u32 mask) { u32 temp[4]; temp[0] = a1->ctrl & mask; @@ -1305,13 +1334,12 @@ static void gfar_swap_bits(struct gfar_filer_entry *a1, b2->ctrl |= temp[2]; } -/* - * Generate a list consisting of masks values with their start and +/* Generate a list consisting of masks values with their start and * end of validity and block as indicator for parts belonging * together (glued by ANDs) in mask_table */ static u32 gfar_generate_mask_table(struct gfar_mask_entry *mask_table, - struct filer_table *tab) + struct filer_table *tab) { u32 i, and_index = 0, block_index = 1; @@ -1327,13 +1355,13 @@ static u32 gfar_generate_mask_table(struct gfar_mask_entry *mask_table, and_index++; } /* cluster starts and ends will be separated because they should - * hold their position */ + * hold their position + */ if (tab->fe[i].ctrl & RQFCR_CLE) block_index++; /* A not set AND indicates the end of a depended block */ if (!(tab->fe[i].ctrl & RQFCR_AND)) block_index++; - } mask_table[and_index - 1].end = i - 1; @@ -1341,14 +1369,13 @@ static u32 gfar_generate_mask_table(struct gfar_mask_entry *mask_table, return and_index; } -/* - * Sorts the entries of mask_table by the values of the masks. +/* Sorts the entries of mask_table by the values of the masks. * Important: The 0xFF80 flags of the first and last entry of a * block must hold their position (which queue, CLusterEnable, ReJEct, * AND) */ static void gfar_sort_mask_table(struct gfar_mask_entry *mask_table, - struct filer_table *temp_table, u32 and_index) + struct filer_table *temp_table, u32 and_index) { /* Pointer to compare function (_asc or _desc) */ int (*gfar_comp)(const void *, const void *); @@ -1359,16 +1386,16 @@ static void gfar_sort_mask_table(struct gfar_mask_entry *mask_table, gfar_comp = &gfar_comp_desc; for (i = 0; i < and_index; i++) { - if (prev != mask_table[i].block) { old_first = mask_table[start].start + 1; old_last = mask_table[i - 1].end; sort(mask_table + start, size, - sizeof(struct gfar_mask_entry), - gfar_comp, &gfar_swap); + sizeof(struct gfar_mask_entry), + gfar_comp, &gfar_swap); /* Toggle order for every block. This makes the - * thing more efficient! */ + * thing more efficient! + */ if (gfar_comp == gfar_comp_desc) gfar_comp = &gfar_comp_asc; else @@ -1378,12 +1405,11 @@ static void gfar_sort_mask_table(struct gfar_mask_entry *mask_table, new_last = mask_table[i - 1].end; gfar_swap_bits(&temp_table->fe[new_first], - &temp_table->fe[old_first], - &temp_table->fe[new_last], - &temp_table->fe[old_last], - RQFCR_QUEUE | RQFCR_CLE | - RQFCR_RJE | RQFCR_AND - ); + &temp_table->fe[old_first], + &temp_table->fe[new_last], + &temp_table->fe[old_last], + RQFCR_QUEUE | RQFCR_CLE | + RQFCR_RJE | RQFCR_AND); start = i; size = 0; @@ -1391,11 +1417,9 @@ static void gfar_sort_mask_table(struct gfar_mask_entry *mask_table, size++; prev = mask_table[i].block; } - } -/* - * Reduces the number of masks needed in the filer table to save entries +/* Reduces the number of masks needed in the filer table to save entries * This is done by sorting the masks of a depended block. A depended block is * identified by gluing ANDs or CLE. The sorting order toggles after every * block. Of course entries in scope of a mask must change their location with @@ -1410,13 +1434,14 @@ static int gfar_optimize_filer_masks(struct filer_table *tab) s32 ret = 0; /* We need a copy of the filer table because - * we want to change its order */ + * we want to change its order + */ temp_table = kmemdup(tab, sizeof(*temp_table), GFP_KERNEL); if (temp_table == NULL) return -ENOMEM; mask_table = kcalloc(MAX_FILER_CACHE_IDX / 2 + 1, - sizeof(struct gfar_mask_entry), GFP_KERNEL); + sizeof(struct gfar_mask_entry), GFP_KERNEL); if (mask_table == NULL) { ret = -ENOMEM; @@ -1428,7 +1453,8 @@ static int gfar_optimize_filer_masks(struct filer_table *tab) gfar_sort_mask_table(mask_table, temp_table, and_index); /* Now we can copy the data from our duplicated filer table to - * the real one in the order the mask table says */ + * the real one in the order the mask table says + */ for (i = 0; i < and_index; i++) { size = mask_table[i].end - mask_table[i].start + 1; gfar_copy_filer_entries(&(tab->fe[j]), @@ -1437,7 +1463,8 @@ static int gfar_optimize_filer_masks(struct filer_table *tab) } /* And finally we just have to check for duplicated masks and drop the - * second ones */ + * second ones + */ for (i = 0; i < tab->index && i < MAX_FILER_CACHE_IDX; i++) { if (tab->fe[i].ctrl == 0x80) { previous_mask = i++; @@ -1448,7 +1475,8 @@ static int gfar_optimize_filer_masks(struct filer_table *tab) if (tab->fe[i].ctrl == 0x80) { if (tab->fe[i].prop == tab->fe[previous_mask].prop) { /* Two identical ones found! - * So drop the second one! */ + * So drop the second one! + */ gfar_trim_filer_entries(i, i, tab); } else /* Not identical! */ @@ -1463,7 +1491,7 @@ end: kfree(temp_table); /* Write the bit-pattern from software's buffer to hardware registers */ static int gfar_write_filer_table(struct gfar_private *priv, - struct filer_table *tab) + struct filer_table *tab) { u32 i = 0; if (tab->index > MAX_FILER_IDX - 1) @@ -1473,13 +1501,15 @@ static int gfar_write_filer_table(struct gfar_private *priv, lock_rx_qs(priv); /* Fill regular entries */ - for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl); i++) + for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl); + i++) gfar_write_filer(priv, i, tab->fe[i].ctrl, tab->fe[i].prop); /* Fill the rest with fall-troughs */ for (; i < MAX_FILER_IDX - 1; i++) gfar_write_filer(priv, i, 0x60, 0xFFFFFFFF); /* Last entry must be default accept - * because that's what people expect */ + * because that's what people expect + */ gfar_write_filer(priv, i, 0x20, 0x0); unlock_rx_qs(priv); @@ -1488,21 +1518,21 @@ static int gfar_write_filer_table(struct gfar_private *priv, } static int gfar_check_capability(struct ethtool_rx_flow_spec *flow, - struct gfar_private *priv) + struct gfar_private *priv) { if (flow->flow_type & FLOW_EXT) { if (~flow->m_ext.data[0] || ~flow->m_ext.data[1]) netdev_warn(priv->ndev, - "User-specific data not supported!\n"); + "User-specific data not supported!\n"); if (~flow->m_ext.vlan_etype) netdev_warn(priv->ndev, - "VLAN-etype not supported!\n"); + "VLAN-etype not supported!\n"); } if (flow->flow_type == IP_USER_FLOW) if (flow->h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4) netdev_warn(priv->ndev, - "IP-Version differing from IPv4 not supported!\n"); + "IP-Version differing from IPv4 not supported!\n"); return 0; } @@ -1520,15 +1550,18 @@ static int gfar_process_filer_changes(struct gfar_private *priv) return -ENOMEM; /* Now convert the existing filer data from flow_spec into - * filer tables binary format */ + * filer tables binary format + */ list_for_each_entry(j, &priv->rx_list.list, list) { ret = gfar_convert_to_filer(&j->fs, tab); if (ret == -EBUSY) { - netdev_err(priv->ndev, "Rule not added: No free space!\n"); + netdev_err(priv->ndev, + "Rule not added: No free space!\n"); goto end; } if (ret == -1) { - netdev_err(priv->ndev, "Rule not added: Unsupported Flow-type!\n"); + netdev_err(priv->ndev, + "Rule not added: Unsupported Flow-type!\n"); goto end; } } @@ -1540,9 +1573,9 @@ static int gfar_process_filer_changes(struct gfar_private *priv) gfar_optimize_filer_masks(tab); pr_debug("\n\tSummary:\n" - "\tData on hardware: %d\n" - "\tCompression rate: %d%%\n", - tab->index, 100 - (100 * tab->index) / i); + "\tData on hardware: %d\n" + "\tCompression rate: %d%%\n", + tab->index, 100 - (100 * tab->index) / i); /* Write everything to hardware */ ret = gfar_write_filer_table(priv, tab); @@ -1551,7 +1584,8 @@ static int gfar_process_filer_changes(struct gfar_private *priv) goto end; } -end: kfree(tab); +end: + kfree(tab); return ret; } @@ -1569,7 +1603,7 @@ static void gfar_invert_masks(struct ethtool_rx_flow_spec *flow) } static int gfar_add_cls(struct gfar_private *priv, - struct ethtool_rx_flow_spec *flow) + struct ethtool_rx_flow_spec *flow) { struct ethtool_flow_spec_container *temp, *comp; int ret = 0; @@ -1591,7 +1625,6 @@ static int gfar_add_cls(struct gfar_private *priv, list_add(&temp->list, &priv->rx_list.list); goto process; } else { - list_for_each_entry(comp, &priv->rx_list.list, list) { if (comp->fs.location > flow->location) { list_add_tail(&temp->list, &comp->list); @@ -1599,8 +1632,8 @@ static int gfar_add_cls(struct gfar_private *priv, } if (comp->fs.location == flow->location) { netdev_err(priv->ndev, - "Rule not added: ID %d not free!\n", - flow->location); + "Rule not added: ID %d not free!\n", + flow->location); ret = -EBUSY; goto clean_mem; } @@ -1642,7 +1675,6 @@ static int gfar_del_cls(struct gfar_private *priv, u32 loc) } return ret; - } static int gfar_get_cls(struct gfar_private *priv, struct ethtool_rxnfc *cmd) @@ -1663,7 +1695,7 @@ static int gfar_get_cls(struct gfar_private *priv, struct ethtool_rxnfc *cmd) } static int gfar_get_cls_all(struct gfar_private *priv, - struct ethtool_rxnfc *cmd, u32 *rule_locs) + struct ethtool_rxnfc *cmd, u32 *rule_locs) { struct ethtool_flow_spec_container *comp; u32 i = 0; @@ -1714,7 +1746,7 @@ static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd) } static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd, - u32 *rule_locs) + u32 *rule_locs) { struct gfar_private *priv = netdev_priv(dev); int ret = 0; @@ -1748,23 +1780,19 @@ static int gfar_get_ts_info(struct net_device *dev, struct gfar_private *priv = netdev_priv(dev); if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)) { - info->so_timestamping = - SOF_TIMESTAMPING_RX_SOFTWARE | - SOF_TIMESTAMPING_SOFTWARE; + info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; info->phc_index = -1; return 0; } - info->so_timestamping = - SOF_TIMESTAMPING_TX_HARDWARE | - SOF_TIMESTAMPING_RX_HARDWARE | - SOF_TIMESTAMPING_RAW_HARDWARE; + info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; info->phc_index = gfar_phc_index; - info->tx_types = - (1 << HWTSTAMP_TX_OFF) | - (1 << HWTSTAMP_TX_ON); - info->rx_filters = - (1 << HWTSTAMP_FILTER_NONE) | - (1 << HWTSTAMP_FILTER_ALL); + info->tx_types = (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); return 0; } -- cgit v1.2.3 From e19ebcab01cc130fa832764d453b263460ec3b91 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 10 May 2012 15:53:14 +0300 Subject: iwlwifi: Check BSS ctx active before call mac80211 It is possible that the BSS context is not active (for example when the current mode is set to GO), or that the vif->type is different than station. In such a case we cannot call mac80211 to report the average rssi for the interface (the function assumes that the vif is valid and that the type is station). Reviewed-by: Emmanuel Grumbach Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index e55ec6c8a920..c31072d42791 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -617,6 +617,11 @@ static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; int ave_rssi; + if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { + IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); + return false; + } + ave_rssi = ieee80211_ave_rssi(ctx->vif); if (!ave_rssi) { /* no rssi data, no changes to reduce tx power */ -- cgit v1.2.3 From 622a9268231bd6b486ec058a28f773fa89926012 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Wed, 9 May 2012 16:11:55 +0300 Subject: iwlwifi: handle race condition in ROC flow When a remain on channel request from mac80211 is followed by a request to tx a mgmt frame offchannel, it is possible that the remain on channel expires before the device reported the tx status for the frame. This causes a race condition in mac80211. To fix this, delay the ROC notification to mac80211 until the device reported the Tx status for all frames in the aux queue. Reviewed-by: Emmanuel Grumbach Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 18 +++++++++++ drivers/net/wireless/iwlwifi/iwl-agn.c | 3 ++ drivers/net/wireless/iwlwifi/iwl-agn.h | 5 +++ drivers/net/wireless/iwlwifi/iwl-dev.h | 1 + drivers/net/wireless/iwlwifi/iwl-scan.c | 51 +++++++++++++++++++++++++------ 5 files changed, 68 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 3366e2e2f00f..ad1d36fa7642 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -486,6 +486,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (sta_priv && sta_priv->client && !is_agg) atomic_inc(&sta_priv->pending_frames); + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + iwl_scan_offchannel_skb(priv); + return 0; drop_unlock_sta: @@ -1136,6 +1139,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct sk_buff *skb; struct iwl_rxon_context *ctx; bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); + bool is_offchannel_skb; tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> IWLAGN_TX_RES_TID_POS; @@ -1149,6 +1153,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, __skb_queue_head_init(&skbs); + is_offchannel_skb = false; + if (tx_resp->frame_count == 1) { u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); @@ -1225,10 +1231,19 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + is_offchannel_skb = + (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } WARN_ON(!is_agg && freed != 1); + + /* + * An offchannel frame can be send only on the AUX queue, where + * there is no aggregation (and reordering) so it only is single + * skb is expected to be processed. + */ + WARN_ON(is_offchannel_skb && freed != 1); } iwl_check_abort_status(priv, tx_resp->frame_count, status); @@ -1239,6 +1254,9 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, ieee80211_tx_status(priv->hw, skb); } + if (is_offchannel_skb) + iwl_scan_offchannel_skb_status(priv); + return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 5149e6f72945..cd95a240511a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -931,6 +931,9 @@ void iwl_down(struct iwl_priv *priv) priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); + /* Set num_aux_in_flight must be done after the transport is stopped */ + atomic_set(&priv->num_aux_in_flight, 0); + /* Clear out all status bits but a few that are stable across reset */ priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << STATUS_RF_KILL_HW | diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index 79c0fe06f4db..5c1693f2573b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -101,6 +101,7 @@ extern struct iwl_lib_ops iwl6030_lib; #define STATUS_CHANNEL_SWITCH_PENDING 11 #define STATUS_SCAN_COMPLETE 12 #define STATUS_POWER_PMI 13 +#define STATUS_SCAN_ROC_EXPIRED 14 struct iwl_ucode_capabilities; @@ -255,6 +256,10 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv, enum iwl_scan_type scan_type, enum ieee80211_band band); +void iwl_scan_roc_expired(struct iwl_priv *priv); +void iwl_scan_offchannel_skb(struct iwl_priv *priv); +void iwl_scan_offchannel_skb_status(struct iwl_priv *priv); + /* For faster active scanning, scan will move to the next channel if fewer than * PLCP_QUIET_THRESH packets are heard on this channel within * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 70062379d0ec..a4936a134162 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -846,6 +846,7 @@ struct iwl_priv { struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; unsigned long ucode_key_table; struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; + atomic_t num_aux_in_flight; u8 mac80211_registered; diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 031d8e21f82f..eb0ded7a8a99 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -101,11 +101,8 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) ieee80211_scan_completed(priv->hw, aborted); } - if (priv->scan_type == IWL_SCAN_ROC) { - ieee80211_remain_on_channel_expired(priv->hw); - priv->hw_roc_channel = NULL; - schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ); - } + if (priv->scan_type == IWL_SCAN_ROC) + iwl_scan_roc_expired(priv); priv->scan_type = IWL_SCAN_NORMAL; priv->scan_vif = NULL; @@ -134,11 +131,8 @@ static void iwl_process_scan_complete(struct iwl_priv *priv) goto out_settings; } - if (priv->scan_type == IWL_SCAN_ROC) { - ieee80211_remain_on_channel_expired(priv->hw); - priv->hw_roc_channel = NULL; - schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ); - } + if (priv->scan_type == IWL_SCAN_ROC) + iwl_scan_roc_expired(priv); if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { int err; @@ -1158,3 +1152,40 @@ void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) mutex_unlock(&priv->mutex); } } + +void iwl_scan_roc_expired(struct iwl_priv *priv) +{ + /* + * The status bit should be set here, to prevent a race + * where the atomic_read returns 1, but before the execution continues + * iwl_scan_offchannel_skb_status() checks if the status bit is set + */ + set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); + + if (atomic_read(&priv->num_aux_in_flight) == 0) { + ieee80211_remain_on_channel_expired(priv->hw); + priv->hw_roc_channel = NULL; + schedule_delayed_work(&priv->hw_roc_disable_work, + 10 * HZ); + + clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); + } else { + IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n", + atomic_read(&priv->num_aux_in_flight)); + } +} + +void iwl_scan_offchannel_skb(struct iwl_priv *priv) +{ + WARN_ON(!priv->hw_roc_start_notified); + atomic_inc(&priv->num_aux_in_flight); +} + +void iwl_scan_offchannel_skb_status(struct iwl_priv *priv) +{ + if (atomic_dec_return(&priv->num_aux_in_flight) == 0 && + test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) { + IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n"); + iwl_scan_roc_expired(priv); + } +} -- cgit v1.2.3 From aca86268faad5caed582de463c96061605c8ad60 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 14 May 2012 09:08:50 +0200 Subject: iwlwifi: make a lot of functions static A lot of functions were temporarily made non-static for experimental work, make them static again now. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 21 +++--- drivers/net/wireless/iwlwifi/iwl-agn.c | 32 ++++----- drivers/net/wireless/iwlwifi/iwl-agn.h | 81 --------------------- drivers/net/wireless/iwlwifi/iwl-mac80211.c | 106 ++++++++++++++-------------- drivers/net/wireless/iwlwifi/iwl-ucode.c | 2 +- 5 files changed, 82 insertions(+), 160 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 0a3aa7c83003..7fc8c25c7d41 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -175,7 +175,8 @@ static int iwlagn_disconn_pan(struct iwl_priv *priv, return ret; } -void iwlagn_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +static void iwlagn_update_qos(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) { int ret; @@ -202,8 +203,8 @@ void iwlagn_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx) IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); } -int iwlagn_update_beacon(struct iwl_priv *priv, - struct ieee80211_vif *vif) +static int iwlagn_update_beacon(struct iwl_priv *priv, + struct ieee80211_vif *vif) { lockdep_assert_held(&priv->mutex); @@ -215,7 +216,7 @@ int iwlagn_update_beacon(struct iwl_priv *priv, } static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) + struct iwl_rxon_context *ctx) { int ret = 0; struct iwl_rxon_assoc_cmd rxon_assoc; @@ -863,8 +864,8 @@ static int iwl_check_rxon_cmd(struct iwl_priv *priv, * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. */ -int iwl_full_rxon_required(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) +static int iwl_full_rxon_required(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) { const struct iwl_rxon_cmd *staging = &ctx->staging; const struct iwl_rxon_cmd *active = &ctx->active; @@ -1294,9 +1295,9 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) return ret; } -void iwlagn_check_needed_chains(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_bss_conf *bss_conf) +static void iwlagn_check_needed_chains(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_bss_conf *bss_conf) { struct ieee80211_vif *vif = ctx->vif; struct iwl_rxon_context *tmp; @@ -1388,7 +1389,7 @@ void iwlagn_check_needed_chains(struct iwl_priv *priv, ht_conf->single_chain_sufficient = !need_multiple; } -void iwlagn_chain_noise_reset(struct iwl_priv *priv) +static void iwlagn_chain_noise_reset(struct iwl_priv *priv) { struct iwl_chain_noise_data *data = &priv->chain_noise_data; int ret; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index cd95a240511a..fa007176bad2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -577,7 +577,7 @@ static const u8 iwlagn_pan_ac_to_queue[] = { 7, 6, 5, 4, }; -void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) +static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) { int i; @@ -644,7 +644,7 @@ void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); } -void iwl_rf_kill_ct_config(struct iwl_priv *priv) +static void iwl_rf_kill_ct_config(struct iwl_priv *priv) { struct iwl_ct_kill_config cmd; struct iwl_ct_kill_throttling_config adv_cmd; @@ -725,7 +725,7 @@ static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) } } -void iwl_send_bt_config(struct iwl_priv *priv) +static void iwl_send_bt_config(struct iwl_priv *priv) { struct iwl_bt_cmd bt_cmd = { .lead_time = BT_LEAD_TIME_DEF, @@ -1080,7 +1080,7 @@ static void iwlagn_disable_roc_work(struct work_struct *work) * *****************************************************************************/ -void iwl_setup_deferred_work(struct iwl_priv *priv) +static void iwl_setup_deferred_work(struct iwl_priv *priv) { priv->workqueue = create_singlethread_workqueue(DRV_NAME); @@ -1331,7 +1331,7 @@ static void iwl_free_geos(struct iwl_priv *priv) clear_bit(STATUS_GEO_CONFIGURED, &priv->status); } -int iwl_init_drv(struct iwl_priv *priv) +static int iwl_init_drv(struct iwl_priv *priv) { int ret; @@ -1394,7 +1394,7 @@ err: return ret; } -void iwl_uninit_drv(struct iwl_priv *priv) +static void iwl_uninit_drv(struct iwl_priv *priv) { iwl_free_geos(priv); iwl_free_channel_map(priv); @@ -1407,7 +1407,7 @@ void iwl_uninit_drv(struct iwl_priv *priv) #endif } -void iwl_set_hw_params(struct iwl_priv *priv) +static void iwl_set_hw_params(struct iwl_priv *priv) { if (priv->cfg->ht_params) priv->hw_params.use_rts_for_aggregation = @@ -1423,7 +1423,7 @@ void iwl_set_hw_params(struct iwl_priv *priv) /* show what optional capabilities we have */ -void iwl_option_config(struct iwl_priv *priv) +static void iwl_option_config(struct iwl_priv *priv) { #ifdef CONFIG_IWLWIFI_DEBUG IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); @@ -1722,7 +1722,7 @@ out: return op_mode; } -void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) +static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); @@ -2187,7 +2187,7 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) } } -void iwl_nic_error(struct iwl_op_mode *op_mode) +static void iwl_nic_error(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); @@ -2200,7 +2200,7 @@ void iwl_nic_error(struct iwl_op_mode *op_mode) iwlagn_fw_error(priv, false); } -void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) +static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); @@ -2210,7 +2210,7 @@ void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) } } -void iwl_nic_config(struct iwl_op_mode *op_mode) +static void iwl_nic_config(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); @@ -2225,7 +2225,7 @@ static void iwl_wimax_active(struct iwl_op_mode *op_mode) IWL_ERR(priv, "RF is used by WiMAX\n"); } -void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) +static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); int mq = priv->queue_to_mac80211[queue]; @@ -2244,7 +2244,7 @@ void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) ieee80211_stop_queue(priv->hw, mq); } -void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) +static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); int mq = priv->queue_to_mac80211[queue]; @@ -2284,7 +2284,7 @@ void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) priv->passive_no_rx = false; } -void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) +static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) { struct ieee80211_tx_info *info; @@ -2293,7 +2293,7 @@ void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) dev_kfree_skb_any(skb); } -void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index 5c1693f2573b..3f3607fd7dad 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -523,85 +523,4 @@ static inline const char *iwl_dvm_get_cmd_string(u8 cmd) return s; return "UNKNOWN"; } - -/* API method exported for mvm hybrid state */ -void iwl_setup_deferred_work(struct iwl_priv *priv); -int iwl_send_wimax_coex(struct iwl_priv *priv); -int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); -void iwl_option_config(struct iwl_priv *priv); -void iwl_set_hw_params(struct iwl_priv *priv); -void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags); -int iwl_init_drv(struct iwl_priv *priv); -void iwl_uninit_drv(struct iwl_priv *priv); -void iwl_send_bt_config(struct iwl_priv *priv); -void iwl_rf_kill_ct_config(struct iwl_priv *priv); -int iwl_setup_interface(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwl_teardown_interface(struct iwl_priv *priv, - struct ieee80211_vif *vif, - bool mode_change); -int iwl_full_rxon_required(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwlagn_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwlagn_check_needed_chains(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_bss_conf *bss_conf); -void iwlagn_chain_noise_reset(struct iwl_priv *priv); -int iwlagn_update_beacon(struct iwl_priv *priv, - struct ieee80211_vif *vif); -void iwl_tt_handler(struct iwl_priv *priv); -void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode); -void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue); -void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state); -void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb); -void iwl_nic_error(struct iwl_op_mode *op_mode); -void iwl_cmd_queue_full(struct iwl_op_mode *op_mode); -void iwl_nic_config(struct iwl_op_mode *op_mode); -int iwlagn_mac_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set); -void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, - enum ieee80211_rssi_event rssi_event); -int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw); -int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw); -void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop); -void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue); -void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_channel_switch *ch_switch); -int iwlagn_mac_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state); -int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size); -int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); -void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta); -void iwlagn_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast); -int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params); -void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data); -void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key); -int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key); -void iwlagn_mac_stop(struct ieee80211_hw *hw); -void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); -int iwlagn_mac_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); #endif /* __iwl_agn_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index ff66200b8eaa..d0b64cd95fc5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -338,7 +338,7 @@ static int iwlagn_mac_start(struct ieee80211_hw *hw) return 0; } -void iwlagn_mac_stop(struct ieee80211_hw *hw) +static void iwlagn_mac_stop(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -366,9 +366,9 @@ void iwlagn_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) +static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -394,7 +394,8 @@ void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, #ifdef CONFIG_PM_SLEEP -int iwlagn_mac_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +static int iwlagn_mac_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; @@ -505,7 +506,7 @@ static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) } #endif -void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -516,21 +517,21 @@ void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); } -void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) +static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); } -int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) +static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; @@ -630,11 +631,11 @@ int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return ret; } -int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) +static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; @@ -756,11 +757,11 @@ static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, return ret; } -int iwlagn_mac_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) +static int iwlagn_mac_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; @@ -839,8 +840,8 @@ int iwlagn_mac_sta_state(struct ieee80211_hw *hw, return ret; } -void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_channel_switch *ch_switch) +static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_channel_switch *ch_switch) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); const struct iwl_channel_info *ch_info; @@ -934,10 +935,10 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) ieee80211_chswitch_done(ctx->vif, is_success); } -void iwlagn_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) +static void iwlagn_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); __le32 filter_or = 0, filter_nand = 0; @@ -984,7 +985,7 @@ void iwlagn_configure_filter(struct ieee80211_hw *hw, FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } -void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) +static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1111,7 +1112,7 @@ static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, return err; } -int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) +static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1128,8 +1129,8 @@ int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) return 0; } -void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, - enum ieee80211_rssi_event rssi_event) +static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, + enum ieee80211_rssi_event rssi_event) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1153,8 +1154,8 @@ void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "leave\n"); } -int iwlagn_mac_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set) +static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, bool set) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1163,9 +1164,9 @@ int iwlagn_mac_set_tim(struct ieee80211_hw *hw, return 0; } -int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) +static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; @@ -1207,7 +1208,7 @@ int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, return 0; } -int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) +static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1223,7 +1224,8 @@ static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) return iwlagn_commit_rxon(priv, ctx); } -int iwl_setup_interface(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +static int iwl_setup_interface(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) { struct ieee80211_vif *vif = ctx->vif; int err, ac; @@ -1343,9 +1345,9 @@ static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, return err; } -void iwl_teardown_interface(struct iwl_priv *priv, - struct ieee80211_vif *vif, - bool mode_change) +static void iwl_teardown_interface(struct iwl_priv *priv, + struct ieee80211_vif *vif, + bool mode_change) { struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); @@ -1486,9 +1488,9 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, return err; } -int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) +static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret; @@ -1543,10 +1545,10 @@ static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) iwl_send_add_sta(priv, &cmd, CMD_ASYNC); } -void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) +static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; diff --git a/drivers/net/wireless/iwlwifi/iwl-ucode.c b/drivers/net/wireless/iwlwifi/iwl-ucode.c index bc40dc68b0f4..d14e633bebc9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-ucode.c +++ b/drivers/net/wireless/iwlwifi/iwl-ucode.c @@ -177,7 +177,7 @@ int iwl_init_alive_start(struct iwl_priv *priv) return 0; } -int iwl_send_wimax_coex(struct iwl_priv *priv) +static int iwl_send_wimax_coex(struct iwl_priv *priv) { struct iwl_wimax_coex_cmd coex_cmd; -- cgit v1.2.3 From 7ad580c2e6d918379b89668ea4e58a3599e27ee5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 15:40:11 +0200 Subject: iwlwifi: use mac80211 channel in connection init mac80211 guarantees that the channel pointer is always valid, so we can use that instead of our own channel list. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 7fc8c25c7d41..c570318a2375 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -37,8 +37,6 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { - const struct iwl_channel_info *ch_info; - memset(&ctx->staging, 0, sizeof(ctx->staging)); if (!ctx->vif) { @@ -80,14 +78,8 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; #endif - ch_info = iwl_get_channel_info(priv, priv->band, - le16_to_cpu(ctx->active.channel)); - - if (!ch_info) - ch_info = &priv->channel_info[0]; - - ctx->staging.channel = cpu_to_le16(ch_info->channel); - priv->band = ch_info->band; + ctx->staging.channel = cpu_to_le16(priv->hw->conf.channel->hw_value); + priv->band = priv->hw->conf.channel->band; iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); -- cgit v1.2.3 From 9dc216d740e1210ccb3da533e2dab99a156300f9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 15:42:56 +0200 Subject: iwlwifi: trust mac80211 channel setting mac80211 will never set, switch to, or scan on an invalid channel, so remove the code to validate the channels against the driver channel list. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 9 --------- drivers/net/wireless/iwlwifi/iwl-mac80211.c | 7 ------- drivers/net/wireless/iwlwifi/iwl-scan.c | 12 +----------- 3 files changed, 1 insertion(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index c570318a2375..36b4965527c2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -1182,7 +1182,6 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) struct iwl_rxon_context *ctx; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = conf->channel; - const struct iwl_channel_info *ch_info; int ret = 0; IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed); @@ -1216,14 +1215,6 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ch_info = iwl_get_channel_info(priv, channel->band, - channel->hw_value); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); - ret = -EINVAL; - goto out; - } - for_each_context(priv, ctx) { /* Configure HT40 channels */ if (ctx->ht.enabled != conf_is_ht(conf)) diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index d0b64cd95fc5..19e5235fba7c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -844,7 +844,6 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, struct ieee80211_channel_switch *ch_switch) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - const struct iwl_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = ch_switch->channel; struct iwl_ht_config *ht_conf = &priv->current_ht_config; @@ -881,12 +880,6 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, if (le16_to_cpu(ctx->active.channel) == ch) goto out; - ch_info = iwl_get_channel_info(priv, channel->band, ch); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_MAC80211(priv, "invalid channel\n"); - goto out; - } - priv->current_ht_config.smps = conf->smps_mode; /* Configure HT40 channels */ diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index eb0ded7a8a99..ea05ae8280e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -534,7 +534,6 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, { struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; - const struct iwl_channel_info *ch_info; u16 passive_dwell = 0; u16 active_dwell = 0; int added, i; @@ -559,16 +558,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, channel = chan->hw_value; scan_ch->channel = cpu_to_le16(channel); - ch_info = iwl_get_channel_info(priv, band, channel); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN(priv, - "Channel %d is INVALID for this band.\n", - channel); - continue; - } - - if (!is_active || is_channel_passive(ch_info) || - (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) + if (!is_active || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; -- cgit v1.2.3 From d9b9d940ac2d1c03ec955dc921dd12e432a64e5c Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 15:44:27 +0200 Subject: iwlwifi: use mac80211 channels for reset scan Instead of iterating our own channel list, use the mac80211 channel list since that's already processed per band and thus makes for less code. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-scan.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index ea05ae8280e1..069849e66234 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -447,27 +447,17 @@ static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, /* Return valid, unused, channel for a passive scan to reset the RF */ static u8 iwl_get_single_channel_number(struct iwl_priv *priv, - enum ieee80211_band band) + enum ieee80211_band band) { - const struct iwl_channel_info *ch_info; - int i; - u8 channel = 0; - u8 min, max; + struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; struct iwl_rxon_context *ctx; + int i; - if (band == IEEE80211_BAND_5GHZ) { - min = 14; - max = priv->channel_count; - } else { - min = 0; - max = 14; - } - - for (i = min; i < max; i++) { + for (i = 0; i < sband->n_channels; i++) { bool busy = false; for_each_context(priv, ctx) { - busy = priv->channel_info[i].channel == + busy = sband->channels[i].hw_value == le16_to_cpu(ctx->staging.channel); if (busy) break; @@ -476,13 +466,11 @@ static u8 iwl_get_single_channel_number(struct iwl_priv *priv, if (busy) continue; - channel = priv->channel_info[i].channel; - ch_info = iwl_get_channel_info(priv, band, channel); - if (is_channel_valid(ch_info)) - break; + if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) + return sband->channels[i].hw_value; } - return channel; + return 0; } static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, -- cgit v1.2.3 From cc66880ba58ef6b9ee645fa1e1c074746ff03ce4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 16:37:57 +0200 Subject: iwlwifi: use mac80211 channel for radar check In channel switch, instead of relying on our internal channel database, just use the mac80211 channel that we filled with that information on startup. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-devices.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-devices.c b/drivers/net/wireless/iwlwifi/iwl-agn-devices.c index 48533b3a0f9a..529cdac6f739 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-devices.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-devices.c @@ -455,7 +455,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl5000_channel_switch_cmd cmd; - const struct iwl_channel_info *ch_info; u32 switch_time_in_usec, ucode_switch_time; u16 ch; u32 tsf_low; @@ -505,14 +504,7 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, } IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", cmd.switch_time); - ch_info = iwl_get_channel_info(priv, priv->band, ch); - if (ch_info) - cmd.expect_beacon = is_channel_radar(ch_info); - else { - IWL_ERR(priv, "invalid channel switch from %u to %u\n", - ctx->active.channel, ch); - return -EFAULT; - } + cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; return iwl_dvm_send_cmd(priv, &hcmd); } @@ -654,7 +646,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; struct iwl6000_channel_switch_cmd cmd; - const struct iwl_channel_info *ch_info; u32 switch_time_in_usec, ucode_switch_time; u16 ch; u32 tsf_low; @@ -704,14 +695,7 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, } IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", cmd.switch_time); - ch_info = iwl_get_channel_info(priv, priv->band, ch); - if (ch_info) - cmd.expect_beacon = is_channel_radar(ch_info); - else { - IWL_ERR(priv, "invalid channel switch from %u to %u\n", - ctx->active.channel, ch); - return -EFAULT; - } + cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; return iwl_dvm_send_cmd(priv, &hcmd); } -- cgit v1.2.3 From 53e1116ebd3c0bab4d6daf0da4492f813b778cf8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 23:59:03 +0200 Subject: iwlwifi: give HT format to mac80211/radiotap We know whether we received a frame in GF format or not, add it to the radiotap information. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-rx.c | 2 ++ drivers/net/wireless/iwlwifi/iwl-mac80211.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index 403de96f9747..5035d2db14b1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -1012,6 +1012,8 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, rx_status.flag |= RX_FLAG_40MHZ; if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status.flag |= RX_FLAG_SHORT_GI; + if (rate_n_flags & RATE_MCS_GF_MSK) + rx_status.flag |= RX_FLAG_HT_GF; iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, rxb, &rx_status); diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index 19e5235fba7c..26a87c2b1653 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -154,6 +155,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, IEEE80211_HW_SCAN_WHILE_IDLE; hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; /* * Including the following line will crash some AP's. This -- cgit v1.2.3 From 76a3aa89937cf0b9a560e4c778e54e993018da60 Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Thu, 17 May 2012 12:42:59 +0300 Subject: iwlwifi: clear STATUS_SCAN_HW when PAN_PARAMS fails The STATUS_SCAN_HW is set before calling iwlagn_set_pan_params (used as an input to calculate slot time allocation). The bit needs to be cleared in case sending the command fails. Reviewed-by: Emmanuel Grumbach Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-scan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 069849e66234..a32b35ea9e60 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -966,8 +966,10 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) set_bit(STATUS_SCAN_HW, &priv->status); ret = iwlagn_set_pan_params(priv); - if (ret) + if (ret) { + clear_bit(STATUS_SCAN_HW, &priv->status); return ret; + } ret = iwl_dvm_send_cmd(priv, &cmd); if (ret) { -- cgit v1.2.3 From 6468a01a7181f3572a3e686910016f89ee71ff45 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 16 May 2012 19:13:54 +0200 Subject: iwlwifi: move PCIe into subdirectory Structure the code a bit more and move all PCIe code including the hardware configuration files into a PCIe specific subdirectory. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/Makefile | 10 +- drivers/net/wireless/iwlwifi/iwl-1000.c | 126 -- drivers/net/wireless/iwlwifi/iwl-2000.c | 225 --- drivers/net/wireless/iwlwifi/iwl-5000.c | 164 -- drivers/net/wireless/iwlwifi/iwl-6000.c | 363 ---- drivers/net/wireless/iwlwifi/iwl-cfg.h | 113 -- drivers/net/wireless/iwlwifi/iwl-pci.c | 379 ---- drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | 444 ----- drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c | 1058 ---------- drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 984 ---------- drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 2178 --------------------- drivers/net/wireless/iwlwifi/pcie/1000.c | 126 ++ drivers/net/wireless/iwlwifi/pcie/2000.c | 225 +++ drivers/net/wireless/iwlwifi/pcie/5000.c | 164 ++ drivers/net/wireless/iwlwifi/pcie/6000.c | 363 ++++ drivers/net/wireless/iwlwifi/pcie/cfg.h | 113 ++ drivers/net/wireless/iwlwifi/pcie/drv.c | 380 ++++ drivers/net/wireless/iwlwifi/pcie/internal.h | 444 +++++ drivers/net/wireless/iwlwifi/pcie/rx.c | 1058 ++++++++++ drivers/net/wireless/iwlwifi/pcie/trans.c | 2178 +++++++++++++++++++++ drivers/net/wireless/iwlwifi/pcie/tx.c | 984 ++++++++++ 21 files changed, 6038 insertions(+), 6041 deletions(-) delete mode 100644 drivers/net/wireless/iwlwifi/iwl-1000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-2000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-5000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-6000.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-cfg.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-pci.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-trans-pcie.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/1000.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/2000.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/5000.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/6000.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/cfg.h create mode 100644 drivers/net/wireless/iwlwifi/pcie/drv.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/internal.h create mode 100644 drivers/net/wireless/iwlwifi/pcie/rx.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/trans.c create mode 100644 drivers/net/wireless/iwlwifi/pcie/tx.c (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 931002738c9f..f155126e9a21 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -15,17 +15,13 @@ CFLAGS_iwl-devtrace.o := -I$(src) # WIFI obj-$(CONFIG_IWLWIFI) += iwlwifi.o -iwlwifi-objs += iwl-5000.o -iwlwifi-objs += iwl-6000.o -iwlwifi-objs += iwl-1000.o -iwlwifi-objs += iwl-2000.o iwlwifi-objs += iwl-io.o -iwlwifi-objs += iwl-pci.o iwlwifi-objs += iwl-drv.o iwlwifi-objs += iwl-debug.o iwlwifi-objs += iwl-notif-wait.o -iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o +iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o +iwlwifi-objs += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o -ccflags-y += -D__CHECK_ENDIAN__ +ccflags-y += -D__CHECK_ENDIAN__ -I$(src) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c deleted file mode 100644 index 2629a6602dfa..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ /dev/null @@ -1,126 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-cfg.h" -#include "iwl-csr.h" -#include "iwl-agn-hw.h" - -/* Highest firmware API version supported */ -#define IWL1000_UCODE_API_MAX 5 -#define IWL100_UCODE_API_MAX 5 - -/* Oldest version we won't warn about */ -#define IWL1000_UCODE_API_OK 5 -#define IWL100_UCODE_API_OK 5 - -/* Lowest firmware API version supported */ -#define IWL1000_UCODE_API_MIN 1 -#define IWL100_UCODE_API_MIN 5 - -/* EEPROM version */ -#define EEPROM_1000_TX_POWER_VERSION (4) -#define EEPROM_1000_EEPROM_VERSION (0x15C) - -#define IWL1000_FW_PRE "iwlwifi-1000-" -#define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode" - -#define IWL100_FW_PRE "iwlwifi-100-" -#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode" - - -static const struct iwl_base_params iwl1000_base_params = { - .num_of_queues = IWLAGN_NUM_QUEUES, - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, - .max_ll_items = OTP_MAX_LL_ITEMS_1000, - .shadow_ram_support = false, - .led_compensation = 51, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .wd_timeout = IWL_WATCHHDOG_DISABLED, - .max_event_log_size = 128, -}; - -static const struct iwl_ht_params iwl1000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ -}; - -#define IWL_DEVICE_1000 \ - .fw_name_pre = IWL1000_FW_PRE, \ - .ucode_api_max = IWL1000_UCODE_API_MAX, \ - .ucode_api_ok = IWL1000_UCODE_API_OK, \ - .ucode_api_min = IWL1000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_1000, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ - .base_params = &iwl1000_base_params, \ - .led_mode = IWL_LED_BLINK - -const struct iwl_cfg iwl1000_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", - IWL_DEVICE_1000, - .ht_params = &iwl1000_ht_params, -}; - -const struct iwl_cfg iwl1000_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", - IWL_DEVICE_1000, -}; - -#define IWL_DEVICE_100 \ - .fw_name_pre = IWL100_FW_PRE, \ - .ucode_api_max = IWL100_UCODE_API_MAX, \ - .ucode_api_ok = IWL100_UCODE_API_OK, \ - .ucode_api_min = IWL100_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_100, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ - .base_params = &iwl1000_base_params, \ - .led_mode = IWL_LED_RF_STATE, \ - .rx_with_siso_diversity = true - -const struct iwl_cfg iwl100_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", - IWL_DEVICE_100, - .ht_params = &iwl1000_ht_params, -}; - -const struct iwl_cfg iwl100_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 100 BG", - IWL_DEVICE_100, -}; - -MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c deleted file mode 100644 index 8133105ac645..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ /dev/null @@ -1,225 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-cfg.h" -#include "iwl-agn-hw.h" -#include "iwl-commands.h" /* needed for BT for now */ - -/* Highest firmware API version supported */ -#define IWL2030_UCODE_API_MAX 6 -#define IWL2000_UCODE_API_MAX 6 -#define IWL105_UCODE_API_MAX 6 -#define IWL135_UCODE_API_MAX 6 - -/* Oldest version we won't warn about */ -#define IWL2030_UCODE_API_OK 6 -#define IWL2000_UCODE_API_OK 6 -#define IWL105_UCODE_API_OK 6 -#define IWL135_UCODE_API_OK 6 - -/* Lowest firmware API version supported */ -#define IWL2030_UCODE_API_MIN 5 -#define IWL2000_UCODE_API_MIN 5 -#define IWL105_UCODE_API_MIN 5 -#define IWL135_UCODE_API_MIN 5 - -/* EEPROM version */ -#define EEPROM_2000_TX_POWER_VERSION (6) -#define EEPROM_2000_EEPROM_VERSION (0x805) - - -#define IWL2030_FW_PRE "iwlwifi-2030-" -#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode" - -#define IWL2000_FW_PRE "iwlwifi-2000-" -#define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode" - -#define IWL105_FW_PRE "iwlwifi-105-" -#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode" - -#define IWL135_FW_PRE "iwlwifi-135-" -#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode" - -static const struct iwl_base_params iwl2000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_2x00, - .shadow_ram_support = true, - .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .wd_timeout = IWL_DEF_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, -}; - - -static const struct iwl_base_params iwl2030_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_2x00, - .shadow_ram_support = true, - .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ - .hd_v2 = true, -}; - -static const struct iwl_ht_params iwl2000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ -}; - -static const struct iwl_bt_params iwl2030_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, - .bt_sco_disable = true, - .bt_session_2 = true, -}; - -#define IWL_DEVICE_2000 \ - .fw_name_pre = IWL2000_FW_PRE, \ - .ucode_api_max = IWL2000_UCODE_API_MAX, \ - .ucode_api_ok = IWL2000_UCODE_API_OK, \ - .ucode_api_min = IWL2000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_2000, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2000_base_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE - -const struct iwl_cfg iwl2000_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", - IWL_DEVICE_2000, - .ht_params = &iwl2000_ht_params, -}; - -const struct iwl_cfg iwl2000_2bgn_d_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", - IWL_DEVICE_2000, - .ht_params = &iwl2000_ht_params, -}; - -#define IWL_DEVICE_2030 \ - .fw_name_pre = IWL2030_FW_PRE, \ - .ucode_api_max = IWL2030_UCODE_API_MAX, \ - .ucode_api_ok = IWL2030_UCODE_API_OK, \ - .ucode_api_min = IWL2030_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_2030, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true - -const struct iwl_cfg iwl2030_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", - IWL_DEVICE_2030, - .ht_params = &iwl2000_ht_params, -}; - -#define IWL_DEVICE_105 \ - .fw_name_pre = IWL105_FW_PRE, \ - .ucode_api_max = IWL105_UCODE_API_MAX, \ - .ucode_api_ok = IWL105_UCODE_API_OK, \ - .ucode_api_min = IWL105_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_105, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2000_base_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ - .rx_with_siso_diversity = true - -const struct iwl_cfg iwl105_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", - IWL_DEVICE_105, - .ht_params = &iwl2000_ht_params, -}; - -const struct iwl_cfg iwl105_bgn_d_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", - IWL_DEVICE_105, - .ht_params = &iwl2000_ht_params, -}; - -#define IWL_DEVICE_135 \ - .fw_name_pre = IWL135_FW_PRE, \ - .ucode_api_max = IWL135_UCODE_API_MAX, \ - .ucode_api_ok = IWL135_UCODE_API_OK, \ - .ucode_api_min = IWL135_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_135, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .base_params = &iwl2030_base_params, \ - .bt_params = &iwl2030_bt_params, \ - .need_temp_offset_calib = true, \ - .temp_offset_v2 = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true, \ - .rx_with_siso_diversity = true - -const struct iwl_cfg iwl135_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", - IWL_DEVICE_135, - .ht_params = &iwl2000_ht_params, -}; - -MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK)); -MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK)); -MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c deleted file mode 100644 index 8e26bc825f23..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ /dev/null @@ -1,164 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-cfg.h" -#include "iwl-agn-hw.h" -#include "iwl-csr.h" - -/* Highest firmware API version supported */ -#define IWL5000_UCODE_API_MAX 5 -#define IWL5150_UCODE_API_MAX 2 - -/* Oldest version we won't warn about */ -#define IWL5000_UCODE_API_OK 5 -#define IWL5150_UCODE_API_OK 2 - -/* Lowest firmware API version supported */ -#define IWL5000_UCODE_API_MIN 1 -#define IWL5150_UCODE_API_MIN 1 - -/* EEPROM versions */ -#define EEPROM_5000_TX_POWER_VERSION (4) -#define EEPROM_5000_EEPROM_VERSION (0x11A) -#define EEPROM_5050_TX_POWER_VERSION (4) -#define EEPROM_5050_EEPROM_VERSION (0x21E) - -#define IWL5000_FW_PRE "iwlwifi-5000-" -#define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode" - -#define IWL5150_FW_PRE "iwlwifi-5150-" -#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode" - -static const struct iwl_base_params iwl5000_base_params = { - .eeprom_size = IWLAGN_EEPROM_IMG_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, - .led_compensation = 51, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .wd_timeout = IWL_WATCHHDOG_DISABLED, - .max_event_log_size = 512, - .no_idle_support = true, -}; - -static const struct iwl_ht_params iwl5000_ht_params = { - .ht_greenfield_support = true, -}; - -#define IWL_DEVICE_5000 \ - .fw_name_pre = IWL5000_FW_PRE, \ - .ucode_api_max = IWL5000_UCODE_API_MAX, \ - .ucode_api_ok = IWL5000_UCODE_API_OK, \ - .ucode_api_min = IWL5000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_5000, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_5000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ - .base_params = &iwl5000_base_params, \ - .led_mode = IWL_LED_BLINK - -const struct iwl_cfg iwl5300_agn_cfg = { - .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", - IWL_DEVICE_5000, - /* at least EEPROM 0x11A has wrong info */ - .valid_tx_ant = ANT_ABC, /* .cfg overwrite */ - .valid_rx_ant = ANT_ABC, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; - -const struct iwl_cfg iwl5100_bgn_cfg = { - .name = "Intel(R) WiFi Link 5100 BGN", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; - -const struct iwl_cfg iwl5100_abg_cfg = { - .name = "Intel(R) WiFi Link 5100 ABG", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ -}; - -const struct iwl_cfg iwl5100_agn_cfg = { - .name = "Intel(R) WiFi Link 5100 AGN", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; - -const struct iwl_cfg iwl5350_agn_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", - .fw_name_pre = IWL5000_FW_PRE, - .ucode_api_max = IWL5000_UCODE_API_MAX, - .ucode_api_ok = IWL5000_UCODE_API_OK, - .ucode_api_min = IWL5000_UCODE_API_MIN, - .device_family = IWL_DEVICE_FAMILY_5000, - .max_inst_size = IWLAGN_RTC_INST_SIZE, - .max_data_size = IWLAGN_RTC_DATA_SIZE, - .eeprom_ver = EEPROM_5050_EEPROM_VERSION, - .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, - .base_params = &iwl5000_base_params, - .ht_params = &iwl5000_ht_params, - .led_mode = IWL_LED_BLINK, - .internal_wimax_coex = true, -}; - -#define IWL_DEVICE_5150 \ - .fw_name_pre = IWL5150_FW_PRE, \ - .ucode_api_max = IWL5150_UCODE_API_MAX, \ - .ucode_api_ok = IWL5150_UCODE_API_OK, \ - .ucode_api_min = IWL5150_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_5150, \ - .max_inst_size = IWLAGN_RTC_INST_SIZE, \ - .max_data_size = IWLAGN_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_5050_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ - .base_params = &iwl5000_base_params, \ - .no_xtal_calib = true, \ - .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true - -const struct iwl_cfg iwl5150_agn_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", - IWL_DEVICE_5150, - .ht_params = &iwl5000_ht_params, - -}; - -const struct iwl_cfg iwl5150_abg_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", - IWL_DEVICE_5150, -}; - -MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c deleted file mode 100644 index 19f7ee84ae89..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ /dev/null @@ -1,363 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include "iwl-config.h" -#include "iwl-cfg.h" -#include "iwl-agn-hw.h" -#include "iwl-commands.h" /* needed for BT for now */ - -/* Highest firmware API version supported */ -#define IWL6000_UCODE_API_MAX 6 -#define IWL6050_UCODE_API_MAX 5 -#define IWL6000G2_UCODE_API_MAX 6 - -/* Oldest version we won't warn about */ -#define IWL6000_UCODE_API_OK 4 -#define IWL6000G2_UCODE_API_OK 5 -#define IWL6050_UCODE_API_OK 5 -#define IWL6000G2B_UCODE_API_OK 6 - -/* Lowest firmware API version supported */ -#define IWL6000_UCODE_API_MIN 4 -#define IWL6050_UCODE_API_MIN 4 -#define IWL6000G2_UCODE_API_MIN 4 - -/* EEPROM versions */ -#define EEPROM_6000_TX_POWER_VERSION (4) -#define EEPROM_6000_EEPROM_VERSION (0x423) -#define EEPROM_6050_TX_POWER_VERSION (4) -#define EEPROM_6050_EEPROM_VERSION (0x532) -#define EEPROM_6150_TX_POWER_VERSION (6) -#define EEPROM_6150_EEPROM_VERSION (0x553) -#define EEPROM_6005_TX_POWER_VERSION (6) -#define EEPROM_6005_EEPROM_VERSION (0x709) -#define EEPROM_6030_TX_POWER_VERSION (6) -#define EEPROM_6030_EEPROM_VERSION (0x709) -#define EEPROM_6035_TX_POWER_VERSION (6) -#define EEPROM_6035_EEPROM_VERSION (0x753) - -#define IWL6000_FW_PRE "iwlwifi-6000-" -#define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode" - -#define IWL6050_FW_PRE "iwlwifi-6050-" -#define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode" - -#define IWL6005_FW_PRE "iwlwifi-6000g2a-" -#define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode" - -#define IWL6030_FW_PRE "iwlwifi-6000g2b-" -#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode" - -static const struct iwl_base_params iwl6000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_6x00, - .shadow_ram_support = true, - .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .wd_timeout = IWL_DEF_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ -}; - -static const struct iwl_base_params iwl6050_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_6x50, - .shadow_ram_support = true, - .led_compensation = 51, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1500, - .wd_timeout = IWL_DEF_WD_TIMEOUT, - .max_event_log_size = 1024, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ -}; - -static const struct iwl_base_params iwl6000_g2_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, - .num_of_queues = IWLAGN_NUM_QUEUES, - .pll_cfg_val = 0, - .max_ll_items = OTP_MAX_LL_ITEMS_6x00, - .shadow_ram_support = true, - .led_compensation = 57, - .adv_thermal_throttle = true, - .support_ct_kill_exit = true, - .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, - .chain_noise_scale = 1000, - .wd_timeout = IWL_LONG_WD_TIMEOUT, - .max_event_log_size = 512, - .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ -}; - -static const struct iwl_ht_params iwl6000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ -}; - -static const struct iwl_bt_params iwl6000_bt_params = { - /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ - .advanced_bt_coexist = true, - .agg_time_limit = BT_AGG_THRESHOLD_DEF, - .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, - .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, - .bt_sco_disable = true, -}; - -#define IWL_DEVICE_6005 \ - .fw_name_pre = IWL6005_FW_PRE, \ - .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ - .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6005, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_6005_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ - .base_params = &iwl6000_g2_base_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE - -const struct iwl_cfg iwl6005_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG", - IWL_DEVICE_6005, -}; - -const struct iwl_cfg iwl6005_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 BG", - IWL_DEVICE_6005, -}; - -const struct iwl_cfg iwl6005_2agn_sff_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2agn_d_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2agn_mow1_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2agn_mow2_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -#define IWL_DEVICE_6030 \ - .fw_name_pre = IWL6030_FW_PRE, \ - .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ - .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6030, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_6030_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ - .base_params = &iwl6000_g2_base_params, \ - .bt_params = &iwl6000_bt_params, \ - .need_temp_offset_calib = true, \ - .led_mode = IWL_LED_RF_STATE, \ - .adv_pm = true \ - -const struct iwl_cfg iwl6030_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6030_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG", - IWL_DEVICE_6030, -}; - -const struct iwl_cfg iwl6030_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6030_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 BG", - IWL_DEVICE_6030, -}; - -const struct iwl_cfg iwl6035_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl1030_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl1030_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1030 BG", - IWL_DEVICE_6030, -}; - -const struct iwl_cfg iwl130_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 130 BGN", - IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, - .rx_with_siso_diversity = true, -}; - -const struct iwl_cfg iwl130_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 130 BG", - IWL_DEVICE_6030, - .rx_with_siso_diversity = true, -}; - -/* - * "i": Internal configuration, use internal Power Amplifier - */ -#define IWL_DEVICE_6000i \ - .fw_name_pre = IWL6000_FW_PRE, \ - .ucode_api_max = IWL6000_UCODE_API_MAX, \ - .ucode_api_ok = IWL6000_UCODE_API_OK, \ - .ucode_api_min = IWL6000_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6000i, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ - .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ - .eeprom_ver = EEPROM_6000_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ - .base_params = &iwl6000_base_params, \ - .led_mode = IWL_LED_BLINK - -const struct iwl_cfg iwl6000i_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", - IWL_DEVICE_6000i, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6000i_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", - IWL_DEVICE_6000i, -}; - -const struct iwl_cfg iwl6000i_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", - IWL_DEVICE_6000i, -}; - -#define IWL_DEVICE_6050 \ - .fw_name_pre = IWL6050_FW_PRE, \ - .ucode_api_max = IWL6050_UCODE_API_MAX, \ - .ucode_api_min = IWL6050_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6050, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \ - .eeprom_ver = EEPROM_6050_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ - .base_params = &iwl6050_base_params, \ - .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true - -const struct iwl_cfg iwl6050_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", - IWL_DEVICE_6050, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6050_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", - IWL_DEVICE_6050, -}; - -#define IWL_DEVICE_6150 \ - .fw_name_pre = IWL6050_FW_PRE, \ - .ucode_api_max = IWL6050_UCODE_API_MAX, \ - .ucode_api_min = IWL6050_UCODE_API_MIN, \ - .device_family = IWL_DEVICE_FAMILY_6150, \ - .max_inst_size = IWL60_RTC_INST_SIZE, \ - .max_data_size = IWL60_RTC_DATA_SIZE, \ - .eeprom_ver = EEPROM_6150_EEPROM_VERSION, \ - .eeprom_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ - .base_params = &iwl6050_base_params, \ - .led_mode = IWL_LED_BLINK, \ - .internal_wimax_coex = true - -const struct iwl_cfg iwl6150_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", - IWL_DEVICE_6150, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6150_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG", - IWL_DEVICE_6150, -}; - -const struct iwl_cfg iwl6000_3agn_cfg = { - .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", - .fw_name_pre = IWL6000_FW_PRE, - .ucode_api_max = IWL6000_UCODE_API_MAX, - .ucode_api_ok = IWL6000_UCODE_API_OK, - .ucode_api_min = IWL6000_UCODE_API_MIN, - .device_family = IWL_DEVICE_FAMILY_6000, - .max_inst_size = IWL60_RTC_INST_SIZE, - .max_data_size = IWL60_RTC_DATA_SIZE, - .eeprom_ver = EEPROM_6000_EEPROM_VERSION, - .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, - .base_params = &iwl6000_base_params, - .ht_params = &iwl6000_ht_params, - .led_mode = IWL_LED_BLINK, -}; - -MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK)); -MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-cfg.h b/drivers/net/wireless/iwlwifi/iwl-cfg.h deleted file mode 100644 index 82152311d73b..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-cfg.h +++ /dev/null @@ -1,113 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __iwl_pci_h__ -#define __iwl_pci_h__ - - -/* - * This file declares the config structures for all devices. - */ - -extern const struct iwl_cfg iwl5300_agn_cfg; -extern const struct iwl_cfg iwl5100_agn_cfg; -extern const struct iwl_cfg iwl5350_agn_cfg; -extern const struct iwl_cfg iwl5100_bgn_cfg; -extern const struct iwl_cfg iwl5100_abg_cfg; -extern const struct iwl_cfg iwl5150_agn_cfg; -extern const struct iwl_cfg iwl5150_abg_cfg; -extern const struct iwl_cfg iwl6005_2agn_cfg; -extern const struct iwl_cfg iwl6005_2abg_cfg; -extern const struct iwl_cfg iwl6005_2bg_cfg; -extern const struct iwl_cfg iwl6005_2agn_sff_cfg; -extern const struct iwl_cfg iwl6005_2agn_d_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; -extern const struct iwl_cfg iwl1030_bgn_cfg; -extern const struct iwl_cfg iwl1030_bg_cfg; -extern const struct iwl_cfg iwl6030_2agn_cfg; -extern const struct iwl_cfg iwl6030_2abg_cfg; -extern const struct iwl_cfg iwl6030_2bgn_cfg; -extern const struct iwl_cfg iwl6030_2bg_cfg; -extern const struct iwl_cfg iwl6000i_2agn_cfg; -extern const struct iwl_cfg iwl6000i_2abg_cfg; -extern const struct iwl_cfg iwl6000i_2bg_cfg; -extern const struct iwl_cfg iwl6000_3agn_cfg; -extern const struct iwl_cfg iwl6050_2agn_cfg; -extern const struct iwl_cfg iwl6050_2abg_cfg; -extern const struct iwl_cfg iwl6150_bgn_cfg; -extern const struct iwl_cfg iwl6150_bg_cfg; -extern const struct iwl_cfg iwl1000_bgn_cfg; -extern const struct iwl_cfg iwl1000_bg_cfg; -extern const struct iwl_cfg iwl100_bgn_cfg; -extern const struct iwl_cfg iwl100_bg_cfg; -extern const struct iwl_cfg iwl130_bgn_cfg; -extern const struct iwl_cfg iwl130_bg_cfg; -extern const struct iwl_cfg iwl2000_2bgn_cfg; -extern const struct iwl_cfg iwl2000_2bgn_d_cfg; -extern const struct iwl_cfg iwl2030_2bgn_cfg; -extern const struct iwl_cfg iwl6035_2agn_cfg; -extern const struct iwl_cfg iwl105_bgn_cfg; -extern const struct iwl_cfg iwl105_bgn_d_cfg; -extern const struct iwl_cfg iwl135_bgn_cfg; - -#endif /* __iwl_pci_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.c b/drivers/net/wireless/iwlwifi/iwl-pci.c deleted file mode 100644 index 0c8a1c2d8847..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-pci.c +++ /dev/null @@ -1,379 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include - -#include "iwl-trans.h" -#include "iwl-cfg.h" -#include "iwl-drv.h" -#include "iwl-trans.h" -#include "iwl-trans-pcie-int.h" - -#define IWL_PCI_DEVICE(dev, subdev, cfg) \ - .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ - .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ - .driver_data = (kernel_ulong_t)&(cfg) - -/* Hardware specific file defines the PCI IDs table for that hardware module */ -static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { - {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ - -/* 5300 Series WiFi */ - {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ - -/* 5350 Series WiFi/WiMax */ - {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ - -/* 5150 Series Wifi/WiMax */ - {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ - - {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ - -/* 6x00 Series */ - {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, - {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, - -/* 6x05 Series */ - {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */ - {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */ - -/* 6x30 Series */ - {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)}, - -/* 6x50 WiFi/WiMax Series */ - {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, - -/* 6150 WiFi/WiMax Series */ - {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)}, - {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)}, - -/* 1000 Series WiFi */ - {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, - -/* 100 Series WiFi */ - {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)}, - {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)}, - -/* 130 Series WiFi */ - {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)}, - {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)}, - -/* 2x00 Series */ - {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, - -/* 2x30 Series */ - {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, - -/* 6x35 Series */ - {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, - -/* 105 Series */ - {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, - -/* 135 Series */ - {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, - - {0} -}; -MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); - -/* PCI registers */ -#define PCI_CFG_RETRY_TIMEOUT 0x041 - -#ifndef CONFIG_IWLWIFI_IDI - -static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); - struct iwl_trans *iwl_trans; - struct iwl_trans_pcie *trans_pcie; - - iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); - if (iwl_trans == NULL) - return -ENOMEM; - - pci_set_drvdata(pdev, iwl_trans); - - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); - trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); - if (!trans_pcie->drv) - goto out_free_trans; - - return 0; - -out_free_trans: - iwl_trans_pcie_free(iwl_trans); - pci_set_drvdata(pdev, NULL); - return -EFAULT; -} - -static void __devexit iwl_pci_remove(struct pci_dev *pdev) -{ - struct iwl_trans *trans = pci_get_drvdata(pdev); - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - iwl_drv_stop(trans_pcie->drv); - iwl_trans_pcie_free(trans); - - pci_set_drvdata(pdev, NULL); -} - -#endif /* CONFIG_IWLWIFI_IDI */ - -#ifdef CONFIG_PM_SLEEP - -static int iwl_pci_suspend(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); - - /* Before you put code here, think about WoWLAN. You cannot check here - * whether WoWLAN is enabled or not, and your code will run even if - * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx. - */ - - return iwl_trans_suspend(iwl_trans); -} - -static int iwl_pci_resume(struct device *device) -{ - struct pci_dev *pdev = to_pci_dev(device); - struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); - - /* Before you put code here, think about WoWLAN. You cannot check here - * whether WoWLAN is enabled or not, and your code will run even if - * WoWLAN is enabled - the NIC may be alive. - */ - - /* - * We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state. - */ - pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - - return iwl_trans_resume(iwl_trans); -} - -static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); - -#define IWL_PM_OPS (&iwl_dev_pm_ops) - -#else - -#define IWL_PM_OPS NULL - -#endif - -#ifdef CONFIG_IWLWIFI_IDI -/* - * Defined externally in iwl-idi.c - */ -int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); -void __devexit iwl_pci_remove(struct pci_dev *pdev); - -#endif /* CONFIG_IWLWIFI_IDI */ - -static struct pci_driver iwl_pci_driver = { - .name = DRV_NAME, - .id_table = iwl_hw_card_ids, - .probe = iwl_pci_probe, - .remove = __devexit_p(iwl_pci_remove), - .driver.pm = IWL_PM_OPS, -}; - -int __must_check iwl_pci_register_driver(void) -{ - int ret; - ret = pci_register_driver(&iwl_pci_driver); - if (ret) - pr_err("Unable to initialize PCI module\n"); - - return ret; -} - -void iwl_pci_unregister_driver(void) -{ - pci_unregister_driver(&iwl_pci_driver); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h deleted file mode 100644 index f027769933d9..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h +++ /dev/null @@ -1,444 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#ifndef __iwl_trans_int_pcie_h__ -#define __iwl_trans_int_pcie_h__ - -#include -#include -#include -#include -#include -#include - -#include "iwl-fh.h" -#include "iwl-csr.h" -#include "iwl-trans.h" -#include "iwl-debug.h" -#include "iwl-io.h" -#include "iwl-op-mode.h" - -struct iwl_host_cmd; - -/*This file includes the declaration that are internal to the - * trans_pcie layer */ - -struct iwl_rx_mem_buffer { - dma_addr_t page_dma; - struct page *page; - struct list_head list; -}; - -/** - * struct isr_statistics - interrupt statistics - * - */ -struct isr_statistics { - u32 hw; - u32 sw; - u32 err_code; - u32 sch; - u32 alive; - u32 rfkill; - u32 ctkill; - u32 wakeup; - u32 rx; - u32 tx; - u32 unhandled; -}; - -/** - * struct iwl_rx_queue - Rx queue - * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) - * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) - * @pool: - * @queue: - * @read: Shared index to newest available Rx buffer - * @write: Shared index to oldest written Rx packet - * @free_count: Number of pre-allocated buffers in rx_free - * @write_actual: - * @rx_free: list of free SKBs for use - * @rx_used: List of Rx buffers with no SKB - * @need_update: flag to indicate we need to update read/write index - * @rb_stts: driver's pointer to receive buffer status - * @rb_stts_dma: bus address of receive buffer status - * @lock: - * - * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers - */ -struct iwl_rx_queue { - __le32 *bd; - dma_addr_t bd_dma; - struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; - struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; - u32 read; - u32 write; - u32 free_count; - u32 write_actual; - struct list_head rx_free; - struct list_head rx_used; - int need_update; - struct iwl_rb_status *rb_stts; - dma_addr_t rb_stts_dma; - spinlock_t lock; -}; - -struct iwl_dma_ptr { - dma_addr_t dma; - void *addr; - size_t size; -}; - -/** - * iwl_queue_inc_wrap - increment queue index, wrap back to beginning - * @index -- current index - * @n_bd -- total number of entries in queue (must be power of 2) - */ -static inline int iwl_queue_inc_wrap(int index, int n_bd) -{ - return ++index & (n_bd - 1); -} - -/** - * iwl_queue_dec_wrap - decrement queue index, wrap back to end - * @index -- current index - * @n_bd -- total number of entries in queue (must be power of 2) - */ -static inline int iwl_queue_dec_wrap(int index, int n_bd) -{ - return --index & (n_bd - 1); -} - -struct iwl_cmd_meta { - /* only for SYNC commands, iff the reply skb is wanted */ - struct iwl_host_cmd *source; - - DEFINE_DMA_UNMAP_ADDR(mapping); - DEFINE_DMA_UNMAP_LEN(len); - - u32 flags; -}; - -/* - * Generic queue structure - * - * Contains common data for Rx and Tx queues. - * - * Note the difference between n_bd and n_window: the hardware - * always assumes 256 descriptors, so n_bd is always 256 (unless - * there might be HW changes in the future). For the normal TX - * queues, n_window, which is the size of the software queue data - * is also 256; however, for the command queue, n_window is only - * 32 since we don't need so many commands pending. Since the HW - * still uses 256 BDs for DMA though, n_bd stays 256. As a result, - * the software buffers (in the variables @meta, @txb in struct - * iwl_tx_queue) only have 32 entries, while the HW buffers (@tfds - * in the same struct) have 256. - * This means that we end up with the following: - * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | - * SW entries: | 0 | ... | 31 | - * where N is a number between 0 and 7. This means that the SW - * data is a window overlayed over the HW queue. - */ -struct iwl_queue { - int n_bd; /* number of BDs in this queue */ - int write_ptr; /* 1-st empty entry (index) host_w*/ - int read_ptr; /* last used entry (index) host_r*/ - /* use for monitoring and recovering the stuck queue */ - dma_addr_t dma_addr; /* physical addr for BD's */ - int n_window; /* safe queue window */ - u32 id; - int low_mark; /* low watermark, resume queue if free - * space more than this */ - int high_mark; /* high watermark, stop queue if free - * space less than this */ -}; - -#define TFD_TX_CMD_SLOTS 256 -#define TFD_CMD_SLOTS 32 - -struct iwl_pcie_tx_queue_entry { - struct iwl_device_cmd *cmd; - struct sk_buff *skb; - struct iwl_cmd_meta meta; -}; - -/** - * struct iwl_tx_queue - Tx Queue for DMA - * @q: generic Rx/Tx queue descriptor - * @tfds: transmit frame descriptors (DMA memory) - * @entries: transmit entries (driver state) - * @lock: queue lock - * @stuck_timer: timer that fires if queue gets stuck - * @trans_pcie: pointer back to transport (for timer) - * @need_update: indicates need to update read/write index - * @active: stores if queue is active - * - * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame - * descriptors) and required locking structures. - */ -struct iwl_tx_queue { - struct iwl_queue q; - struct iwl_tfd *tfds; - struct iwl_pcie_tx_queue_entry *entries; - spinlock_t lock; - struct timer_list stuck_timer; - struct iwl_trans_pcie *trans_pcie; - u8 need_update; - u8 active; -}; - -/** - * struct iwl_trans_pcie - PCIe transport specific data - * @rxq: all the RX queue data - * @rx_replenish: work that will be called when buffers need to be allocated - * @drv - pointer to iwl_drv - * @trans: pointer to the generic transport area - * @irq - the irq number for the device - * @irq_requested: true when the irq has been requested - * @scd_base_addr: scheduler sram base address in SRAM - * @scd_bc_tbls: pointer to the byte count table of the scheduler - * @kw: keep warm address - * @pci_dev: basic pci-network driver stuff - * @hw_base: pci hardware address support - * @ucode_write_complete: indicates that the ucode has been copied. - * @ucode_write_waitq: wait queue for uCode load - * @status - transport specific status flags - * @cmd_queue - command queue number - * @rx_buf_size_8k: 8 kB RX buffer size - * @rx_page_order: page order for receive buffer size - * @wd_timeout: queue watchdog timeout (jiffies) - */ -struct iwl_trans_pcie { - struct iwl_rx_queue rxq; - struct work_struct rx_replenish; - struct iwl_trans *trans; - struct iwl_drv *drv; - - /* INT ICT Table */ - __le32 *ict_tbl; - dma_addr_t ict_tbl_dma; - int ict_index; - u32 inta; - bool use_ict; - bool irq_requested; - struct tasklet_struct irq_tasklet; - struct isr_statistics isr_stats; - - unsigned int irq; - spinlock_t irq_lock; - u32 inta_mask; - u32 scd_base_addr; - struct iwl_dma_ptr scd_bc_tbls; - struct iwl_dma_ptr kw; - - struct iwl_tx_queue *txq; - unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - - /* PCI bus related data */ - struct pci_dev *pci_dev; - void __iomem *hw_base; - - bool ucode_write_complete; - wait_queue_head_t ucode_write_waitq; - unsigned long status; - u8 cmd_queue; - u8 n_no_reclaim_cmds; - u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; - u8 setup_q_to_fifo[IWL_MAX_HW_QUEUES]; - u8 n_q_to_fifo; - - bool rx_buf_size_8k; - u32 rx_page_order; - - const char **command_names; - - /* queue watchdog */ - unsigned long wd_timeout; -}; - -/***************************************************** -* DRIVER STATUS FUNCTIONS -******************************************************/ -#define STATUS_HCMD_ACTIVE 0 -#define STATUS_DEVICE_ENABLED 1 -#define STATUS_TPOWER_PMI 2 -#define STATUS_INT_ENABLED 3 - -#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ - ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) - -static inline struct iwl_trans * -iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) -{ - return container_of((void *)trans_pcie, struct iwl_trans, - trans_specific); -} - -struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, - const struct pci_device_id *ent, - const struct iwl_cfg *cfg); -void iwl_trans_pcie_free(struct iwl_trans *trans); - -/***************************************************** -* RX -******************************************************/ -void iwl_bg_rx_replenish(struct work_struct *data); -void iwl_irq_tasklet(struct iwl_trans *trans); -void iwlagn_rx_replenish(struct iwl_trans *trans); -void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, - struct iwl_rx_queue *q); - -/***************************************************** -* ICT -******************************************************/ -void iwl_reset_ict(struct iwl_trans *trans); -void iwl_disable_ict(struct iwl_trans *trans); -int iwl_alloc_isr_ict(struct iwl_trans *trans); -void iwl_free_isr_ict(struct iwl_trans *trans); -irqreturn_t iwl_isr_ict(int irq, void *data); - -/***************************************************** -* TX / HCMD -******************************************************/ -void iwl_txq_update_write_ptr(struct iwl_trans *trans, - struct iwl_tx_queue *txq); -int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset); -int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id); -int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); -void iwl_tx_cmd_complete(struct iwl_trans *trans, - struct iwl_rx_cmd_buffer *rxb, int handler_status); -void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - u16 byte_cnt); -void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int queue); -void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); -void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - int tx_fifo_id, bool active); -void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn); -void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - enum dma_data_direction dma_dir); -int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, - struct sk_buff_head *skbs); -int iwl_queue_space(const struct iwl_queue *q); - -/***************************************************** -* Error handling -******************************************************/ -int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display); -void iwl_dump_csr(struct iwl_trans *trans); - -/***************************************************** -* Helpers -******************************************************/ -static inline void iwl_disable_interrupts(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); - - /* disable interrupts from uCode/NIC to host */ - iwl_write32(trans, CSR_INT_MASK, 0x00000000); - - /* acknowledge/clear/reset any interrupts still pending - * from uCode or flow handler (Rx/Tx DMA) */ - iwl_write32(trans, CSR_INT, 0xffffffff); - iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); - IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); -} - -static inline void iwl_enable_interrupts(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &trans_pcie->status); - iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); -} - -static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) -{ - IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); - iwl_write32(trans, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); -} - -static inline void iwl_wake_queue(struct iwl_trans *trans, - struct iwl_tx_queue *txq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) { - IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id); - iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id); - } -} - -static inline void iwl_stop_queue(struct iwl_trans *trans, - struct iwl_tx_queue *txq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) { - iwl_op_mode_queue_full(trans->op_mode, txq->q.id); - IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id); - } else - IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n", - txq->q.id); -} - -static inline int iwl_queue_used(const struct iwl_queue *q, int i) -{ - return q->write_ptr >= q->read_ptr ? - (i >= q->read_ptr && i < q->write_ptr) : - !(i < q->read_ptr && i >= q->write_ptr); -} - -static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) -{ - return index & (q->n_window - 1); -} - -static inline const char * -trans_pcie_get_cmd_string(struct iwl_trans_pcie *trans_pcie, u8 cmd) -{ - if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) - return "UNKNOWN"; - return trans_pcie->command_names[cmd]; -} - -static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) -{ - return !(iwl_read32(trans, CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); -} - -#endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c deleted file mode 100644 index 98605fc7ad37..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ /dev/null @@ -1,1058 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include - -#include "iwl-prph.h" -#include "iwl-io.h" -#include "iwl-trans-pcie-int.h" -#include "iwl-op-mode.h" - -#ifdef CONFIG_IWLWIFI_IDI -#include "iwl-amfh.h" -#endif - -/****************************************************************************** - * - * RX path functions - * - ******************************************************************************/ - -/* - * Rx theory of operation - * - * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), - * each of which point to Receive Buffers to be filled by the NIC. These get - * used not only for Rx frames, but for any command response or notification - * from the NIC. The driver and NIC manage the Rx buffers by means - * of indexes into the circular buffer. - * - * Rx Queue Indexes - * The host/firmware share two index registers for managing the Rx buffers. - * - * The READ index maps to the first position that the firmware may be writing - * to -- the driver can read up to (but not including) this position and get - * good data. - * The READ index is managed by the firmware once the card is enabled. - * - * The WRITE index maps to the last position the driver has read from -- the - * position preceding WRITE is the last slot the firmware can place a packet. - * - * The queue is empty (no good data) if WRITE = READ - 1, and is full if - * WRITE = READ. - * - * During initialization, the host sets up the READ queue position to the first - * INDEX position, and WRITE to the last (READ - 1 wrapped) - * - * When the firmware places a packet in a buffer, it will advance the READ index - * and fire the RX interrupt. The driver can then query the READ index and - * process as many packets as possible, moving the WRITE index forward as it - * resets the Rx queue buffers with new memory. - * - * The management in the driver is as follows: - * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When - * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled - * to replenish the iwl->rxq->rx_free. - * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the - * iwl->rxq is replenished and the READ INDEX is updated (updating the - * 'processed' and 'read' driver indexes as well) - * + A received packet is processed and handed to the kernel network stack, - * detached from the iwl->rxq. The driver 'processed' index is updated. - * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free - * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ - * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there - * were enough free buffers and RX_STALLED is set it is cleared. - * - * - * Driver sequence: - * - * iwl_rx_queue_alloc() Allocates rx_free - * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls - * iwl_rx_queue_restock - * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx - * queue, updates firmware pointers, and updates - * the WRITE index. If insufficient rx_free buffers - * are available, schedules iwl_rx_replenish - * - * -- enable interrupts -- - * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the - * READ INDEX, detaching the SKB from the pool. - * Moves the packet buffer from queue to rx_used. - * Calls iwl_rx_queue_restock to refill any empty - * slots. - * ... - * - */ - -/** - * iwl_rx_queue_space - Return number of free slots available in queue. - */ -static int iwl_rx_queue_space(const struct iwl_rx_queue *q) -{ - int s = q->read - q->write; - if (s <= 0) - s += RX_QUEUE_SIZE; - /* keep some buffer to not confuse full and empty queue */ - s -= 2; - if (s < 0) - s = 0; - return s; -} - -/** - * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue - */ -void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, - struct iwl_rx_queue *q) -{ - unsigned long flags; - u32 reg; - - spin_lock_irqsave(&q->lock, flags); - - if (q->need_update == 0) - goto exit_unlock; - - if (trans->cfg->base_params->shadow_reg_enable) { - /* shadow register enabled */ - /* Device expects a multiple of 8 */ - q->write_actual = (q->write & ~0x7); - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual); - } else { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - /* If power-saving is in use, make sure device is awake */ - if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, - "Rx queue requesting wakeup," - " GP1 = 0x%x\n", reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - goto exit_unlock; - } - - q->write_actual = (q->write & ~0x7); - iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, - q->write_actual); - - /* Else device is assumed to be awake */ - } else { - /* Device expects a multiple of 8 */ - q->write_actual = (q->write & ~0x7); - iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, - q->write_actual); - } - } - q->need_update = 0; - - exit_unlock: - spin_unlock_irqrestore(&q->lock, flags); -} - -/** - * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr - */ -static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) -{ - return cpu_to_le32((u32)(dma_addr >> 8)); -} - -/** - * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool - * - * If there are slots in the RX queue that need to be restocked, - * and we have free pre-allocated buffers, fill the ranks as much - * as we can, pulling from rx_free. - * - * This moves the 'write' index forward to catch up with 'processed', and - * also updates the memory address in the firmware to reference the new - * target buffer. - */ -static void iwlagn_rx_queue_restock(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct list_head *element; - struct iwl_rx_mem_buffer *rxb; - unsigned long flags; - - spin_lock_irqsave(&rxq->lock, flags); - while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { - /* The overwritten rxb must be a used one */ - rxb = rxq->queue[rxq->write]; - BUG_ON(rxb && rxb->page); - - /* Get next free Rx buffer, remove from free list */ - element = rxq->rx_free.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); - - /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma); - rxq->queue[rxq->write] = rxb; - rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; - rxq->free_count--; - } - spin_unlock_irqrestore(&rxq->lock, flags); - /* If the pre-allocated buffer pool is dropping low, schedule to - * refill it */ - if (rxq->free_count <= RX_LOW_WATERMARK) - schedule_work(&trans_pcie->rx_replenish); - - - /* If we've added more space for the firmware to place data, tell it. - * Increment device's write pointer in multiples of 8. */ - if (rxq->write_actual != (rxq->write & ~0x7)) { - spin_lock_irqsave(&rxq->lock, flags); - rxq->need_update = 1; - spin_unlock_irqrestore(&rxq->lock, flags); - iwl_rx_queue_update_write_ptr(trans, rxq); - } -} - -/** - * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free - * - * When moving to rx_free an SKB is allocated for the slot. - * - * Also restock the Rx queue via iwl_rx_queue_restock. - * This is called as a scheduled work item (except for during initialization) - */ -static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct list_head *element; - struct iwl_rx_mem_buffer *rxb; - struct page *page; - unsigned long flags; - gfp_t gfp_mask = priority; - - while (1) { - spin_lock_irqsave(&rxq->lock, flags); - if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); - return; - } - spin_unlock_irqrestore(&rxq->lock, flags); - - if (rxq->free_count > RX_LOW_WATERMARK) - gfp_mask |= __GFP_NOWARN; - - if (trans_pcie->rx_page_order > 0) - gfp_mask |= __GFP_COMP; - - /* Alloc a new receive buffer */ - page = alloc_pages(gfp_mask, trans_pcie->rx_page_order); - if (!page) { - if (net_ratelimit()) - IWL_DEBUG_INFO(trans, "alloc_pages failed, " - "order: %d\n", - trans_pcie->rx_page_order); - - if ((rxq->free_count <= RX_LOW_WATERMARK) && - net_ratelimit()) - IWL_CRIT(trans, "Failed to alloc_pages with %s." - "Only %u free buffers remaining.\n", - priority == GFP_ATOMIC ? - "GFP_ATOMIC" : "GFP_KERNEL", - rxq->free_count); - /* We don't reschedule replenish work here -- we will - * call the restock method and if it still needs - * more buffers it will schedule replenish */ - return; - } - - spin_lock_irqsave(&rxq->lock, flags); - - if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); - __free_pages(page, trans_pcie->rx_page_order); - return; - } - element = rxq->rx_used.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); - - spin_unlock_irqrestore(&rxq->lock, flags); - - BUG_ON(rxb->page); - rxb->page = page; - /* Get physical address of the RB */ - rxb->page_dma = - dma_map_page(trans->dev, page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - /* dma address must be no more than 36 bits */ - BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); - /* and also 256 byte aligned! */ - BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); - - spin_lock_irqsave(&rxq->lock, flags); - - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - - spin_unlock_irqrestore(&rxq->lock, flags); - } -} - -void iwlagn_rx_replenish(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - iwlagn_rx_allocate(trans, GFP_KERNEL); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwlagn_rx_queue_restock(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); -} - -static void iwlagn_rx_replenish_now(struct iwl_trans *trans) -{ - iwlagn_rx_allocate(trans, GFP_ATOMIC); - - iwlagn_rx_queue_restock(trans); -} - -void iwl_bg_rx_replenish(struct work_struct *data) -{ - struct iwl_trans_pcie *trans_pcie = - container_of(data, struct iwl_trans_pcie, rx_replenish); - - iwlagn_rx_replenish(trans_pcie->trans); -} - -static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, - struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - unsigned long flags; - bool page_stolen = false; - int max_len = PAGE_SIZE << trans_pcie->rx_page_order; - u32 offset = 0; - - if (WARN_ON(!rxb)) - return; - - dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE); - - while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) { - struct iwl_rx_packet *pkt; - struct iwl_device_cmd *cmd; - u16 sequence; - bool reclaim; - int index, cmd_index, err, len; - struct iwl_rx_cmd_buffer rxcb = { - ._offset = offset, - ._page = rxb->page, - ._page_stolen = false, - .truesize = max_len, - }; - - pkt = rxb_addr(&rxcb); - - if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) - break; - - IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n", - rxcb._offset, - trans_pcie_get_cmd_string(trans_pcie, pkt->hdr.cmd), - pkt->hdr.cmd); - - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - len += sizeof(u32); /* account for status word */ - trace_iwlwifi_dev_rx(trans->dev, pkt, len); - - /* Reclaim a command buffer only if this packet is a response - * to a (driver-originated) command. - * If the packet (e.g. Rx frame) originated from uCode, - * there is no command buffer to reclaim. - * Ucode should set SEQ_RX_FRAME bit if ucode-originated, - * but apparently a few don't get set; catch them here. */ - reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); - if (reclaim) { - int i; - - for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { - if (trans_pcie->no_reclaim_cmds[i] == - pkt->hdr.cmd) { - reclaim = false; - break; - } - } - } - - sequence = le16_to_cpu(pkt->hdr.sequence); - index = SEQ_TO_INDEX(sequence); - cmd_index = get_cmd_index(&txq->q, index); - - if (reclaim) - cmd = txq->entries[cmd_index].cmd; - else - cmd = NULL; - - err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); - - /* - * After here, we should always check rxcb._page_stolen, - * if it is true then one of the handlers took the page. - */ - - if (reclaim) { - /* Invoke any callbacks, transfer the buffer to caller, - * and fire off the (possibly) blocking - * iwl_trans_send_cmd() - * as we reclaim the driver command queue */ - if (!rxcb._page_stolen) - iwl_tx_cmd_complete(trans, &rxcb, err); - else - IWL_WARN(trans, "Claim null rxb?\n"); - } - - page_stolen |= rxcb._page_stolen; - offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN); - } - - /* page was stolen from us -- free our reference */ - if (page_stolen) { - __free_pages(rxb->page, trans_pcie->rx_page_order); - rxb->page = NULL; - } - - /* Reuse the page if possible. For notification packets and - * SKBs that fail to Rx correctly, add them back into the - * rx_free list for reuse later. */ - spin_lock_irqsave(&rxq->lock, flags); - if (rxb->page != NULL) { - rxb->page_dma = - dma_map_page(trans->dev, rxb->page, 0, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - list_add_tail(&rxb->list, &rxq->rx_free); - rxq->free_count++; - } else - list_add_tail(&rxb->list, &rxq->rx_used); - spin_unlock_irqrestore(&rxq->lock, flags); -} - -/** - * iwl_rx_handle - Main entry function for receiving responses from uCode - * - * Uses the priv->rx_handlers callback function array to invoke - * the appropriate handlers, including command responses, - * frame-received notifications, and other notifications. - */ -static void iwl_rx_handle(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - u32 r, i; - u8 fill_rx = 0; - u32 count = 8; - int total_empty; - - /* uCode's read index (stored in shared DRAM) indicates the last Rx - * buffer that the driver may process (last buffer filled by ucode). */ - r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; - i = rxq->read; - - /* Rx interrupt, but nothing sent from uCode */ - if (i == r) - IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); - - /* calculate total frames need to be restock after handling RX */ - total_empty = r - rxq->write_actual; - if (total_empty < 0) - total_empty += RX_QUEUE_SIZE; - - if (total_empty > (RX_QUEUE_SIZE / 2)) - fill_rx = 1; - - while (i != r) { - struct iwl_rx_mem_buffer *rxb; - - rxb = rxq->queue[i]; - rxq->queue[i] = NULL; - - IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", - r, i, rxb); - iwl_rx_handle_rxbuf(trans, rxb); - - i = (i + 1) & RX_QUEUE_MASK; - /* If there are a lot of unused frames, - * restock the Rx queue so ucode wont assert. */ - if (fill_rx) { - count++; - if (count >= 8) { - rxq->read = i; - iwlagn_rx_replenish_now(trans); - count = 0; - } - } - } - - /* Backtrack one entry */ - rxq->read = i; - if (fill_rx) - iwlagn_rx_replenish_now(trans); - else - iwlagn_rx_queue_restock(trans); -} - -/** - * iwl_irq_handle_error - called for HW or SW error interrupt from card - */ -static void iwl_irq_handle_error(struct iwl_trans *trans) -{ - /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ - if (trans->cfg->internal_wimax_coex && - (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & - APMS_CLK_VAL_MRB_FUNC_MODE) || - (iwl_read_prph(trans, APMG_PS_CTRL_REG) & - APMG_PS_CTRL_VAL_RESET_REQ))) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - iwl_op_mode_wimax_active(trans->op_mode); - wake_up(&trans->wait_command_queue); - return; - } - - iwl_dump_csr(trans); - iwl_dump_fh(trans, NULL, false); - - iwl_op_mode_nic_error(trans->op_mode); -} - -/* tasklet for iwlagn interrupt */ -void iwl_irq_tasklet(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - u32 inta = 0; - u32 handled = 0; - unsigned long flags; - u32 i; -#ifdef CONFIG_IWLWIFI_DEBUG - u32 inta_mask; -#endif - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* Ack/clear/reset pending uCode interrupts. - * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, - */ - /* There is a hardware bug in the interrupt mask function that some - * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if - * they are disabled in the CSR_INT_MASK register. Furthermore the - * ICT interrupt handling mechanism has another bug that might cause - * these unmasked interrupts fail to be detected. We workaround the - * hardware bugs here by ACKing all the possible interrupts so that - * interrupt coalescing can still be achieved. - */ - iwl_write32(trans, CSR_INT, - trans_pcie->inta | ~trans_pcie->inta_mask); - - inta = trans_pcie->inta; - -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(IWL_DL_ISR)) { - /* just for debug */ - inta_mask = iwl_read32(trans, CSR_INT_MASK); - IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", - inta, inta_mask); - } -#endif - - /* saved interrupt in inta variable now we can reset trans_pcie->inta */ - trans_pcie->inta = 0; - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - /* Now service all interrupt bits discovered above. */ - if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(trans, "Hardware error detected. Restarting.\n"); - - /* Tell the device to stop sending interrupts */ - iwl_disable_interrupts(trans); - - isr_stats->hw++; - iwl_irq_handle_error(trans); - - handled |= CSR_INT_BIT_HW_ERR; - - return; - } - -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(IWL_DL_ISR)) { - /* NIC fires this, but we don't use it, redundant with WAKEUP */ - if (inta & CSR_INT_BIT_SCD) { - IWL_DEBUG_ISR(trans, "Scheduler finished to transmit " - "the frame/frames.\n"); - isr_stats->sch++; - } - - /* Alive notification via Rx interrupt will do the real work */ - if (inta & CSR_INT_BIT_ALIVE) { - IWL_DEBUG_ISR(trans, "Alive interrupt\n"); - isr_stats->alive++; - } - } -#endif - /* Safely ignore these bits for debug checks below */ - inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); - - /* HW RF KILL switch toggled */ - if (inta & CSR_INT_BIT_RF_KILL) { - bool hw_rfkill; - - hw_rfkill = iwl_is_rfkill_set(trans); - IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", - hw_rfkill ? "disable radio" : "enable radio"); - - isr_stats->rfkill++; - - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - - handled |= CSR_INT_BIT_RF_KILL; - } - - /* Chip got too hot and stopped itself */ - if (inta & CSR_INT_BIT_CT_KILL) { - IWL_ERR(trans, "Microcode CT kill error detected.\n"); - isr_stats->ctkill++; - handled |= CSR_INT_BIT_CT_KILL; - } - - /* Error detected by uCode */ - if (inta & CSR_INT_BIT_SW_ERR) { - IWL_ERR(trans, "Microcode SW error detected. " - " Restarting 0x%X.\n", inta); - isr_stats->sw++; - iwl_irq_handle_error(trans); - handled |= CSR_INT_BIT_SW_ERR; - } - - /* uCode wakes up after power-down sleep */ - if (inta & CSR_INT_BIT_WAKEUP) { - IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); - iwl_rx_queue_update_write_ptr(trans, &trans_pcie->rxq); - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) - iwl_txq_update_write_ptr(trans, - &trans_pcie->txq[i]); - - isr_stats->wakeup++; - - handled |= CSR_INT_BIT_WAKEUP; - } - - /* All uCode command responses, including Tx command responses, - * Rx "responses" (frame-received notification), and other - * notifications from uCode come through here*/ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX | - CSR_INT_BIT_RX_PERIODIC)) { - IWL_DEBUG_ISR(trans, "Rx interrupt\n"); - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { - handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); - iwl_write32(trans, CSR_FH_INT_STATUS, - CSR_FH_INT_RX_MASK); - } - if (inta & CSR_INT_BIT_RX_PERIODIC) { - handled |= CSR_INT_BIT_RX_PERIODIC; - iwl_write32(trans, - CSR_INT, CSR_INT_BIT_RX_PERIODIC); - } - /* Sending RX interrupt require many steps to be done in the - * the device: - * 1- write interrupt to current index in ICT table. - * 2- dma RX frame. - * 3- update RX shared data to indicate last write index. - * 4- send interrupt. - * This could lead to RX race, driver could receive RX interrupt - * but the shared data changes does not reflect this; - * periodic interrupt will detect any dangling Rx activity. - */ - - /* Disable periodic interrupt; we use it as just a one-shot. */ - iwl_write8(trans, CSR_INT_PERIODIC_REG, - CSR_INT_PERIODIC_DIS); -#ifdef CONFIG_IWLWIFI_IDI - iwl_amfh_rx_handler(); -#else - iwl_rx_handle(trans); -#endif - /* - * Enable periodic interrupt in 8 msec only if we received - * real RX interrupt (instead of just periodic int), to catch - * any dangling Rx interrupt. If it was just the periodic - * interrupt, there was no dangling Rx activity, and no need - * to extend the periodic interrupt; one-shot is enough. - */ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) - iwl_write8(trans, CSR_INT_PERIODIC_REG, - CSR_INT_PERIODIC_ENA); - - isr_stats->rx++; - } - - /* This "Tx" DMA channel is used only for loading uCode */ - if (inta & CSR_INT_BIT_FH_TX) { - iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK); - IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); - isr_stats->tx++; - handled |= CSR_INT_BIT_FH_TX; - /* Wake up uCode load routine, now that load is complete */ - trans_pcie->ucode_write_complete = true; - wake_up(&trans_pcie->ucode_write_waitq); - } - - if (inta & ~handled) { - IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled); - isr_stats->unhandled++; - } - - if (inta & ~(trans_pcie->inta_mask)) { - IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n", - inta & ~trans_pcie->inta_mask); - } - - /* Re-enable all interrupts */ - /* only Re-enable if disabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status)) - iwl_enable_interrupts(trans); - /* Re-enable RF_KILL if it occurred */ - else if (handled & CSR_INT_BIT_RF_KILL) - iwl_enable_rfkill_int(trans); -} - -/****************************************************************************** - * - * ICT functions - * - ******************************************************************************/ - -/* a device (PCI-E) page is 4096 bytes long */ -#define ICT_SHIFT 12 -#define ICT_SIZE (1 << ICT_SHIFT) -#define ICT_COUNT (ICT_SIZE / sizeof(u32)) - -/* Free dram table */ -void iwl_free_isr_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (trans_pcie->ict_tbl) { - dma_free_coherent(trans->dev, ICT_SIZE, - trans_pcie->ict_tbl, - trans_pcie->ict_tbl_dma); - trans_pcie->ict_tbl = NULL; - trans_pcie->ict_tbl_dma = 0; - } -} - - -/* - * allocate dram shared table, it is an aligned memory - * block of ICT_SIZE. - * also reset all data related to ICT table interrupt. - */ -int iwl_alloc_isr_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - trans_pcie->ict_tbl = - dma_alloc_coherent(trans->dev, ICT_SIZE, - &trans_pcie->ict_tbl_dma, - GFP_KERNEL); - if (!trans_pcie->ict_tbl) - return -ENOMEM; - - /* just an API sanity check ... it is guaranteed to be aligned */ - if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { - iwl_free_isr_ict(trans); - return -EINVAL; - } - - IWL_DEBUG_ISR(trans, "ict dma addr %Lx\n", - (unsigned long long)trans_pcie->ict_tbl_dma); - - IWL_DEBUG_ISR(trans, "ict vir addr %p\n", trans_pcie->ict_tbl); - - /* reset table and index to all 0 */ - memset(trans_pcie->ict_tbl, 0, ICT_SIZE); - trans_pcie->ict_index = 0; - - /* add periodic RX interrupt */ - trans_pcie->inta_mask |= CSR_INT_BIT_RX_PERIODIC; - return 0; -} - -/* Device is going up inform it about using ICT interrupt table, - * also we need to tell the driver to start using ICT interrupt. - */ -void iwl_reset_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 val; - unsigned long flags; - - if (!trans_pcie->ict_tbl) - return; - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_disable_interrupts(trans); - - memset(trans_pcie->ict_tbl, 0, ICT_SIZE); - - val = trans_pcie->ict_tbl_dma >> ICT_SHIFT; - - val |= CSR_DRAM_INT_TBL_ENABLE; - val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; - - IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val); - - iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val); - trans_pcie->use_ict = true; - trans_pcie->ict_index = 0; - iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); - iwl_enable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); -} - -/* Device is going down disable ict interrupt usage */ -void iwl_disable_ict(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - trans_pcie->use_ict = false; - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); -} - -static irqreturn_t iwl_isr(int irq, void *data) -{ - struct iwl_trans *trans = data; - struct iwl_trans_pcie *trans_pcie; - u32 inta, inta_mask; - unsigned long flags; -#ifdef CONFIG_IWLWIFI_DEBUG - u32 inta_fh; -#endif - if (!trans) - return IRQ_NONE; - - trace_iwlwifi_dev_irq(trans->dev); - - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. */ - inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ - iwl_write32(trans, CSR_INT_MASK, 0x00000000); - - /* Discover which interrupts are active/pending */ - inta = iwl_read32(trans, CSR_INT); - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - if (!inta) { - IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); - goto none; - } - - if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { - /* Hardware disappeared. It might have already raised - * an interrupt */ - IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); - goto unplugged; - } - -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(IWL_DL_ISR)) { - inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS); - IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, " - "fh 0x%08x\n", inta, inta_mask, inta_fh); - } -#endif - - trans_pcie->inta |= inta; - /* iwl_irq_tasklet() will service interrupts and re-enable them */ - if (likely(inta)) - tasklet_schedule(&trans_pcie->irq_tasklet); - else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - - unplugged: - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_HANDLED; - - none: - /* re-enable interrupts here since we don't have anything to service. */ - /* only Re-enable if disabled by irq and no schedules tasklet. */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_NONE; -} - -/* interrupt handler using ict table, with this interrupt driver will - * stop using INTA register to get device's interrupt, reading this register - * is expensive, device will write interrupts in ICT dram table, increment - * index then will fire interrupt to driver, driver will OR all ICT table - * entries from current index up to table entry with 0 value. the result is - * the interrupt we need to service, driver will set the entries back to 0 and - * set index. - */ -irqreturn_t iwl_isr_ict(int irq, void *data) -{ - struct iwl_trans *trans = data; - struct iwl_trans_pcie *trans_pcie; - u32 inta, inta_mask; - u32 val = 0; - u32 read; - unsigned long flags; - - if (!trans) - return IRQ_NONE; - - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* dram interrupt table not set yet, - * use legacy interrupt. - */ - if (!trans_pcie->use_ict) - return iwl_isr(irq, data); - - trace_iwlwifi_dev_irq(trans->dev); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. - */ - inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ - iwl_write32(trans, CSR_INT_MASK, 0x00000000); - - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); - trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); - if (!read) { - IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); - goto none; - } - - /* - * Collect all entries up to the first 0, starting from ict_index; - * note we already read at ict_index. - */ - do { - val |= read; - IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", - trans_pcie->ict_index, read); - trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; - trans_pcie->ict_index = - iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); - - read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); - trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, - read); - } while (read); - - /* We should not get this value, just ignore it. */ - if (val == 0xffffffff) - val = 0; - - /* - * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit - * (bit 15 before shifting it to 31) to clear when using interrupt - * coalescing. fortunately, bits 18 and 19 stay set when this happens - * so we use them to decide on the real state of the Rx bit. - * In order words, bit 15 is set if bit 18 or bit 19 are set. - */ - if (val & 0xC0000) - val |= 0x8000; - - inta = (0xff & val) | ((0xff00 & val) << 16); - IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", - inta, inta_mask, val); - - inta &= trans_pcie->inta_mask; - trans_pcie->inta |= inta; - - /* iwl_irq_tasklet() will service interrupts and re-enable them */ - if (likely(inta)) - tasklet_schedule(&trans_pcie->irq_tasklet); - else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) { - /* Allow interrupt if was disabled by this handler and - * no tasklet was schedules, We should not enable interrupt, - * tasklet will enable it. - */ - iwl_enable_interrupts(trans); - } - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_HANDLED; - - none: - /* re-enable interrupts here since we don't have anything to service. - * only Re-enable if disabled by irq. - */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_NONE; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c deleted file mode 100644 index 6f601cd05a94..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ /dev/null @@ -1,984 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include - -#include "iwl-debug.h" -#include "iwl-csr.h" -#include "iwl-prph.h" -#include "iwl-io.h" -#include "iwl-agn-hw.h" -#include "iwl-op-mode.h" -#include "iwl-trans-pcie-int.h" -/* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "iwl-commands.h" - -#define IWL_TX_CRC_SIZE 4 -#define IWL_TX_DELIMITER_SIZE 4 - -/** - * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array - */ -void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - u16 byte_cnt) -{ - struct iwlagn_scd_bc_tbl *scd_bc_tbl; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int write_ptr = txq->q.write_ptr; - int txq_id = txq->q.id; - u8 sec_ctl = 0; - u8 sta_id = 0; - u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; - __le16 bc_ent; - struct iwl_tx_cmd *tx_cmd = - (void *) txq->entries[txq->q.write_ptr].cmd->payload; - - scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - - WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); - - sta_id = tx_cmd->sta_id; - sec_ctl = tx_cmd->sec_ctl; - - switch (sec_ctl & TX_CMD_SEC_MSK) { - case TX_CMD_SEC_CCM: - len += CCMP_MIC_LEN; - break; - case TX_CMD_SEC_TKIP: - len += TKIP_ICV_LEN; - break; - case TX_CMD_SEC_WEP: - len += WEP_IV_LEN + WEP_ICV_LEN; - break; - } - - bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12)); - - scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; - - if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; -} - -/** - * iwl_txq_update_write_ptr - Send new write index to hardware - */ -void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq) -{ - u32 reg = 0; - int txq_id = txq->q.id; - - if (txq->need_update == 0) - return; - - if (trans->cfg->base_params->shadow_reg_enable) { - /* shadow register enabled */ - iwl_write32(trans, HBUS_TARG_WRPTR, - txq->q.write_ptr | (txq_id << 8)); - } else { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - /* if we're trying to save power */ - if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { - /* wake up nic if it's powered down ... - * uCode will wake up, and interrupt us again, so next - * time we'll skip this part. */ - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, - "Tx queue %d requesting wakeup," - " GP1 = 0x%x\n", txq_id, reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - return; - } - - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - txq->q.write_ptr | (txq_id << 8)); - - /* - * else not in power-save mode, - * uCode will never sleep when we're - * trying to tx (during RFKILL, we're not trying to tx). - */ - } else - iwl_write32(trans, HBUS_TARG_WRPTR, - txq->q.write_ptr | (txq_id << 8)); - } - txq->need_update = 0; -} - -static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - - dma_addr_t addr = get_unaligned_le32(&tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - addr |= - ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; - - return addr; -} - -static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - - return le16_to_cpu(tb->hi_n_len) >> 4; -} - -static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, - dma_addr_t addr, u16 len) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - u16 hi_n_len = len << 4; - - put_unaligned_le32(addr, &tb->lo); - if (sizeof(dma_addr_t) > sizeof(u32)) - hi_n_len |= ((addr >> 16) >> 16) & 0xF; - - tb->hi_n_len = cpu_to_le16(hi_n_len); - - tfd->num_tbs = idx + 1; -} - -static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) -{ - return tfd->num_tbs & 0x1f; -} - -static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, - struct iwl_tfd *tfd, enum dma_data_direction dma_dir) -{ - int i; - int num_tbs; - - /* Sanity check on number of chunks */ - num_tbs = iwl_tfd_get_num_tbs(tfd); - - if (num_tbs >= IWL_NUM_OF_TBS) { - IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); - /* @todo issue fatal error, it is quite serious situation */ - return; - } - - /* Unmap tx_cmd */ - if (num_tbs) - dma_unmap_single(trans->dev, - dma_unmap_addr(meta, mapping), - dma_unmap_len(meta, len), - DMA_BIDIRECTIONAL); - - /* Unmap chunks, if any. */ - for (i = 1; i < num_tbs; i++) - dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i), - iwl_tfd_tb_get_len(tfd, i), dma_dir); - - tfd->num_tbs = 0; -} - -/** - * iwl_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] - * @trans - transport private data - * @txq - tx queue - * @dma_dir - the direction of the DMA mapping - * - * Does NOT advance any TFD circular buffer read/write indexes - * Does NOT free the TFD itself (which is within circular buffer) - */ -void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - enum dma_data_direction dma_dir) -{ - struct iwl_tfd *tfd_tmp = txq->tfds; - - /* rd_ptr is bounded by n_bd and idx is bounded by n_window */ - int rd_ptr = txq->q.read_ptr; - int idx = get_cmd_index(&txq->q, rd_ptr); - - lockdep_assert_held(&txq->lock); - - /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ - iwl_unmap_tfd(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], - dma_dir); - - /* free SKB */ - if (txq->entries) { - struct sk_buff *skb; - - skb = txq->entries[idx].skb; - - /* Can be called from irqs-disabled context - * If skb is not NULL, it means that the whole queue is being - * freed and that the queue is not empty - free the skb - */ - if (skb) { - iwl_op_mode_free_skb(trans->op_mode, skb); - txq->entries[idx].skb = NULL; - } - } -} - -int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - dma_addr_t addr, u16 len, - u8 reset) -{ - struct iwl_queue *q; - struct iwl_tfd *tfd, *tfd_tmp; - u32 num_tbs; - - q = &txq->q; - tfd_tmp = txq->tfds; - tfd = &tfd_tmp[q->write_ptr]; - - if (reset) - memset(tfd, 0, sizeof(*tfd)); - - num_tbs = iwl_tfd_get_num_tbs(tfd); - - /* Each TFD can point to a maximum 20 Tx buffers */ - if (num_tbs >= IWL_NUM_OF_TBS) { - IWL_ERR(trans, "Error can not send more than %d chunks\n", - IWL_NUM_OF_TBS); - return -EINVAL; - } - - if (WARN_ON(addr & ~DMA_BIT_MASK(36))) - return -EINVAL; - - if (unlikely(addr & ~IWL_TX_DMA_MASK)) - IWL_ERR(trans, "Unaligned address = %llx\n", - (unsigned long long)addr); - - iwl_tfd_set_tb(tfd, num_tbs, addr, len); - - return 0; -} - -/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** - * DMA services - * - * Theory of operation - * - * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer - * of buffer descriptors, each of which points to one or more data buffers for - * the device to read from or fill. Driver and device exchange status of each - * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty - * entries in each circular buffer, to protect against confusing empty and full - * queue states. - * - * The device reads or writes the data in the queues via the device's several - * DMA/FIFO channels. Each queue is mapped to a single DMA channel. - * - * For Tx queue, there are low mark and high mark limits. If, after queuing - * the packet for Tx, free space become < low mark, Tx queue stopped. When - * reclaiming packets (on 'tx done IRQ), if free space become > high mark, - * Tx queue resumed. - * - ***************************************************/ - -int iwl_queue_space(const struct iwl_queue *q) -{ - int s = q->read_ptr - q->write_ptr; - - if (q->read_ptr > q->write_ptr) - s -= q->n_bd; - - if (s <= 0) - s += q->n_window; - /* keep some reserve to not confuse empty and full situations */ - s -= 2; - if (s < 0) - s = 0; - return s; -} - -/** - * iwl_queue_init - Initialize queue's high/low-water and read/write indexes - */ -int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) -{ - q->n_bd = count; - q->n_window = slots_num; - q->id = id; - - /* count must be power-of-two size, otherwise iwl_queue_inc_wrap - * and iwl_queue_dec_wrap are broken. */ - if (WARN_ON(!is_power_of_2(count))) - return -EINVAL; - - /* slots_num must be power-of-two size, otherwise - * get_cmd_index is broken. */ - if (WARN_ON(!is_power_of_2(slots_num))) - return -EINVAL; - - q->low_mark = q->n_window / 4; - if (q->low_mark < 4) - q->low_mark = 4; - - q->high_mark = q->n_window / 8; - if (q->high_mark < 2) - q->high_mark = 2; - - q->write_ptr = q->read_ptr = 0; - - return 0; -} - -static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq) -{ - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - int txq_id = txq->q.id; - int read_ptr = txq->q.read_ptr; - u8 sta_id = 0; - __le16 bc_ent; - struct iwl_tx_cmd *tx_cmd = - (void *)txq->entries[txq->q.read_ptr].cmd->payload; - - WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); - - if (txq_id != trans_pcie->cmd_queue) - sta_id = tx_cmd->sta_id; - - bc_ent = cpu_to_le16(1 | (sta_id << 12)); - scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; - - if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; -} - -static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid, - u16 txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 tbl_dw_addr; - u32 tbl_dw; - u16 scd_q2ratid; - - scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK; - - tbl_dw_addr = trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(txq_id); - - tbl_dw = iwl_read_targ_mem(trans, tbl_dw_addr); - - if (txq_id & 0x1) - tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); - else - tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); - - iwl_write_targ_mem(trans, tbl_dw_addr, tbl_dw); - - return 0; -} - -static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id) -{ - /* Simply stop the queue, but don't change any configuration; - * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ - iwl_write_prph(trans, - SCD_QUEUE_STATUS_BITS(txq_id), - (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| - (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); -} - -void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index) -{ - IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d\n", txq_id, index & 0xff); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (index & 0xff) | (txq_id << 8)); - iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), index); -} - -void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - int tx_fifo_id, bool active) -{ - int txq_id = txq->q.id; - - iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), - (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << SCD_QUEUE_STTS_REG_POS_WSL) | - SCD_QUEUE_STTS_REG_MSK); - - if (active) - IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d\n", - txq_id, tx_fifo_id); - else - IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); -} - -void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - u16 ra_tid = BUILD_RAxTID(sta_id, tid); - - if (test_and_set_bit(txq_id, trans_pcie->queue_used)) - WARN_ONCE(1, "queue %d already used - expect issues", txq_id); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* Stop this Tx queue before configuring it */ - iwlagn_tx_queue_stop_scheduler(trans, txq_id); - - /* Map receiver-address / traffic-ID to this queue */ - iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id); - - /* Set this queue as a chain-building queue */ - iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); - - /* enable aggregations for the queue */ - iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); - - /* Place first TFD at index corresponding to start sequence number. - * Assumes that ssn_idx is valid (!= 0xFFF) */ - trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); - trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); - iwl_trans_set_wr_ptrs(trans, txq_id, ssn); - - /* Set up Tx window size and frame limit for this queue */ - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), - ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - - iwl_set_bits_prph(trans, SCD_INTERRUPT_MASK, (1 << txq_id)); - - /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], - fifo, true); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); -} - -void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { - WARN_ONCE(1, "queue %d not used", txq_id); - return; - } - - iwlagn_tx_queue_stop_scheduler(trans, txq_id); - - iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); - - trans_pcie->txq[txq_id].q.read_ptr = 0; - trans_pcie->txq[txq_id].q.write_ptr = 0; - iwl_trans_set_wr_ptrs(trans, txq_id, 0); - - iwl_clear_bits_prph(trans, SCD_INTERRUPT_MASK, BIT(txq_id)); - - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], - 0, false); -} - -/*************** HOST COMMAND QUEUE FUNCTIONS *****/ - -/** - * iwl_enqueue_hcmd - enqueue a uCode command - * @priv: device private data point - * @cmd: a point to the ucode command structure - * - * The function returns < 0 values to indicate the operation is - * failed. On success, it turns the index (> 0) of command in the - * command queue. - */ -static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_queue *q = &txq->q; - struct iwl_device_cmd *out_cmd; - struct iwl_cmd_meta *out_meta; - dma_addr_t phys_addr; - u32 idx; - u16 copy_size, cmd_size; - bool had_nocopy = false; - int i; - u8 *cmd_dest; -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; - int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; - int trace_idx; -#endif - - copy_size = sizeof(out_cmd->hdr); - cmd_size = sizeof(out_cmd->hdr); - - /* need one for the header if the first is NOCOPY */ - BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); - - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { - if (!cmd->len[i]) - continue; - if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { - had_nocopy = true; - } else { - /* NOCOPY must not be followed by normal! */ - if (WARN_ON(had_nocopy)) - return -EINVAL; - copy_size += cmd->len[i]; - } - cmd_size += cmd->len[i]; - } - - /* - * If any of the command structures end up being larger than - * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically - * allocated into separate TFDs, then we will need to - * increase the size of the buffers. - */ - if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) - return -EINVAL; - - spin_lock_bh(&txq->lock); - - if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { - spin_unlock_bh(&txq->lock); - - IWL_ERR(trans, "No space in command queue\n"); - iwl_op_mode_cmd_queue_full(trans->op_mode); - return -ENOSPC; - } - - idx = get_cmd_index(q, q->write_ptr); - out_cmd = txq->entries[idx].cmd; - out_meta = &txq->entries[idx].meta; - - memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ - if (cmd->flags & CMD_WANT_SKB) - out_meta->source = cmd; - - /* set up the header */ - - out_cmd->hdr.cmd = cmd->id; - out_cmd->hdr.flags = 0; - out_cmd->hdr.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | - INDEX_TO_SEQ(q->write_ptr)); - - /* and copy the data that needs to be copied */ - - cmd_dest = out_cmd->payload; - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { - if (!cmd->len[i]) - continue; - if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) - break; - memcpy(cmd_dest, cmd->data[i], cmd->len[i]); - cmd_dest += cmd->len[i]; - } - - IWL_DEBUG_HC(trans, - "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", - trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), - out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), - cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); - - phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { - idx = -ENOMEM; - goto out; - } - - dma_unmap_addr_set(out_meta, mapping, phys_addr); - dma_unmap_len_set(out_meta, len, copy_size); - - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - trace_bufs[0] = &out_cmd->hdr; - trace_lens[0] = copy_size; - trace_idx = 1; -#endif - - for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { - if (!cmd->len[i]) - continue; - if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) - continue; - phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], - cmd->len[i], DMA_BIDIRECTIONAL); - if (dma_mapping_error(trans->dev, phys_addr)) { - iwl_unmap_tfd(trans, out_meta, - &txq->tfds[q->write_ptr], - DMA_BIDIRECTIONAL); - idx = -ENOMEM; - goto out; - } - - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, - cmd->len[i], 0); -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - trace_bufs[trace_idx] = cmd->data[i]; - trace_lens[trace_idx] = cmd->len[i]; - trace_idx++; -#endif - } - - out_meta->flags = cmd->flags; - - txq->need_update = 1; - - /* check that tracing gets all possible blocks */ - BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - trace_iwlwifi_dev_hcmd(trans->dev, cmd->flags, - trace_bufs[0], trace_lens[0], - trace_bufs[1], trace_lens[1], - trace_bufs[2], trace_lens[2]); -#endif - - /* start timer if queue currently empty */ - if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); - - /* Increment and update queue's write index */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - iwl_txq_update_write_ptr(trans, txq); - - out: - spin_unlock_bh(&txq->lock); - return idx; -} - -static inline void iwl_queue_progress(struct iwl_trans_pcie *trans_pcie, - struct iwl_tx_queue *txq) -{ - if (!trans_pcie->wd_timeout) - return; - - /* - * if empty delete timer, otherwise move timer forward - * since we're making progress on this queue - */ - if (txq->q.read_ptr == txq->q.write_ptr) - del_timer(&txq->stuck_timer); - else - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); -} - -/** - * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd - * - * When FW advances 'R' index, all entries between old and new 'R' index - * need to be reclaimed. As result, some free space forms. If there is - * enough free space (> low mark), wake the stack that feeds us. - */ -static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, - int idx) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - int nfreed = 0; - - lockdep_assert_held(&txq->lock); - - if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) { - IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, idx, q->n_bd, - q->write_ptr, q->read_ptr); - return; - } - - for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { - - if (nfreed++ > 0) { - IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", - idx, q->write_ptr, q->read_ptr); - iwl_op_mode_nic_error(trans->op_mode); - } - - } - - iwl_queue_progress(trans_pcie, txq); -} - -/** - * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them - * @rxb: Rx buffer to reclaim - * @handler_status: return value of the handler of the command - * (put in setup_rx_handlers) - * - * If an Rx buffer has an async callback associated with it the callback - * will be executed. The attached skb (if present) will only be freed - * if the callback returns 1 - */ -void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, - int handler_status) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int index = SEQ_TO_INDEX(sequence); - int cmd_index; - struct iwl_device_cmd *cmd; - struct iwl_cmd_meta *meta; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - - /* If a Tx command is being handled and it isn't in the actual - * command queue then there a command routing bug has been introduced - * in the queue management code. */ - if (WARN(txq_id != trans_pcie->cmd_queue, - "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, trans_pcie->cmd_queue, sequence, - trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, - trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { - iwl_print_hex_error(trans, pkt, 32); - return; - } - - spin_lock(&txq->lock); - - cmd_index = get_cmd_index(&txq->q, index); - cmd = txq->entries[cmd_index].cmd; - meta = &txq->entries[cmd_index].meta; - - iwl_unmap_tfd(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); - - /* Input error checking is done when commands are added to queue. */ - if (meta->flags & CMD_WANT_SKB) { - struct page *p = rxb_steal_page(rxb); - - meta->source->resp_pkt = pkt; - meta->source->_rx_page_addr = (unsigned long)page_address(p); - meta->source->_rx_page_order = trans_pcie->rx_page_order; - meta->source->handler_status = handler_status; - } - - iwl_hcmd_queue_reclaim(trans, txq_id, index); - - if (!(meta->flags & CMD_ASYNC)) { - if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { - IWL_WARN(trans, - "HCMD_ACTIVE already clear for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, - cmd->hdr.cmd)); - } - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, - cmd->hdr.cmd)); - wake_up(&trans->wait_command_queue); - } - - meta->flags = 0; - - spin_unlock(&txq->lock); -} - -#define HOST_COMPLETE_TIMEOUT (2 * HZ) - -static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - - /* An asynchronous command can not expect an SKB to be set. */ - if (WARN_ON(cmd->flags & CMD_WANT_SKB)) - return -EINVAL; - - - ret = iwl_enqueue_hcmd(trans, cmd); - if (ret < 0) { - IWL_ERR(trans, - "Error sending %s: enqueue_hcmd failed: %d\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); - return ret; - } - return 0; -} - -static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int cmd_idx; - int ret; - - IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); - - if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status))) { - IWL_ERR(trans, "Command %s: a command is already active!\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); - return -EIO; - } - - IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); - - cmd_idx = iwl_enqueue_hcmd(trans, cmd); - if (cmd_idx < 0) { - ret = cmd_idx; - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - IWL_ERR(trans, - "Error sending %s: enqueue_hcmd failed: %d\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); - return ret; - } - - ret = wait_event_timeout(trans->wait_command_queue, - !test_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status), - HOST_COMPLETE_TIMEOUT); - if (!ret) { - if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { - struct iwl_tx_queue *txq = - &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_queue *q = &txq->q; - - IWL_ERR(trans, - "Error sending %s: time out after %dms.\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), - jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - - IWL_ERR(trans, - "Current CMD queue read_ptr %d write_ptr %d\n", - q->read_ptr, q->write_ptr); - - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - IWL_DEBUG_INFO(trans, - "Clearing HCMD_ACTIVE for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, - cmd->id)); - ret = -ETIMEDOUT; - goto cancel; - } - } - - if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { - IWL_ERR(trans, "Error: Response NULL in '%s'\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); - ret = -EIO; - goto cancel; - } - - return 0; - -cancel: - if (cmd->flags & CMD_WANT_SKB) { - /* - * Cancel the CMD_WANT_SKB flag for the cmd in the - * TX cmd queue. Otherwise in case the cmd comes - * in later, it will possibly set an invalid - * address (cmd->meta.source). - */ - trans_pcie->txq[trans_pcie->cmd_queue]. - entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; - } - - if (cmd->resp_pkt) { - iwl_free_resp(cmd); - cmd->resp_pkt = NULL; - } - - return ret; -} - -int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) -{ - if (cmd->flags & CMD_ASYNC) - return iwl_send_cmd_async(trans, cmd); - - return iwl_send_cmd_sync(trans, cmd); -} - -/* Frees buffers until index _not_ inclusive */ -int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, - struct sk_buff_head *skbs) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - int last_to_free; - int freed = 0; - - /* This function is not meant to release cmd queue*/ - if (WARN_ON(txq_id == trans_pcie->cmd_queue)) - return 0; - - lockdep_assert_held(&txq->lock); - - /*Since we free until index _not_ inclusive, the one before index is - * the last we will free. This one must be used */ - last_to_free = iwl_queue_dec_wrap(index, q->n_bd); - - if ((index >= q->n_bd) || - (iwl_queue_used(q, last_to_free) == 0)) { - IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, last_to_free, q->n_bd, - q->write_ptr, q->read_ptr); - return 0; - } - - if (WARN_ON(!skb_queue_empty(skbs))) - return 0; - - for (; - q->read_ptr != index; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { - - if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) - continue; - - __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); - - txq->entries[txq->q.read_ptr].skb = NULL; - - iwlagn_txq_inval_byte_cnt_tbl(trans, txq); - - iwl_txq_free_tfd(trans, txq, DMA_TO_DEVICE); - freed++; - } - - iwl_queue_progress(trans_pcie, txq); - - return freed; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c deleted file mode 100644 index 1e50401023e0..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ /dev/null @@ -1,2178 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include - -#include "iwl-drv.h" -#include "iwl-trans.h" -#include "iwl-trans-pcie-int.h" -#include "iwl-csr.h" -#include "iwl-prph.h" -#include "iwl-eeprom.h" -#include "iwl-agn-hw.h" -/* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "iwl-commands.h" - -#define SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie) \ - (((1<cfg->base_params->num_of_queues) - 1) &\ - (~(1<<(trans_pcie)->cmd_queue))) - -static int iwl_trans_rx_alloc(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct device *dev = trans->dev; - - memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); - - spin_lock_init(&rxq->lock); - - if (WARN_ON(rxq->bd || rxq->rb_stts)) - return -EINVAL; - - /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - &rxq->bd_dma, GFP_KERNEL); - if (!rxq->bd) - goto err_bd; - - /*Allocate the driver's pointer to receive buffer status */ - rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), - &rxq->rb_stts_dma, GFP_KERNEL); - if (!rxq->rb_stts) - goto err_rb_stts; - - return 0; - -err_rb_stts: - dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); - rxq->bd = NULL; -err_bd: - return -ENOMEM; -} - -static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - int i; - - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { - /* In the reset function, these buffers may have been allocated - * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].page != NULL) { - dma_unmap_page(trans->dev, rxq->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rxq->pool[i].page, - trans_pcie->rx_page_order); - rxq->pool[i].page = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - } -} - -static void iwl_trans_rx_hw_init(struct iwl_trans *trans, - struct iwl_rx_queue *rxq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 rb_size; - const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - u32 rb_timeout = RX_RB_TIMEOUT; /* FIXME: RX_RB_TIMEOUT for all devices? */ - - if (trans_pcie->rx_buf_size_8k) - rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; - else - rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; - - /* Stop Rx DMA */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - - /* Reset driver's Rx queue write index */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); - - /* Tell device where to find RBD circular buffer in DRAM */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, - (u32)(rxq->bd_dma >> 8)); - - /* Tell device where in DRAM to update its Rx status */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, - rxq->rb_stts_dma >> 4); - - /* Enable Rx DMA - * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in - * the credit mechanism in 5000 HW RX FIFO - * Direct rx interrupts to hosts - * Rx buffer size 4 or 8k - * RB timeout 0x10 - * 256 RBDs - */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | - FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | - FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | - rb_size| - (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| - (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); - - /* Set interrupt coalescing timer to default (2048 usecs) */ - iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); -} - -static int iwl_rx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - - int i, err; - unsigned long flags; - - if (!rxq->bd) { - err = iwl_trans_rx_alloc(trans); - if (err) - return err; - } - - spin_lock_irqsave(&rxq->lock, flags); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - iwl_trans_rxq_free_rx_bufs(trans); - - for (i = 0; i < RX_QUEUE_SIZE; i++) - rxq->queue[i] = NULL; - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - rxq->free_count = 0; - spin_unlock_irqrestore(&rxq->lock, flags); - - iwlagn_rx_replenish(trans); - - iwl_trans_rx_hw_init(trans, rxq); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - rxq->need_update = 1; - iwl_rx_queue_update_write_ptr(trans, rxq); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - return 0; -} - -static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - unsigned long flags; - - /*if rxq->bd is NULL, it means that nothing has been allocated, - * exit now */ - if (!rxq->bd) { - IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); - return; - } - - spin_lock_irqsave(&rxq->lock, flags); - iwl_trans_rxq_free_rx_bufs(trans); - spin_unlock_irqrestore(&rxq->lock, flags); - - dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); - rxq->bd = NULL; - - if (rxq->rb_stts) - dma_free_coherent(trans->dev, - sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - else - IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); - memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); - rxq->rb_stts = NULL; -} - -static int iwl_trans_rx_stop(struct iwl_trans *trans) -{ - - /* stop Rx DMA */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, - FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); -} - -static int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr, size_t size) -{ - if (WARN_ON(ptr->addr)) - return -EINVAL; - - ptr->addr = dma_alloc_coherent(trans->dev, size, - &ptr->dma, GFP_KERNEL); - if (!ptr->addr) - return -ENOMEM; - ptr->size = size; - return 0; -} - -static void iwlagn_free_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr) -{ - if (unlikely(!ptr->addr)) - return; - - dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); - memset(ptr, 0, sizeof(*ptr)); -} - -static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) -{ - struct iwl_tx_queue *txq = (void *)data; - struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; - struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); - - spin_lock(&txq->lock); - /* check if triggered erroneously */ - if (txq->q.read_ptr == txq->q.write_ptr) { - spin_unlock(&txq->lock); - return; - } - spin_unlock(&txq->lock); - - - IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, - jiffies_to_msecs(trans_pcie->wd_timeout)); - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->q.read_ptr, txq->q.write_ptr); - IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n", - iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq->q.id)) - & (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id))); - - iwl_op_mode_nic_error(trans->op_mode); -} - -static int iwl_trans_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue *txq, int slots_num, - u32 txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; - int i; - - if (WARN_ON(txq->entries || txq->tfds)) - return -EINVAL; - - setup_timer(&txq->stuck_timer, iwl_trans_pcie_queue_stuck_timer, - (unsigned long)txq); - txq->trans_pcie = trans_pcie; - - txq->q.n_window = slots_num; - - txq->entries = kcalloc(slots_num, - sizeof(struct iwl_pcie_tx_queue_entry), - GFP_KERNEL); - - if (!txq->entries) - goto error; - - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < slots_num; i++) { - txq->entries[i].cmd = - kmalloc(sizeof(struct iwl_device_cmd), - GFP_KERNEL); - if (!txq->entries[i].cmd) - goto error; - } - - /* Circular buffer of transmit frame descriptors (TFDs), - * shared with device */ - txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, - &txq->q.dma_addr, GFP_KERNEL); - if (!txq->tfds) { - IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); - goto error; - } - txq->q.id = txq_id; - - return 0; -error: - if (txq->entries && txq_id == trans_pcie->cmd_queue) - for (i = 0; i < slots_num; i++) - kfree(txq->entries[i].cmd); - kfree(txq->entries); - txq->entries = NULL; - - return -ENOMEM; - -} - -static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, - int slots_num, u32 txq_id) -{ - int ret; - - txq->need_update = 0; - - /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise - * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ - BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); - - /* Initialize queue's high/low-water marks, and head/tail indexes */ - ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, - txq_id); - if (ret) - return ret; - - spin_lock_init(&txq->lock); - - /* - * Tell nic where to find circular buffer of Tx Frame Descriptors for - * given Tx queue, and enable the DMA channel used for that queue. - * Circular buffer (TFD queue in DRAM) physical base address */ - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); - - return 0; -} - -/** - * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's - */ -static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - enum dma_data_direction dma_dir; - - if (!q->n_bd) - return; - - /* In the command queue, all the TBs are mapped as BIDI - * so unmap them as such. - */ - if (txq_id == trans_pcie->cmd_queue) - dma_dir = DMA_BIDIRECTIONAL; - else - dma_dir = DMA_TO_DEVICE; - - spin_lock_bh(&txq->lock); - while (q->write_ptr != q->read_ptr) { - iwl_txq_free_tfd(trans, txq, dma_dir); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); - } - spin_unlock_bh(&txq->lock); -} - -/** - * iwl_tx_queue_free - Deallocate DMA queue. - * @txq: Transmit queue to deallocate. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * 0-fill, but do not free "txq" descriptor structure. - */ -static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct device *dev = trans->dev; - int i; - - if (WARN_ON(!txq)) - return; - - iwl_tx_queue_unmap(trans, txq_id); - - /* De-alloc array of command/tx buffers */ - - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < txq->q.n_window; i++) - kfree(txq->entries[i].cmd); - - /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) { - dma_free_coherent(dev, sizeof(struct iwl_tfd) * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); - memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); - } - - kfree(txq->entries); - txq->entries = NULL; - - del_timer_sync(&txq->stuck_timer); - - /* 0-fill queue descriptor structure */ - memset(txq, 0, sizeof(*txq)); -} - -/** - * iwl_trans_tx_free - Free TXQ Context - * - * Destroy all TX DMA queues and structures - */ -static void iwl_trans_pcie_tx_free(struct iwl_trans *trans) -{ - int txq_id; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* Tx queues */ - if (trans_pcie->txq) { - for (txq_id = 0; - txq_id < trans->cfg->base_params->num_of_queues; txq_id++) - iwl_tx_queue_free(trans, txq_id); - } - - kfree(trans_pcie->txq); - trans_pcie->txq = NULL; - - iwlagn_free_dma_ptr(trans, &trans_pcie->kw); - - iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); -} - -/** - * iwl_trans_tx_alloc - allocate TX context - * Allocate all Tx DMA structures and initialize them - * - * @param priv - * @return error code - */ -static int iwl_trans_tx_alloc(struct iwl_trans *trans) -{ - int ret; - int txq_id, slots_num; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * - sizeof(struct iwlagn_scd_bc_tbl); - - /*It is not allowed to alloc twice, so warn when this happens. - * We cannot rely on the previous allocation, so free and fail */ - if (WARN_ON(trans_pcie->txq)) { - ret = -EINVAL; - goto error; - } - - ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, - scd_bc_tbls_size); - if (ret) { - IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); - goto error; - } - - /* Alloc keep-warm buffer */ - ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); - if (ret) { - IWL_ERR(trans, "Keep Warm allocation failed\n"); - goto error; - } - - trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, - sizeof(struct iwl_tx_queue), GFP_KERNEL); - if (!trans_pcie->txq) { - IWL_ERR(trans, "Not enough memory for txq\n"); - ret = ENOMEM; - goto error; - } - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); - goto error; - } - } - - return 0; - -error: - iwl_trans_pcie_tx_free(trans); - - return ret; -} -static int iwl_tx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - int txq_id, slots_num; - unsigned long flags; - bool alloc = false; - - if (!trans_pcie->txq) { - ret = iwl_trans_tx_alloc(trans); - if (ret) - goto error; - alloc = true; - } - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* Turn off all Tx DMA fifos */ - iwl_write_prph(trans, SCD_TXFACT, 0); - - /* Tell NIC where to find the "keep warm" buffer */ - iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, - trans_pcie->kw.dma >> 4); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); - goto error; - } - } - - return 0; -error: - /*Upon error, free only if we allocated something */ - if (alloc) - iwl_trans_pcie_tx_free(trans); - return ret; -} - -static void iwl_set_pwr_vmain(struct iwl_trans *trans) -{ -/* - * (for documentation purposes) - * to set power to V_AUX, do: - - if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) - iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - */ - - iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); -} - -/* PCI registers */ -#define PCI_CFG_RETRY_TIMEOUT 0x041 -#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 -#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 - -static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int pos; - u16 pci_lnk_ctl; - - struct pci_dev *pci_dev = trans_pcie->pci_dev; - - pos = pci_pcie_cap(pci_dev); - pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); - return pci_lnk_ctl; -} - -static void iwl_apm_config(struct iwl_trans *trans) -{ - /* - * HW bug W/A for instability in PCIe bus L0S->L1 transition. - * Check if BIOS (or OS) enabled L1-ASPM on this device. - * If so (likely), disable L0S, so device moves directly L0->L1; - * costs negligible amount of power savings. - * If not (unlikely), enable L0S, so there is at least some - * power savings, even without L1. - */ - u16 lctl = iwl_pciexp_link_ctrl(trans); - - if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == - PCI_CFG_LINK_CTRL_VAL_L1_EN) { - /* L1-ASPM enabled; disable(!) L0S */ - iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - dev_printk(KERN_INFO, trans->dev, - "L1 Enabled; Disabling L0S\n"); - } else { - /* L1-ASPM disabled; enable(!) L0S */ - iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - dev_printk(KERN_INFO, trans->dev, - "L1 Disabled; Enabling L0S\n"); - } - trans->pm_support = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); -} - -/* - * Start up NIC's basic functionality after it has been reset - * (e.g. after platform boot, or shutdown via iwl_apm_stop()) - * NOTE: This does not load uCode nor start the embedded processor - */ -static int iwl_apm_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret = 0; - IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); - - /* - * Use "set_bit" below rather than "write", to preserve any hardware - * bits already set by default after reset. - */ - - /* Disable L0S exit timer (platform NMI Work/Around) */ - iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); - - /* - * Disable L0s without affecting L1; - * don't wait for ICH L0s (ICH bug W/A) - */ - iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); - - /* Set FH wait threshold to maximum (HW error during stress W/A) */ - iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); - - /* - * Enable HAP INTA (interrupt from management bus) to - * wake device's PCI Express link L1a -> L0s - */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); - - iwl_apm_config(trans); - - /* Configure analog phase-lock-loop before activating to D0A */ - if (trans->cfg->base_params->pll_cfg_val) - iwl_set_bit(trans, CSR_ANA_PLL_CFG, - trans->cfg->base_params->pll_cfg_val); - - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is supported, e.g. iwl_write_prph() - * and accesses to uCode SRAM. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (ret < 0) { - IWL_DEBUG_INFO(trans, "Failed to init the card\n"); - goto out; - } - - /* - * Enable DMA clock and wait for it to stabilize. - * - * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits - * do not disable clocks. This preserves any hardware bits already - * set by default in "CLK_CTRL_REG" after reset. - */ - iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); - udelay(20); - - /* Disable L1-Active */ - iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - - set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); - -out: - return ret; -} - -static int iwl_apm_stop_master(struct iwl_trans *trans) -{ - int ret = 0; - - /* stop device's busmaster DMA activity */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); - - ret = iwl_poll_bit(trans, CSR_RESET, - CSR_RESET_REG_FLAG_MASTER_DISABLED, - CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); - if (ret) - IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); - - IWL_DEBUG_INFO(trans, "stop master\n"); - - return ret; -} - -static void iwl_apm_stop(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); - - clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); - - /* Stop device's DMA activity */ - iwl_apm_stop_master(trans); - - /* Reset the entire device */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); - - udelay(10); - - /* - * Clear "initialization complete" bit to move adapter from - * D0A* (powered-up Active) --> D0U* (Uninitialized) state. - */ - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); -} - -static int iwl_nic_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - /* nic_init */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_apm_init(trans); - - /* Set interrupt coalescing calibration timer to default (512 usecs) */ - iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - iwl_set_pwr_vmain(trans); - - iwl_op_mode_nic_config(trans->op_mode); - -#ifndef CONFIG_IWLWIFI_IDI - /* Allocate the RX queue, or reset if it is already allocated */ - iwl_rx_init(trans); -#endif - - /* Allocate or reset and init all Tx and Command queues */ - if (iwl_tx_init(trans)) - return -ENOMEM; - - if (trans->cfg->base_params->shadow_reg_enable) { - /* enable shadow regs in HW */ - iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); - IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); - } - - return 0; -} - -#define HW_READY_TIMEOUT (50) - -/* Note: returns poll_bit return value, which is >= 0 if success */ -static int iwl_set_hw_ready(struct iwl_trans *trans) -{ - int ret; - - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); - - /* See if we got it */ - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - HW_READY_TIMEOUT); - - IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); - return ret; -} - -/* Note: returns standard 0/-ERROR code */ -static int iwl_prepare_card_hw(struct iwl_trans *trans) -{ - int ret; - - IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); - - ret = iwl_set_hw_ready(trans); - /* If the card is ready, exit 0 */ - if (ret >= 0) - return 0; - - /* If HW is not ready, prepare the conditions to check again */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); - - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, - CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); - - if (ret < 0) - return ret; - - /* HW should be ready by now, check again. */ - ret = iwl_set_hw_ready(trans); - if (ret >= 0) - return 0; - return ret; -} - -/* - * ucode - */ -static int iwl_load_section(struct iwl_trans *trans, u8 section_num, - const struct fw_desc *section) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - dma_addr_t phy_addr = section->p_addr; - u32 byte_cnt = section->len; - u32 dst_addr = section->offset; - int ret; - - trans_pcie->ucode_write_complete = false; - - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); - - iwl_write_direct32(trans, - FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), - dst_addr); - - iwl_write_direct32(trans, - FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), - phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); - - iwl_write_direct32(trans, - FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), - (iwl_get_dma_hi_addr(phy_addr) - << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); - - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | - 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | - FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); - - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | - FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - - IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", - section_num); - ret = wait_event_timeout(trans_pcie->ucode_write_waitq, - trans_pcie->ucode_write_complete, 5 * HZ); - if (!ret) { - IWL_ERR(trans, "Could not load the [%d] uCode section\n", - section_num); - return -ETIMEDOUT; - } - - return 0; -} - -static int iwl_load_given_ucode(struct iwl_trans *trans, - const struct fw_img *image) -{ - int ret = 0; - int i; - - for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { - if (!image->sec[i].p_addr) - break; - - ret = iwl_load_section(trans, i, &image->sec[i]); - if (ret) - return ret; - } - - /* Remove all resets to allow NIC to operate */ - iwl_write32(trans, CSR_RESET, 0); - - return 0; -} - -static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, - const struct fw_img *fw) -{ - int ret; - bool hw_rfkill; - - /* This may fail if AMT took ownership of the device */ - if (iwl_prepare_card_hw(trans)) { - IWL_WARN(trans, "Exit HW not ready\n"); - return -EIO; - } - - iwl_enable_rfkill_int(trans); - - /* If platform's RF_KILL switch is NOT set to KILL */ - hw_rfkill = iwl_is_rfkill_set(trans); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - if (hw_rfkill) - return -ERFKILL; - - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - - ret = iwl_nic_init(trans); - if (ret) { - IWL_ERR(trans, "Unable to init nic\n"); - return ret; - } - - /* make sure rfkill handshake bits are cleared */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - iwl_enable_interrupts(trans); - - /* really make sure rfkill handshake bits are cleared */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - /* Load the given image to the HW */ - return iwl_load_given_ucode(trans, fw); -} - -/* - * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - * must be called under the irq lock and with MAC access - */ -static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) -{ - struct iwl_trans_pcie __maybe_unused *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - lockdep_assert_held(&trans_pcie->irq_lock); - - iwl_write_prph(trans, SCD_TXFACT, mask); -} - -static void iwl_tx_start(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 a; - unsigned long flags; - int i, chan; - u32 reg_val; - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - trans_pcie->scd_base_addr = - iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); - a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; - /* reset conext data memory */ - for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(trans, a, 0); - /* reset tx status memory */ - for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(trans, a, 0); - for (; a < trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE( - trans->cfg->base_params->num_of_queues); - a += 4) - iwl_write_targ_mem(trans, a, 0); - - iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, - trans_pcie->scd_bc_tbls.dma >> 10); - - /* Enable DMA channel */ - for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) - iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); - - /* Update FH chicken bits */ - reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); - iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, - reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - - iwl_write_prph(trans, SCD_QUEUECHAIN_SEL, - SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie)); - iwl_write_prph(trans, SCD_AGGR_SEL, 0); - - /* initiate the queues */ - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - iwl_write_prph(trans, SCD_QUEUE_RDPTR(i), 0); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, 0 | (i << 8)); - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(i), 0); - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(i) + - sizeof(u32), - ((SCD_WIN_SIZE << - SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((SCD_FRAME_LIMIT << - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - } - - iwl_write_prph(trans, SCD_INTERRUPT_MASK, - IWL_MASK(0, trans->cfg->base_params->num_of_queues)); - - /* Activate all Tx DMA/FIFO channels */ - iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); - - iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0); - - /* make sure all queue are not stopped/used */ - memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); - memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); - - for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { - int fifo = trans_pcie->setup_q_to_fifo[i]; - - set_bit(i, trans_pcie->queue_used); - - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i], - fifo, true); - } - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - /* Enable L1-Active */ - iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); -} - -static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) -{ - iwl_reset_ict(trans); - iwl_tx_start(trans); -} - -/** - * iwlagn_txq_ctx_stop - Stop all Tx DMA channels - */ -static int iwl_trans_tx_stop(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ch, txq_id, ret; - unsigned long flags; - - /* Turn off all Tx DMA fifos */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - iwl_trans_txq_set_sched(trans, 0); - - /* Stop each Tx DMA channel, and wait for it to be idle */ - for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); - ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); - if (ret < 0) - IWL_ERR(trans, - "Failing on timeout while stopping DMA channel %d [0x%08x]", - ch, - iwl_read_direct32(trans, - FH_TSSR_TX_STATUS_REG)); - } - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - if (!trans_pcie->txq) { - IWL_WARN(trans, "Stopping tx queues that aren't allocated..."); - return 0; - } - - /* Unmap DMA from host system and free skb's */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) - iwl_tx_queue_unmap(trans, txq_id); - - return 0; -} - -static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - /* tell the device to stop sending interrupts */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - /* device going down, Stop using ICT table */ - iwl_disable_ict(trans); - - /* - * If a HW restart happens during firmware loading, - * then the firmware loading might call this function - * and later it might be called again due to the - * restart. So don't process again if the device is - * already dead. - */ - if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { - iwl_trans_tx_stop(trans); -#ifndef CONFIG_IWLWIFI_IDI - iwl_trans_rx_stop(trans); -#endif - /* Power-down device's busmaster DMA clocks */ - iwl_write_prph(trans, APMG_CLK_DIS_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - udelay(5); - } - - /* Make sure (redundant) we've released our request to stay awake */ - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - - /* Stop the device, and put it in low power state */ - iwl_apm_stop(trans); - - /* Upon stop, the APM issues an interrupt if HW RF kill is set. - * Clean again the interrupt here - */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - iwl_enable_rfkill_int(trans); - - /* wait to make sure we flush pending tasklet*/ - synchronize_irq(trans_pcie->irq); - tasklet_kill(&trans_pcie->irq_tasklet); - - cancel_work_sync(&trans_pcie->rx_replenish); - - /* stop and reset the on-board processor */ - iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - /* clear all status bits */ - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); - clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); - clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); -} - -static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) -{ - /* let the ucode operate on its own */ - iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - - iwl_disable_interrupts(trans); - iwl_clear_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); -} - -static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; - struct iwl_cmd_meta *out_meta; - struct iwl_tx_queue *txq; - struct iwl_queue *q; - dma_addr_t phys_addr = 0; - dma_addr_t txcmd_phys; - dma_addr_t scratch_phys; - u16 len, firstlen, secondlen; - u8 wait_write_ptr = 0; - __le16 fc = hdr->frame_control; - u8 hdr_len = ieee80211_hdrlen(fc); - u16 __maybe_unused wifi_seq; - - txq = &trans_pcie->txq[txq_id]; - q = &txq->q; - - if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { - WARN_ON_ONCE(1); - return -EINVAL; - } - - spin_lock(&txq->lock); - - /* Set up driver data for this TFD */ - txq->entries[q->write_ptr].skb = skb; - txq->entries[q->write_ptr].cmd = dev_cmd; - - dev_cmd->hdr.cmd = REPLY_TX; - dev_cmd->hdr.sequence = - cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | - INDEX_TO_SEQ(q->write_ptr))); - - /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_meta = &txq->entries[q->write_ptr].meta; - - /* - * Use the first empty entry in this queue's command buffer array - * to contain the Tx command and MAC header concatenated together - * (payload data will be in another buffer). - * Size of this varies, due to varying MAC header length. - * If end is not dword aligned, we'll have 2 extra bytes at the end - * of the MAC header (device reads on dword boundaries). - * We'll tell device about this padding later. - */ - len = sizeof(struct iwl_tx_cmd) + - sizeof(struct iwl_cmd_header) + hdr_len; - firstlen = (len + 3) & ~3; - - /* Tell NIC about any 2-byte padding after MAC header */ - if (firstlen != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; - - /* Physical address of this Tx command's header (not MAC header!), - * within command buffer array. */ - txcmd_phys = dma_map_single(trans->dev, - &dev_cmd->hdr, firstlen, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) - goto out_err; - dma_unmap_addr_set(out_meta, mapping, txcmd_phys); - dma_unmap_len_set(out_meta, len, firstlen); - - if (!ieee80211_has_morefrags(fc)) { - txq->need_update = 1; - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } - - /* Set up TFD's 2nd entry to point directly to remainder of skb, - * if any (802.11 null frames have no payload). */ - secondlen = skb->len - hdr_len; - if (secondlen > 0) { - phys_addr = dma_map_single(trans->dev, skb->data + hdr_len, - secondlen, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { - dma_unmap_single(trans->dev, - dma_unmap_addr(out_meta, mapping), - dma_unmap_len(out_meta, len), - DMA_BIDIRECTIONAL); - goto out_err; - } - } - - /* Attach buffers to TFD */ - iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1); - if (secondlen > 0) - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, - secondlen, 0); - - scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch); - - /* take back ownership of DMA buffer to enable update */ - dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - - IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", - le16_to_cpu(dev_cmd->hdr.sequence)); - IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - - /* Set up entry for this TFD in Tx byte-count array */ - iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); - - dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - - trace_iwlwifi_dev_tx(trans->dev, - &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], - sizeof(struct iwl_tfd), - &dev_cmd->hdr, firstlen, - skb->data + hdr_len, secondlen); - - /* start timer if queue currently empty */ - if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); - - /* Tell device the write index *just past* this latest filled TFD */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - iwl_txq_update_write_ptr(trans, txq); - - /* - * At this point the frame is "transmitted" successfully - * and we will get a TX status notification eventually, - * regardless of the value of ret. "ret" only indicates - * whether or not we should update the write pointer. - */ - if (iwl_queue_space(q) < q->high_mark) { - if (wait_write_ptr) { - txq->need_update = 1; - iwl_txq_update_write_ptr(trans, txq); - } else { - iwl_stop_queue(trans, txq); - } - } - spin_unlock(&txq->lock); - return 0; - out_err: - spin_unlock(&txq->lock); - return -1; -} - -static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int err; - bool hw_rfkill; - - trans_pcie->inta_mask = CSR_INI_SET_MASK; - - if (!trans_pcie->irq_requested) { - tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) - iwl_irq_tasklet, (unsigned long)trans); - - iwl_alloc_isr_ict(trans); - - err = request_irq(trans_pcie->irq, iwl_isr_ict, IRQF_SHARED, - DRV_NAME, trans); - if (err) { - IWL_ERR(trans, "Error allocating IRQ %d\n", - trans_pcie->irq); - goto error; - } - - INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish); - trans_pcie->irq_requested = true; - } - - err = iwl_prepare_card_hw(trans); - if (err) { - IWL_ERR(trans, "Error while preparing HW: %d", err); - goto err_free_irq; - } - - iwl_apm_init(trans); - - /* From now on, the op_mode will be kept updated about RF kill state */ - iwl_enable_rfkill_int(trans); - - hw_rfkill = iwl_is_rfkill_set(trans); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - - return err; - -err_free_irq: - free_irq(trans_pcie->irq, trans); -error: - iwl_free_isr_ict(trans); - tasklet_kill(&trans_pcie->irq_tasklet); - return err; -} - -static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, - bool op_mode_leaving) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; - unsigned long flags; - - iwl_apm_stop(trans); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - - if (!op_mode_leaving) { - /* - * Even if we stop the HW, we still want the RF kill - * interrupt - */ - iwl_enable_rfkill_int(trans); - - /* - * Check again since the RF kill state may have changed while - * all the interrupts were disabled, in this case we couldn't - * receive the RF kill interrupt and update the state in the - * op_mode. - */ - hw_rfkill = iwl_is_rfkill_set(trans); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - } -} - -static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - struct sk_buff_head *skbs) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - /* n_bd is usually 256 => n_bd - 1 = 0xff */ - int tfd_num = ssn & (txq->q.n_bd - 1); - int freed = 0; - - spin_lock(&txq->lock); - - if (txq->q.read_ptr != tfd_num) { - IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", - txq_id, txq->q.read_ptr, tfd_num, ssn); - freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); - if (iwl_queue_space(&txq->q) > txq->q.low_mark) - iwl_wake_queue(trans, txq); - } - - spin_unlock(&txq->lock); -} - -static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) -{ - writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); -} - -static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) -{ - writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); -} - -static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) -{ - return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); -} - -static void iwl_trans_pcie_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - trans_pcie->cmd_queue = trans_cfg->cmd_queue; - if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) - trans_pcie->n_no_reclaim_cmds = 0; - else - trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; - if (trans_pcie->n_no_reclaim_cmds) - memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, - trans_pcie->n_no_reclaim_cmds * sizeof(u8)); - - trans_pcie->n_q_to_fifo = trans_cfg->n_queue_to_fifo; - - if (WARN_ON(trans_pcie->n_q_to_fifo > IWL_MAX_HW_QUEUES)) - trans_pcie->n_q_to_fifo = IWL_MAX_HW_QUEUES; - - /* at least the command queue must be mapped */ - WARN_ON(!trans_pcie->n_q_to_fifo); - - memcpy(trans_pcie->setup_q_to_fifo, trans_cfg->queue_to_fifo, - trans_pcie->n_q_to_fifo * sizeof(u8)); - - trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; - if (trans_pcie->rx_buf_size_8k) - trans_pcie->rx_page_order = get_order(8 * 1024); - else - trans_pcie->rx_page_order = get_order(4 * 1024); - - trans_pcie->wd_timeout = - msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); - - trans_pcie->command_names = trans_cfg->command_names; -} - -void iwl_trans_pcie_free(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - iwl_trans_pcie_tx_free(trans); -#ifndef CONFIG_IWLWIFI_IDI - iwl_trans_pcie_rx_free(trans); -#endif - if (trans_pcie->irq_requested == true) { - free_irq(trans_pcie->irq, trans); - iwl_free_isr_ict(trans); - } - - pci_disable_msi(trans_pcie->pci_dev); - iounmap(trans_pcie->hw_base); - pci_release_regions(trans_pcie->pci_dev); - pci_disable_device(trans_pcie->pci_dev); - - kfree(trans); -} - -static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (state) - set_bit(STATUS_TPOWER_PMI, &trans_pcie->status); - else - clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); -} - -#ifdef CONFIG_PM_SLEEP -static int iwl_trans_pcie_suspend(struct iwl_trans *trans) -{ - return 0; -} - -static int iwl_trans_pcie_resume(struct iwl_trans *trans) -{ - bool hw_rfkill; - - iwl_enable_rfkill_int(trans); - - hw_rfkill = iwl_is_rfkill_set(trans); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - - if (!hw_rfkill) - iwl_enable_interrupts(trans); - - return 0; -} -#endif /* CONFIG_PM_SLEEP */ - -#define IWL_FLUSH_WAIT_MS 2000 - -static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq; - struct iwl_queue *q; - int cnt; - unsigned long now = jiffies; - int ret = 0; - - /* waiting for all the tx frames complete might take a while */ - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - if (cnt == trans_pcie->cmd_queue) - continue; - txq = &trans_pcie->txq[cnt]; - q = &txq->q; - while (q->read_ptr != q->write_ptr && !time_after(jiffies, - now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) - msleep(1); - - if (q->read_ptr != q->write_ptr) { - IWL_ERR(trans, "fail to flush all tx fifo queues\n"); - ret = -ETIMEDOUT; - break; - } - } - return ret; -} - -static const char *get_fh_string(int cmd) -{ -#define IWL_CMD(x) case x: return #x - switch (cmd) { - IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); - IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); - IWL_CMD(FH_RSCSR_CHNL0_WPTR); - IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); - IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); - IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); - IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); - IWL_CMD(FH_TSSR_TX_STATUS_REG); - IWL_CMD(FH_TSSR_TX_ERROR_REG); - default: - return "UNKNOWN"; - } -#undef IWL_CMD -} - -int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display) -{ - int i; -#ifdef CONFIG_IWLWIFI_DEBUG - int pos = 0; - size_t bufsz = 0; -#endif - static const u32 fh_tbl[] = { - FH_RSCSR_CHNL0_STTS_WPTR_REG, - FH_RSCSR_CHNL0_RBDCB_BASE_REG, - FH_RSCSR_CHNL0_WPTR, - FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_MEM_RSSR_SHARED_CTRL_REG, - FH_MEM_RSSR_RX_STATUS_REG, - FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, - FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_ERROR_REG - }; -#ifdef CONFIG_IWLWIFI_DEBUG - if (display) { - bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - pos += scnprintf(*buf + pos, bufsz - pos, - "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { - pos += scnprintf(*buf + pos, bufsz - pos, - " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(trans, fh_tbl[i])); - } - return pos; - } -#endif - IWL_ERR(trans, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { - IWL_ERR(trans, " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(trans, fh_tbl[i])); - } - return 0; -} - -static const char *get_csr_string(int cmd) -{ -#define IWL_CMD(x) case x: return #x - switch (cmd) { - IWL_CMD(CSR_HW_IF_CONFIG_REG); - IWL_CMD(CSR_INT_COALESCING); - IWL_CMD(CSR_INT); - IWL_CMD(CSR_INT_MASK); - IWL_CMD(CSR_FH_INT_STATUS); - IWL_CMD(CSR_GPIO_IN); - IWL_CMD(CSR_RESET); - IWL_CMD(CSR_GP_CNTRL); - IWL_CMD(CSR_HW_REV); - IWL_CMD(CSR_EEPROM_REG); - IWL_CMD(CSR_EEPROM_GP); - IWL_CMD(CSR_OTP_GP_REG); - IWL_CMD(CSR_GIO_REG); - IWL_CMD(CSR_GP_UCODE_REG); - IWL_CMD(CSR_GP_DRIVER_REG); - IWL_CMD(CSR_UCODE_DRV_GP1); - IWL_CMD(CSR_UCODE_DRV_GP2); - IWL_CMD(CSR_LED_REG); - IWL_CMD(CSR_DRAM_INT_TBL_REG); - IWL_CMD(CSR_GIO_CHICKEN_BITS); - IWL_CMD(CSR_ANA_PLL_CFG); - IWL_CMD(CSR_HW_REV_WA_REG); - IWL_CMD(CSR_DBG_HPET_MEM_REG); - default: - return "UNKNOWN"; - } -#undef IWL_CMD -} - -void iwl_dump_csr(struct iwl_trans *trans) -{ - int i; - static const u32 csr_tbl[] = { - CSR_HW_IF_CONFIG_REG, - CSR_INT_COALESCING, - CSR_INT, - CSR_INT_MASK, - CSR_FH_INT_STATUS, - CSR_GPIO_IN, - CSR_RESET, - CSR_GP_CNTRL, - CSR_HW_REV, - CSR_EEPROM_REG, - CSR_EEPROM_GP, - CSR_OTP_GP_REG, - CSR_GIO_REG, - CSR_GP_UCODE_REG, - CSR_GP_DRIVER_REG, - CSR_UCODE_DRV_GP1, - CSR_UCODE_DRV_GP2, - CSR_LED_REG, - CSR_DRAM_INT_TBL_REG, - CSR_GIO_CHICKEN_BITS, - CSR_ANA_PLL_CFG, - CSR_HW_REV_WA_REG, - CSR_DBG_HPET_MEM_REG - }; - IWL_ERR(trans, "CSR values:\n"); - IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " - "CSR_INT_PERIODIC_REG)\n"); - for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { - IWL_ERR(trans, " %25s: 0X%08x\n", - get_csr_string(csr_tbl[i]), - iwl_read32(trans, csr_tbl[i])); - } -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -/* create and remove of files */ -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, trans, \ - &iwl_dbgfs_##name##_ops)) \ - return -ENOMEM; \ -} while (0) - -/* file operation */ -#define DEBUGFS_READ_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ - char __user *user_buf, \ - size_t count, loff_t *ppos); - -#define DEBUGFS_WRITE_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos); - - -#define DEBUGFS_READ_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_WRITE_FILE_OPS(name) \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq; - struct iwl_queue *q; - char *buf; - int pos = 0; - int cnt; - int ret; - size_t bufsz; - - bufsz = sizeof(char) * 64 * trans->cfg->base_params->num_of_queues; - - if (!trans_pcie->txq) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { - txq = &trans_pcie->txq[cnt]; - q = &txq->q; - pos += scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u use=%d stop=%d\n", - cnt, q->read_ptr, q->write_ptr, - !!test_bit(cnt, trans_pcie->queue_used), - !!test_bit(cnt, trans_pcie->queue_stopped)); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", - rxq->read); - pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", - rxq->write); - pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", - rxq->free_count); - if (rxq->rb_stts) { - pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", - le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); - } else { - pos += scnprintf(buf + pos, bufsz - pos, - "closed_rb_num: Not Allocated\n"); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_interrupt_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - int pos = 0; - char *buf; - int bufsz = 24 * 64; /* 24 items * 64 char per item */ - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, - "Interrupt Statistics Report:\n"); - - pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", - isr_stats->hw); - pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", - isr_stats->sw); - if (isr_stats->sw || isr_stats->hw) { - pos += scnprintf(buf + pos, bufsz - pos, - "\tLast Restarting Code: 0x%X\n", - isr_stats->err_code); - } -#ifdef CONFIG_IWLWIFI_DEBUG - pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", - isr_stats->sch); - pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", - isr_stats->alive); -#endif - pos += scnprintf(buf + pos, bufsz - pos, - "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); - - pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", - isr_stats->ctkill); - - pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", - isr_stats->wakeup); - - pos += scnprintf(buf + pos, bufsz - pos, - "Rx command responses:\t\t %u\n", isr_stats->rx); - - pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", - isr_stats->tx); - - pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", - isr_stats->unhandled); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_interrupt_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - char buf[8]; - int buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &reset_flag) != 1) - return -EFAULT; - if (reset_flag == 0) - memset(isr_stats, 0, sizeof(*isr_stats)); - - return count; -} - -static ssize_t iwl_dbgfs_csr_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char buf[8]; - int buf_size; - int csr; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &csr) != 1) - return -EFAULT; - - iwl_dump_csr(trans); - - return count; -} - -static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char *buf; - int pos = 0; - ssize_t ret = -EFAULT; - - ret = pos = iwl_dump_fh(trans, &buf, true); - if (buf) { - ret = simple_read_from_buffer(user_buf, - count, ppos, buf, pos); - kfree(buf); - } - - return ret; -} - -static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - - if (!trans->op_mode) - return -EAGAIN; - - iwl_op_mode_nic_error(trans->op_mode); - - return count; -} - -DEBUGFS_READ_WRITE_FILE_OPS(interrupt); -DEBUGFS_READ_FILE_OPS(fh_reg); -DEBUGFS_READ_FILE_OPS(rx_queue); -DEBUGFS_READ_FILE_OPS(tx_queue); -DEBUGFS_WRITE_FILE_OPS(csr); -DEBUGFS_WRITE_FILE_OPS(fw_restart); - -/* - * Create the debugfs files and directories - * - */ -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); - DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); - DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); - DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); - DEBUGFS_ADD_FILE(fw_restart, dir, S_IWUSR); - return 0; -} -#else -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - return 0; -} -#endif /*CONFIG_IWLWIFI_DEBUGFS */ - -static const struct iwl_trans_ops trans_ops_pcie = { - .start_hw = iwl_trans_pcie_start_hw, - .stop_hw = iwl_trans_pcie_stop_hw, - .fw_alive = iwl_trans_pcie_fw_alive, - .start_fw = iwl_trans_pcie_start_fw, - .stop_device = iwl_trans_pcie_stop_device, - - .wowlan_suspend = iwl_trans_pcie_wowlan_suspend, - - .send_cmd = iwl_trans_pcie_send_cmd, - - .tx = iwl_trans_pcie_tx, - .reclaim = iwl_trans_pcie_reclaim, - - .tx_agg_disable = iwl_trans_pcie_tx_agg_disable, - .tx_agg_setup = iwl_trans_pcie_tx_agg_setup, - - .dbgfs_register = iwl_trans_pcie_dbgfs_register, - - .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, - -#ifdef CONFIG_PM_SLEEP - .suspend = iwl_trans_pcie_suspend, - .resume = iwl_trans_pcie_resume, -#endif - .write8 = iwl_trans_pcie_write8, - .write32 = iwl_trans_pcie_write32, - .read32 = iwl_trans_pcie_read32, - .configure = iwl_trans_pcie_configure, - .set_pmi = iwl_trans_pcie_set_pmi, -}; - -struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, - const struct pci_device_id *ent, - const struct iwl_cfg *cfg) -{ - struct iwl_trans_pcie *trans_pcie; - struct iwl_trans *trans; - u16 pci_cmd; - int err; - - trans = kzalloc(sizeof(struct iwl_trans) + - sizeof(struct iwl_trans_pcie), GFP_KERNEL); - - if (WARN_ON(!trans)) - return NULL; - - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - trans->ops = &trans_ops_pcie; - trans->cfg = cfg; - trans_pcie->trans = trans; - spin_lock_init(&trans_pcie->irq_lock); - init_waitqueue_head(&trans_pcie->ucode_write_waitq); - - /* W/A - seems to solve weird behavior. We need to remove this if we - * don't want to stay in L1 all the time. This wastes a lot of power */ - pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | - PCIE_LINK_STATE_CLKPM); - - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_no_pci; - } - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); - if (err) { - err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (!err) - err = pci_set_consistent_dma_mask(pdev, - DMA_BIT_MASK(32)); - /* both attempts failed: */ - if (err) { - dev_printk(KERN_ERR, &pdev->dev, - "No suitable DMA available.\n"); - goto out_pci_disable_device; - } - } - - err = pci_request_regions(pdev, DRV_NAME); - if (err) { - dev_printk(KERN_ERR, &pdev->dev, "pci_request_regions failed"); - goto out_pci_disable_device; - } - - trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); - if (!trans_pcie->hw_base) { - dev_printk(KERN_ERR, &pdev->dev, "pci_ioremap_bar failed"); - err = -ENODEV; - goto out_pci_release_regions; - } - - dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_len = 0x%08llx\n", - (unsigned long long) pci_resource_len(pdev, 0)); - dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_base = %p\n", trans_pcie->hw_base); - - dev_printk(KERN_INFO, &pdev->dev, - "HW Revision ID = 0x%X\n", pdev->revision); - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); - - err = pci_enable_msi(pdev); - if (err) - dev_printk(KERN_ERR, &pdev->dev, - "pci_enable_msi failed(0X%x)", err); - - trans->dev = &pdev->dev; - trans_pcie->irq = pdev->irq; - trans_pcie->pci_dev = pdev; - trans->hw_rev = iwl_read32(trans, CSR_HW_REV); - trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; - snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), - "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); - - /* TODO: Move this away, not needed if not MSI */ - /* enable rfkill interrupt: hw bug w/a */ - pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); - if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { - pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); - } - - /* Initialize the wait queue for commands */ - init_waitqueue_head(&trans->wait_command_queue); - spin_lock_init(&trans->reg_lock); - - return trans; - -out_pci_release_regions: - pci_release_regions(pdev); -out_pci_disable_device: - pci_disable_device(pdev); -out_no_pci: - kfree(trans); - return NULL; -} diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c new file mode 100644 index 000000000000..0cfbf9fb74d5 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/1000.c @@ -0,0 +1,126 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-csr.h" +#include "iwl-agn-hw.h" +#include "cfg.h" + +/* Highest firmware API version supported */ +#define IWL1000_UCODE_API_MAX 5 +#define IWL100_UCODE_API_MAX 5 + +/* Oldest version we won't warn about */ +#define IWL1000_UCODE_API_OK 5 +#define IWL100_UCODE_API_OK 5 + +/* Lowest firmware API version supported */ +#define IWL1000_UCODE_API_MIN 1 +#define IWL100_UCODE_API_MIN 5 + +/* EEPROM version */ +#define EEPROM_1000_TX_POWER_VERSION (4) +#define EEPROM_1000_EEPROM_VERSION (0x15C) + +#define IWL1000_FW_PRE "iwlwifi-1000-" +#define IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE __stringify(api) ".ucode" + +#define IWL100_FW_PRE "iwlwifi-100-" +#define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE __stringify(api) ".ucode" + + +static const struct iwl_base_params iwl1000_base_params = { + .num_of_queues = IWLAGN_NUM_QUEUES, + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .max_ll_items = OTP_MAX_LL_ITEMS_1000, + .shadow_ram_support = false, + .led_compensation = 51, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_WATCHHDOG_DISABLED, + .max_event_log_size = 128, +}; + +static const struct iwl_ht_params iwl1000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ +}; + +#define IWL_DEVICE_1000 \ + .fw_name_pre = IWL1000_FW_PRE, \ + .ucode_api_max = IWL1000_UCODE_API_MAX, \ + .ucode_api_ok = IWL1000_UCODE_API_OK, \ + .ucode_api_min = IWL1000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_1000, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ + .base_params = &iwl1000_base_params, \ + .led_mode = IWL_LED_BLINK + +const struct iwl_cfg iwl1000_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", + IWL_DEVICE_1000, + .ht_params = &iwl1000_ht_params, +}; + +const struct iwl_cfg iwl1000_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", + IWL_DEVICE_1000, +}; + +#define IWL_DEVICE_100 \ + .fw_name_pre = IWL100_FW_PRE, \ + .ucode_api_max = IWL100_UCODE_API_MAX, \ + .ucode_api_ok = IWL100_UCODE_API_OK, \ + .ucode_api_min = IWL100_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_100, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ + .base_params = &iwl1000_base_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .rx_with_siso_diversity = true + +const struct iwl_cfg iwl100_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", + IWL_DEVICE_100, + .ht_params = &iwl1000_ht_params, +}; + +const struct iwl_cfg iwl100_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 100 BG", + IWL_DEVICE_100, +}; + +MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/pcie/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c new file mode 100644 index 000000000000..f6efa122336b --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/2000.c @@ -0,0 +1,225 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "cfg.h" +#include "iwl-commands.h" /* needed for BT for now */ + +/* Highest firmware API version supported */ +#define IWL2030_UCODE_API_MAX 6 +#define IWL2000_UCODE_API_MAX 6 +#define IWL105_UCODE_API_MAX 6 +#define IWL135_UCODE_API_MAX 6 + +/* Oldest version we won't warn about */ +#define IWL2030_UCODE_API_OK 6 +#define IWL2000_UCODE_API_OK 6 +#define IWL105_UCODE_API_OK 6 +#define IWL135_UCODE_API_OK 6 + +/* Lowest firmware API version supported */ +#define IWL2030_UCODE_API_MIN 5 +#define IWL2000_UCODE_API_MIN 5 +#define IWL105_UCODE_API_MIN 5 +#define IWL135_UCODE_API_MIN 5 + +/* EEPROM version */ +#define EEPROM_2000_TX_POWER_VERSION (6) +#define EEPROM_2000_EEPROM_VERSION (0x805) + + +#define IWL2030_FW_PRE "iwlwifi-2030-" +#define IWL2030_MODULE_FIRMWARE(api) IWL2030_FW_PRE __stringify(api) ".ucode" + +#define IWL2000_FW_PRE "iwlwifi-2000-" +#define IWL2000_MODULE_FIRMWARE(api) IWL2000_FW_PRE __stringify(api) ".ucode" + +#define IWL105_FW_PRE "iwlwifi-105-" +#define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode" + +#define IWL135_FW_PRE "iwlwifi-135-" +#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl2000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_2x00, + .shadow_ram_support = true, + .led_compensation = 51, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_DEF_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .hd_v2 = true, +}; + + +static const struct iwl_base_params iwl2030_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_2x00, + .shadow_ram_support = true, + .led_compensation = 57, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .hd_v2 = true, +}; + +static const struct iwl_ht_params iwl2000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ +}; + +static const struct iwl_bt_params iwl2030_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, + .bt_sco_disable = true, + .bt_session_2 = true, +}; + +#define IWL_DEVICE_2000 \ + .fw_name_pre = IWL2000_FW_PRE, \ + .ucode_api_max = IWL2000_UCODE_API_MAX, \ + .ucode_api_ok = IWL2000_UCODE_API_OK, \ + .ucode_api_min = IWL2000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_2000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2000_base_params, \ + .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ + .led_mode = IWL_LED_RF_STATE + +const struct iwl_cfg iwl2000_2bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", + IWL_DEVICE_2000, + .ht_params = &iwl2000_ht_params, +}; + +const struct iwl_cfg iwl2000_2bgn_d_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", + IWL_DEVICE_2000, + .ht_params = &iwl2000_ht_params, +}; + +#define IWL_DEVICE_2030 \ + .fw_name_pre = IWL2030_FW_PRE, \ + .ucode_api_max = IWL2030_UCODE_API_MAX, \ + .ucode_api_ok = IWL2030_UCODE_API_OK, \ + .ucode_api_min = IWL2030_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_2030, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2030_base_params, \ + .bt_params = &iwl2030_bt_params, \ + .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ + .led_mode = IWL_LED_RF_STATE, \ + .adv_pm = true + +const struct iwl_cfg iwl2030_2bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", + IWL_DEVICE_2030, + .ht_params = &iwl2000_ht_params, +}; + +#define IWL_DEVICE_105 \ + .fw_name_pre = IWL105_FW_PRE, \ + .ucode_api_max = IWL105_UCODE_API_MAX, \ + .ucode_api_ok = IWL105_UCODE_API_OK, \ + .ucode_api_min = IWL105_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_105, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2000_base_params, \ + .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ + .led_mode = IWL_LED_RF_STATE, \ + .adv_pm = true, \ + .rx_with_siso_diversity = true + +const struct iwl_cfg iwl105_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", + IWL_DEVICE_105, + .ht_params = &iwl2000_ht_params, +}; + +const struct iwl_cfg iwl105_bgn_d_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", + IWL_DEVICE_105, + .ht_params = &iwl2000_ht_params, +}; + +#define IWL_DEVICE_135 \ + .fw_name_pre = IWL135_FW_PRE, \ + .ucode_api_max = IWL135_UCODE_API_MAX, \ + .ucode_api_ok = IWL135_UCODE_API_OK, \ + .ucode_api_min = IWL135_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_135, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ + .base_params = &iwl2030_base_params, \ + .bt_params = &iwl2030_bt_params, \ + .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ + .led_mode = IWL_LED_RF_STATE, \ + .adv_pm = true, \ + .rx_with_siso_diversity = true + +const struct iwl_cfg iwl135_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", + IWL_DEVICE_135, + .ht_params = &iwl2000_ht_params, +}; + +MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_OK)); +MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_OK)); +MODULE_FIRMWARE(IWL135_MODULE_FIRMWARE(IWL135_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/pcie/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c new file mode 100644 index 000000000000..92e502e6e7e5 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/5000.c @@ -0,0 +1,164 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "iwl-csr.h" +#include "cfg.h" + +/* Highest firmware API version supported */ +#define IWL5000_UCODE_API_MAX 5 +#define IWL5150_UCODE_API_MAX 2 + +/* Oldest version we won't warn about */ +#define IWL5000_UCODE_API_OK 5 +#define IWL5150_UCODE_API_OK 2 + +/* Lowest firmware API version supported */ +#define IWL5000_UCODE_API_MIN 1 +#define IWL5150_UCODE_API_MIN 1 + +/* EEPROM versions */ +#define EEPROM_5000_TX_POWER_VERSION (4) +#define EEPROM_5000_EEPROM_VERSION (0x11A) +#define EEPROM_5050_TX_POWER_VERSION (4) +#define EEPROM_5050_EEPROM_VERSION (0x21E) + +#define IWL5000_FW_PRE "iwlwifi-5000-" +#define IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE __stringify(api) ".ucode" + +#define IWL5150_FW_PRE "iwlwifi-5150-" +#define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl5000_base_params = { + .eeprom_size = IWLAGN_EEPROM_IMG_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, + .led_compensation = 51, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_WATCHHDOG_DISABLED, + .max_event_log_size = 512, + .no_idle_support = true, +}; + +static const struct iwl_ht_params iwl5000_ht_params = { + .ht_greenfield_support = true, +}; + +#define IWL_DEVICE_5000 \ + .fw_name_pre = IWL5000_FW_PRE, \ + .ucode_api_max = IWL5000_UCODE_API_MAX, \ + .ucode_api_ok = IWL5000_UCODE_API_OK, \ + .ucode_api_min = IWL5000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_5000, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_5000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ + .base_params = &iwl5000_base_params, \ + .led_mode = IWL_LED_BLINK + +const struct iwl_cfg iwl5300_agn_cfg = { + .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", + IWL_DEVICE_5000, + /* at least EEPROM 0x11A has wrong info */ + .valid_tx_ant = ANT_ABC, /* .cfg overwrite */ + .valid_rx_ant = ANT_ABC, /* .cfg overwrite */ + .ht_params = &iwl5000_ht_params, +}; + +const struct iwl_cfg iwl5100_bgn_cfg = { + .name = "Intel(R) WiFi Link 5100 BGN", + IWL_DEVICE_5000, + .valid_tx_ant = ANT_B, /* .cfg overwrite */ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ + .ht_params = &iwl5000_ht_params, +}; + +const struct iwl_cfg iwl5100_abg_cfg = { + .name = "Intel(R) WiFi Link 5100 ABG", + IWL_DEVICE_5000, + .valid_tx_ant = ANT_B, /* .cfg overwrite */ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ +}; + +const struct iwl_cfg iwl5100_agn_cfg = { + .name = "Intel(R) WiFi Link 5100 AGN", + IWL_DEVICE_5000, + .valid_tx_ant = ANT_B, /* .cfg overwrite */ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ + .ht_params = &iwl5000_ht_params, +}; + +const struct iwl_cfg iwl5350_agn_cfg = { + .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", + .fw_name_pre = IWL5000_FW_PRE, + .ucode_api_max = IWL5000_UCODE_API_MAX, + .ucode_api_ok = IWL5000_UCODE_API_OK, + .ucode_api_min = IWL5000_UCODE_API_MIN, + .device_family = IWL_DEVICE_FAMILY_5000, + .max_inst_size = IWLAGN_RTC_INST_SIZE, + .max_data_size = IWLAGN_RTC_DATA_SIZE, + .eeprom_ver = EEPROM_5050_EEPROM_VERSION, + .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, + .base_params = &iwl5000_base_params, + .ht_params = &iwl5000_ht_params, + .led_mode = IWL_LED_BLINK, + .internal_wimax_coex = true, +}; + +#define IWL_DEVICE_5150 \ + .fw_name_pre = IWL5150_FW_PRE, \ + .ucode_api_max = IWL5150_UCODE_API_MAX, \ + .ucode_api_ok = IWL5150_UCODE_API_OK, \ + .ucode_api_min = IWL5150_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_5150, \ + .max_inst_size = IWLAGN_RTC_INST_SIZE, \ + .max_data_size = IWLAGN_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_5050_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ + .base_params = &iwl5000_base_params, \ + .no_xtal_calib = true, \ + .led_mode = IWL_LED_BLINK, \ + .internal_wimax_coex = true + +const struct iwl_cfg iwl5150_agn_cfg = { + .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", + IWL_DEVICE_5150, + .ht_params = &iwl5000_ht_params, + +}; + +const struct iwl_cfg iwl5150_abg_cfg = { + .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", + IWL_DEVICE_5150, +}; + +MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c new file mode 100644 index 000000000000..7360cd9e0148 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/6000.c @@ -0,0 +1,363 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include "iwl-config.h" +#include "iwl-agn-hw.h" +#include "cfg.h" +#include "iwl-commands.h" /* needed for BT for now */ + +/* Highest firmware API version supported */ +#define IWL6000_UCODE_API_MAX 6 +#define IWL6050_UCODE_API_MAX 5 +#define IWL6000G2_UCODE_API_MAX 6 + +/* Oldest version we won't warn about */ +#define IWL6000_UCODE_API_OK 4 +#define IWL6000G2_UCODE_API_OK 5 +#define IWL6050_UCODE_API_OK 5 +#define IWL6000G2B_UCODE_API_OK 6 + +/* Lowest firmware API version supported */ +#define IWL6000_UCODE_API_MIN 4 +#define IWL6050_UCODE_API_MIN 4 +#define IWL6000G2_UCODE_API_MIN 4 + +/* EEPROM versions */ +#define EEPROM_6000_TX_POWER_VERSION (4) +#define EEPROM_6000_EEPROM_VERSION (0x423) +#define EEPROM_6050_TX_POWER_VERSION (4) +#define EEPROM_6050_EEPROM_VERSION (0x532) +#define EEPROM_6150_TX_POWER_VERSION (6) +#define EEPROM_6150_EEPROM_VERSION (0x553) +#define EEPROM_6005_TX_POWER_VERSION (6) +#define EEPROM_6005_EEPROM_VERSION (0x709) +#define EEPROM_6030_TX_POWER_VERSION (6) +#define EEPROM_6030_EEPROM_VERSION (0x709) +#define EEPROM_6035_TX_POWER_VERSION (6) +#define EEPROM_6035_EEPROM_VERSION (0x753) + +#define IWL6000_FW_PRE "iwlwifi-6000-" +#define IWL6000_MODULE_FIRMWARE(api) IWL6000_FW_PRE __stringify(api) ".ucode" + +#define IWL6050_FW_PRE "iwlwifi-6050-" +#define IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE __stringify(api) ".ucode" + +#define IWL6005_FW_PRE "iwlwifi-6000g2a-" +#define IWL6005_MODULE_FIRMWARE(api) IWL6005_FW_PRE __stringify(api) ".ucode" + +#define IWL6030_FW_PRE "iwlwifi-6000g2b-" +#define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE __stringify(api) ".ucode" + +static const struct iwl_base_params iwl6000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .led_compensation = 51, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_DEF_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ +}; + +static const struct iwl_base_params iwl6050_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_6x50, + .shadow_ram_support = true, + .led_compensation = 51, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1500, + .wd_timeout = IWL_DEF_WD_TIMEOUT, + .max_event_log_size = 1024, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ +}; + +static const struct iwl_base_params iwl6000_g2_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .led_compensation = 57, + .adv_thermal_throttle = true, + .support_ct_kill_exit = true, + .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .chain_noise_scale = 1000, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ +}; + +static const struct iwl_ht_params iwl6000_ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ +}; + +static const struct iwl_bt_params iwl6000_bt_params = { + /* Due to bluetooth, we transmit 2.4 GHz probes only on antenna A */ + .advanced_bt_coexist = true, + .agg_time_limit = BT_AGG_THRESHOLD_DEF, + .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, + .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, + .bt_sco_disable = true, +}; + +#define IWL_DEVICE_6005 \ + .fw_name_pre = IWL6005_FW_PRE, \ + .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000G2_UCODE_API_OK, \ + .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6005, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_6005_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ + .base_params = &iwl6000_g2_base_params, \ + .need_temp_offset_calib = true, \ + .led_mode = IWL_LED_RF_STATE + +const struct iwl_cfg iwl6005_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG", + IWL_DEVICE_6005, +}; + +const struct iwl_cfg iwl6005_2bg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205 BG", + IWL_DEVICE_6005, +}; + +const struct iwl_cfg iwl6005_2agn_sff_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2agn_d_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2agn_mow1_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6005_2agn_mow2_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN", + IWL_DEVICE_6005, + .ht_params = &iwl6000_ht_params, +}; + +#define IWL_DEVICE_6030 \ + .fw_name_pre = IWL6030_FW_PRE, \ + .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000G2B_UCODE_API_OK, \ + .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6030, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_6030_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ + .base_params = &iwl6000_g2_base_params, \ + .bt_params = &iwl6000_bt_params, \ + .need_temp_offset_calib = true, \ + .led_mode = IWL_LED_RF_STATE, \ + .adv_pm = true \ + +const struct iwl_cfg iwl6030_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6030_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG", + IWL_DEVICE_6030, +}; + +const struct iwl_cfg iwl6030_2bgn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6030_2bg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6230 BG", + IWL_DEVICE_6030, +}; + +const struct iwl_cfg iwl6035_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl1030_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl1030_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 1030 BG", + IWL_DEVICE_6030, +}; + +const struct iwl_cfg iwl130_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 130 BGN", + IWL_DEVICE_6030, + .ht_params = &iwl6000_ht_params, + .rx_with_siso_diversity = true, +}; + +const struct iwl_cfg iwl130_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N 130 BG", + IWL_DEVICE_6030, + .rx_with_siso_diversity = true, +}; + +/* + * "i": Internal configuration, use internal Power Amplifier + */ +#define IWL_DEVICE_6000i \ + .fw_name_pre = IWL6000_FW_PRE, \ + .ucode_api_max = IWL6000_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000_UCODE_API_OK, \ + .ucode_api_min = IWL6000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6000i, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ + .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ + .eeprom_ver = EEPROM_6000_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ + .base_params = &iwl6000_base_params, \ + .led_mode = IWL_LED_BLINK + +const struct iwl_cfg iwl6000i_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", + IWL_DEVICE_6000i, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6000i_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", + IWL_DEVICE_6000i, +}; + +const struct iwl_cfg iwl6000i_2bg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", + IWL_DEVICE_6000i, +}; + +#define IWL_DEVICE_6050 \ + .fw_name_pre = IWL6050_FW_PRE, \ + .ucode_api_max = IWL6050_UCODE_API_MAX, \ + .ucode_api_min = IWL6050_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6050, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \ + .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \ + .eeprom_ver = EEPROM_6050_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ + .base_params = &iwl6050_base_params, \ + .led_mode = IWL_LED_BLINK, \ + .internal_wimax_coex = true + +const struct iwl_cfg iwl6050_2agn_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", + IWL_DEVICE_6050, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6050_2abg_cfg = { + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", + IWL_DEVICE_6050, +}; + +#define IWL_DEVICE_6150 \ + .fw_name_pre = IWL6050_FW_PRE, \ + .ucode_api_max = IWL6050_UCODE_API_MAX, \ + .ucode_api_min = IWL6050_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_6150, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .eeprom_ver = EEPROM_6150_EEPROM_VERSION, \ + .eeprom_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ + .base_params = &iwl6050_base_params, \ + .led_mode = IWL_LED_BLINK, \ + .internal_wimax_coex = true + +const struct iwl_cfg iwl6150_bgn_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", + IWL_DEVICE_6150, + .ht_params = &iwl6000_ht_params, +}; + +const struct iwl_cfg iwl6150_bg_cfg = { + .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG", + IWL_DEVICE_6150, +}; + +const struct iwl_cfg iwl6000_3agn_cfg = { + .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", + .fw_name_pre = IWL6000_FW_PRE, + .ucode_api_max = IWL6000_UCODE_API_MAX, + .ucode_api_ok = IWL6000_UCODE_API_OK, + .ucode_api_min = IWL6000_UCODE_API_MIN, + .device_family = IWL_DEVICE_FAMILY_6000, + .max_inst_size = IWL60_RTC_INST_SIZE, + .max_data_size = IWL60_RTC_DATA_SIZE, + .eeprom_ver = EEPROM_6000_EEPROM_VERSION, + .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, + .base_params = &iwl6000_base_params, + .ht_params = &iwl6000_ht_params, + .led_mode = IWL_LED_BLINK, +}; + +MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_OK)); +MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2B_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/pcie/cfg.h b/drivers/net/wireless/iwlwifi/pcie/cfg.h new file mode 100644 index 000000000000..82152311d73b --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/cfg.h @@ -0,0 +1,113 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_pci_h__ +#define __iwl_pci_h__ + + +/* + * This file declares the config structures for all devices. + */ + +extern const struct iwl_cfg iwl5300_agn_cfg; +extern const struct iwl_cfg iwl5100_agn_cfg; +extern const struct iwl_cfg iwl5350_agn_cfg; +extern const struct iwl_cfg iwl5100_bgn_cfg; +extern const struct iwl_cfg iwl5100_abg_cfg; +extern const struct iwl_cfg iwl5150_agn_cfg; +extern const struct iwl_cfg iwl5150_abg_cfg; +extern const struct iwl_cfg iwl6005_2agn_cfg; +extern const struct iwl_cfg iwl6005_2abg_cfg; +extern const struct iwl_cfg iwl6005_2bg_cfg; +extern const struct iwl_cfg iwl6005_2agn_sff_cfg; +extern const struct iwl_cfg iwl6005_2agn_d_cfg; +extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; +extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; +extern const struct iwl_cfg iwl1030_bgn_cfg; +extern const struct iwl_cfg iwl1030_bg_cfg; +extern const struct iwl_cfg iwl6030_2agn_cfg; +extern const struct iwl_cfg iwl6030_2abg_cfg; +extern const struct iwl_cfg iwl6030_2bgn_cfg; +extern const struct iwl_cfg iwl6030_2bg_cfg; +extern const struct iwl_cfg iwl6000i_2agn_cfg; +extern const struct iwl_cfg iwl6000i_2abg_cfg; +extern const struct iwl_cfg iwl6000i_2bg_cfg; +extern const struct iwl_cfg iwl6000_3agn_cfg; +extern const struct iwl_cfg iwl6050_2agn_cfg; +extern const struct iwl_cfg iwl6050_2abg_cfg; +extern const struct iwl_cfg iwl6150_bgn_cfg; +extern const struct iwl_cfg iwl6150_bg_cfg; +extern const struct iwl_cfg iwl1000_bgn_cfg; +extern const struct iwl_cfg iwl1000_bg_cfg; +extern const struct iwl_cfg iwl100_bgn_cfg; +extern const struct iwl_cfg iwl100_bg_cfg; +extern const struct iwl_cfg iwl130_bgn_cfg; +extern const struct iwl_cfg iwl130_bg_cfg; +extern const struct iwl_cfg iwl2000_2bgn_cfg; +extern const struct iwl_cfg iwl2000_2bgn_d_cfg; +extern const struct iwl_cfg iwl2030_2bgn_cfg; +extern const struct iwl_cfg iwl6035_2agn_cfg; +extern const struct iwl_cfg iwl105_bgn_cfg; +extern const struct iwl_cfg iwl105_bgn_d_cfg; +extern const struct iwl_cfg iwl135_bgn_cfg; + +#endif /* __iwl_pci_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c new file mode 100644 index 000000000000..f4c3500b68c6 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -0,0 +1,380 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +#include "iwl-trans.h" +#include "iwl-drv.h" +#include "iwl-trans.h" + +#include "cfg.h" +#include "internal.h" + +#define IWL_PCI_DEVICE(dev, subdev, cfg) \ + .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ + .driver_data = (kernel_ulong_t)&(cfg) + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { + {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ + +/* 5300 Series WiFi */ + {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ + +/* 5350 Series WiFi/WiMax */ + {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ + +/* 5150 Series Wifi/WiMax */ + {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ + + {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ + +/* 6x00 Series */ + {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, + +/* 6x05 Series */ + {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */ + {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */ + +/* 6x30 Series */ + {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)}, + {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)}, + {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)}, + +/* 6x50 WiFi/WiMax Series */ + {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, + +/* 6150 WiFi/WiMax Series */ + {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)}, + {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)}, + +/* 1000 Series WiFi */ + {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, + +/* 100 Series WiFi */ + {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)}, + {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)}, + {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)}, + +/* 130 Series WiFi */ + {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)}, + {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)}, + +/* 2x00 Series */ + {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, + +/* 2x30 Series */ + {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, + +/* 6x35 Series */ + {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, + +/* 105 Series */ + {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, + +/* 135 Series */ + {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, + + {0} +}; +MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); + +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + +#ifndef CONFIG_IWLWIFI_IDI + +static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); + struct iwl_trans *iwl_trans; + struct iwl_trans_pcie *trans_pcie; + + iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg); + if (iwl_trans == NULL) + return -ENOMEM; + + pci_set_drvdata(pdev, iwl_trans); + + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans); + trans_pcie->drv = iwl_drv_start(iwl_trans, cfg); + if (!trans_pcie->drv) + goto out_free_trans; + + return 0; + +out_free_trans: + iwl_trans_pcie_free(iwl_trans); + pci_set_drvdata(pdev, NULL); + return -EFAULT; +} + +static void __devexit iwl_pci_remove(struct pci_dev *pdev) +{ + struct iwl_trans *trans = pci_get_drvdata(pdev); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_drv_stop(trans_pcie->drv); + iwl_trans_pcie_free(trans); + + pci_set_drvdata(pdev, NULL); +} + +#endif /* CONFIG_IWLWIFI_IDI */ + +#ifdef CONFIG_PM_SLEEP + +static int iwl_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); + + /* Before you put code here, think about WoWLAN. You cannot check here + * whether WoWLAN is enabled or not, and your code will run even if + * WoWLAN is enabled - don't kill the NIC, someone may need it in Sx. + */ + + return iwl_trans_suspend(iwl_trans); +} + +static int iwl_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_trans *iwl_trans = pci_get_drvdata(pdev); + + /* Before you put code here, think about WoWLAN. You cannot check here + * whether WoWLAN is enabled or not, and your code will run even if + * WoWLAN is enabled - the NIC may be alive. + */ + + /* + * We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + return iwl_trans_resume(iwl_trans); +} + +static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); + +#define IWL_PM_OPS (&iwl_dev_pm_ops) + +#else + +#define IWL_PM_OPS NULL + +#endif + +#ifdef CONFIG_IWLWIFI_IDI +/* + * Defined externally in iwl-idi.c + */ +int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +void __devexit iwl_pci_remove(struct pci_dev *pdev); + +#endif /* CONFIG_IWLWIFI_IDI */ + +static struct pci_driver iwl_pci_driver = { + .name = DRV_NAME, + .id_table = iwl_hw_card_ids, + .probe = iwl_pci_probe, + .remove = __devexit_p(iwl_pci_remove), + .driver.pm = IWL_PM_OPS, +}; + +int __must_check iwl_pci_register_driver(void) +{ + int ret; + ret = pci_register_driver(&iwl_pci_driver); + if (ret) + pr_err("Unable to initialize PCI module\n"); + + return ret; +} + +void iwl_pci_unregister_driver(void) +{ + pci_unregister_driver(&iwl_pci_driver); +} diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h new file mode 100644 index 000000000000..f027769933d9 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -0,0 +1,444 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#ifndef __iwl_trans_int_pcie_h__ +#define __iwl_trans_int_pcie_h__ + +#include +#include +#include +#include +#include +#include + +#include "iwl-fh.h" +#include "iwl-csr.h" +#include "iwl-trans.h" +#include "iwl-debug.h" +#include "iwl-io.h" +#include "iwl-op-mode.h" + +struct iwl_host_cmd; + +/*This file includes the declaration that are internal to the + * trans_pcie layer */ + +struct iwl_rx_mem_buffer { + dma_addr_t page_dma; + struct page *page; + struct list_head list; +}; + +/** + * struct isr_statistics - interrupt statistics + * + */ +struct isr_statistics { + u32 hw; + u32 sw; + u32 err_code; + u32 sch; + u32 alive; + u32 rfkill; + u32 ctkill; + u32 wakeup; + u32 rx; + u32 tx; + u32 unhandled; +}; + +/** + * struct iwl_rx_queue - Rx queue + * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) + * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) + * @pool: + * @queue: + * @read: Shared index to newest available Rx buffer + * @write: Shared index to oldest written Rx packet + * @free_count: Number of pre-allocated buffers in rx_free + * @write_actual: + * @rx_free: list of free SKBs for use + * @rx_used: List of Rx buffers with no SKB + * @need_update: flag to indicate we need to update read/write index + * @rb_stts: driver's pointer to receive buffer status + * @rb_stts_dma: bus address of receive buffer status + * @lock: + * + * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers + */ +struct iwl_rx_queue { + __le32 *bd; + dma_addr_t bd_dma; + struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; + struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; + u32 read; + u32 write; + u32 free_count; + u32 write_actual; + struct list_head rx_free; + struct list_head rx_used; + int need_update; + struct iwl_rb_status *rb_stts; + dma_addr_t rb_stts_dma; + spinlock_t lock; +}; + +struct iwl_dma_ptr { + dma_addr_t dma; + void *addr; + size_t size; +}; + +/** + * iwl_queue_inc_wrap - increment queue index, wrap back to beginning + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl_queue_inc_wrap(int index, int n_bd) +{ + return ++index & (n_bd - 1); +} + +/** + * iwl_queue_dec_wrap - decrement queue index, wrap back to end + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl_queue_dec_wrap(int index, int n_bd) +{ + return --index & (n_bd - 1); +} + +struct iwl_cmd_meta { + /* only for SYNC commands, iff the reply skb is wanted */ + struct iwl_host_cmd *source; + + DEFINE_DMA_UNMAP_ADDR(mapping); + DEFINE_DMA_UNMAP_LEN(len); + + u32 flags; +}; + +/* + * Generic queue structure + * + * Contains common data for Rx and Tx queues. + * + * Note the difference between n_bd and n_window: the hardware + * always assumes 256 descriptors, so n_bd is always 256 (unless + * there might be HW changes in the future). For the normal TX + * queues, n_window, which is the size of the software queue data + * is also 256; however, for the command queue, n_window is only + * 32 since we don't need so many commands pending. Since the HW + * still uses 256 BDs for DMA though, n_bd stays 256. As a result, + * the software buffers (in the variables @meta, @txb in struct + * iwl_tx_queue) only have 32 entries, while the HW buffers (@tfds + * in the same struct) have 256. + * This means that we end up with the following: + * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | + * SW entries: | 0 | ... | 31 | + * where N is a number between 0 and 7. This means that the SW + * data is a window overlayed over the HW queue. + */ +struct iwl_queue { + int n_bd; /* number of BDs in this queue */ + int write_ptr; /* 1-st empty entry (index) host_w*/ + int read_ptr; /* last used entry (index) host_r*/ + /* use for monitoring and recovering the stuck queue */ + dma_addr_t dma_addr; /* physical addr for BD's */ + int n_window; /* safe queue window */ + u32 id; + int low_mark; /* low watermark, resume queue if free + * space more than this */ + int high_mark; /* high watermark, stop queue if free + * space less than this */ +}; + +#define TFD_TX_CMD_SLOTS 256 +#define TFD_CMD_SLOTS 32 + +struct iwl_pcie_tx_queue_entry { + struct iwl_device_cmd *cmd; + struct sk_buff *skb; + struct iwl_cmd_meta meta; +}; + +/** + * struct iwl_tx_queue - Tx Queue for DMA + * @q: generic Rx/Tx queue descriptor + * @tfds: transmit frame descriptors (DMA memory) + * @entries: transmit entries (driver state) + * @lock: queue lock + * @stuck_timer: timer that fires if queue gets stuck + * @trans_pcie: pointer back to transport (for timer) + * @need_update: indicates need to update read/write index + * @active: stores if queue is active + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + */ +struct iwl_tx_queue { + struct iwl_queue q; + struct iwl_tfd *tfds; + struct iwl_pcie_tx_queue_entry *entries; + spinlock_t lock; + struct timer_list stuck_timer; + struct iwl_trans_pcie *trans_pcie; + u8 need_update; + u8 active; +}; + +/** + * struct iwl_trans_pcie - PCIe transport specific data + * @rxq: all the RX queue data + * @rx_replenish: work that will be called when buffers need to be allocated + * @drv - pointer to iwl_drv + * @trans: pointer to the generic transport area + * @irq - the irq number for the device + * @irq_requested: true when the irq has been requested + * @scd_base_addr: scheduler sram base address in SRAM + * @scd_bc_tbls: pointer to the byte count table of the scheduler + * @kw: keep warm address + * @pci_dev: basic pci-network driver stuff + * @hw_base: pci hardware address support + * @ucode_write_complete: indicates that the ucode has been copied. + * @ucode_write_waitq: wait queue for uCode load + * @status - transport specific status flags + * @cmd_queue - command queue number + * @rx_buf_size_8k: 8 kB RX buffer size + * @rx_page_order: page order for receive buffer size + * @wd_timeout: queue watchdog timeout (jiffies) + */ +struct iwl_trans_pcie { + struct iwl_rx_queue rxq; + struct work_struct rx_replenish; + struct iwl_trans *trans; + struct iwl_drv *drv; + + /* INT ICT Table */ + __le32 *ict_tbl; + dma_addr_t ict_tbl_dma; + int ict_index; + u32 inta; + bool use_ict; + bool irq_requested; + struct tasklet_struct irq_tasklet; + struct isr_statistics isr_stats; + + unsigned int irq; + spinlock_t irq_lock; + u32 inta_mask; + u32 scd_base_addr; + struct iwl_dma_ptr scd_bc_tbls; + struct iwl_dma_ptr kw; + + struct iwl_tx_queue *txq; + unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + + /* PCI bus related data */ + struct pci_dev *pci_dev; + void __iomem *hw_base; + + bool ucode_write_complete; + wait_queue_head_t ucode_write_waitq; + unsigned long status; + u8 cmd_queue; + u8 n_no_reclaim_cmds; + u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; + u8 setup_q_to_fifo[IWL_MAX_HW_QUEUES]; + u8 n_q_to_fifo; + + bool rx_buf_size_8k; + u32 rx_page_order; + + const char **command_names; + + /* queue watchdog */ + unsigned long wd_timeout; +}; + +/***************************************************** +* DRIVER STATUS FUNCTIONS +******************************************************/ +#define STATUS_HCMD_ACTIVE 0 +#define STATUS_DEVICE_ENABLED 1 +#define STATUS_TPOWER_PMI 2 +#define STATUS_INT_ENABLED 3 + +#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ + ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) + +static inline struct iwl_trans * +iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) +{ + return container_of((void *)trans_pcie, struct iwl_trans, + trans_specific); +} + +struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, + const struct pci_device_id *ent, + const struct iwl_cfg *cfg); +void iwl_trans_pcie_free(struct iwl_trans *trans); + +/***************************************************** +* RX +******************************************************/ +void iwl_bg_rx_replenish(struct work_struct *data); +void iwl_irq_tasklet(struct iwl_trans *trans); +void iwlagn_rx_replenish(struct iwl_trans *trans); +void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, + struct iwl_rx_queue *q); + +/***************************************************** +* ICT +******************************************************/ +void iwl_reset_ict(struct iwl_trans *trans); +void iwl_disable_ict(struct iwl_trans *trans); +int iwl_alloc_isr_ict(struct iwl_trans *trans); +void iwl_free_isr_ict(struct iwl_trans *trans); +irqreturn_t iwl_isr_ict(int irq, void *data); + +/***************************************************** +* TX / HCMD +******************************************************/ +void iwl_txq_update_write_ptr(struct iwl_trans *trans, + struct iwl_tx_queue *txq); +int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, + struct iwl_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset); +int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id); +int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +void iwl_tx_cmd_complete(struct iwl_trans *trans, + struct iwl_rx_cmd_buffer *rxb, int handler_status); +void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_tx_queue *txq, + u16 byte_cnt); +void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int queue); +void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); +void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, + struct iwl_tx_queue *txq, + int tx_fifo_id, bool active); +void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn); +void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, + enum dma_data_direction dma_dir); +int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, + struct sk_buff_head *skbs); +int iwl_queue_space(const struct iwl_queue *q); + +/***************************************************** +* Error handling +******************************************************/ +int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display); +void iwl_dump_csr(struct iwl_trans *trans); + +/***************************************************** +* Helpers +******************************************************/ +static inline void iwl_disable_interrupts(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); + + /* disable interrupts from uCode/NIC to host */ + iwl_write32(trans, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl_write32(trans, CSR_INT, 0xffffffff); + iwl_write32(trans, CSR_FH_INT_STATUS, 0xffffffff); + IWL_DEBUG_ISR(trans, "Disabled interrupts\n"); +} + +static inline void iwl_enable_interrupts(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &trans_pcie->status); + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); +} + +static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) +{ + IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); + iwl_write32(trans, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); +} + +static inline void iwl_wake_queue(struct iwl_trans *trans, + struct iwl_tx_queue *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (test_and_clear_bit(txq->q.id, trans_pcie->queue_stopped)) { + IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d\n", txq->q.id); + iwl_op_mode_queue_not_full(trans->op_mode, txq->q.id); + } +} + +static inline void iwl_stop_queue(struct iwl_trans *trans, + struct iwl_tx_queue *txq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!test_and_set_bit(txq->q.id, trans_pcie->queue_stopped)) { + iwl_op_mode_queue_full(trans->op_mode, txq->q.id); + IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d\n", txq->q.id); + } else + IWL_DEBUG_TX_QUEUES(trans, "hwq %d already stopped\n", + txq->q.id); +} + +static inline int iwl_queue_used(const struct iwl_queue *q, int i) +{ + return q->write_ptr >= q->read_ptr ? + (i >= q->read_ptr && i < q->write_ptr) : + !(i < q->read_ptr && i >= q->write_ptr); +} + +static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) +{ + return index & (q->n_window - 1); +} + +static inline const char * +trans_pcie_get_cmd_string(struct iwl_trans_pcie *trans_pcie, u8 cmd) +{ + if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) + return "UNKNOWN"; + return trans_pcie->command_names[cmd]; +} + +static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) +{ + return !(iwl_read32(trans, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); +} + +#endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c new file mode 100644 index 000000000000..d6860c070c16 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -0,0 +1,1058 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include + +#include "iwl-prph.h" +#include "iwl-io.h" +#include "internal.h" +#include "iwl-op-mode.h" + +#ifdef CONFIG_IWLWIFI_IDI +#include "iwl-amfh.h" +#endif + +/****************************************************************************** + * + * RX path functions + * + ******************************************************************************/ + +/* + * Rx theory of operation + * + * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), + * each of which point to Receive Buffers to be filled by the NIC. These get + * used not only for Rx frames, but for any command response or notification + * from the NIC. The driver and NIC manage the Rx buffers by means + * of indexes into the circular buffer. + * + * Rx Queue Indexes + * The host/firmware share two index registers for managing the Rx buffers. + * + * The READ index maps to the first position that the firmware may be writing + * to -- the driver can read up to (but not including) this position and get + * good data. + * The READ index is managed by the firmware once the card is enabled. + * + * The WRITE index maps to the last position the driver has read from -- the + * position preceding WRITE is the last slot the firmware can place a packet. + * + * The queue is empty (no good data) if WRITE = READ - 1, and is full if + * WRITE = READ. + * + * During initialization, the host sets up the READ queue position to the first + * INDEX position, and WRITE to the last (READ - 1 wrapped) + * + * When the firmware places a packet in a buffer, it will advance the READ index + * and fire the RX interrupt. The driver can then query the READ index and + * process as many packets as possible, moving the WRITE index forward as it + * resets the Rx queue buffers with new memory. + * + * The management in the driver is as follows: + * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When + * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replenish the iwl->rxq->rx_free. + * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the + * iwl->rxq is replenished and the READ INDEX is updated (updating the + * 'processed' and 'read' driver indexes as well) + * + A received packet is processed and handed to the kernel network stack, + * detached from the iwl->rxq. The driver 'processed' index is updated. + * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ + * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * iwl_rx_queue_alloc() Allocates rx_free + * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls + * iwl_rx_queue_restock + * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx + * queue, updates firmware pointers, and updates + * the WRITE index. If insufficient rx_free buffers + * are available, schedules iwl_rx_replenish + * + * -- enable interrupts -- + * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the + * READ INDEX, detaching the SKB from the pool. + * Moves the packet buffer from queue to rx_used. + * Calls iwl_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/** + * iwl_rx_queue_space - Return number of free slots available in queue. + */ +static int iwl_rx_queue_space(const struct iwl_rx_queue *q) +{ + int s = q->read - q->write; + if (s <= 0) + s += RX_QUEUE_SIZE; + /* keep some buffer to not confuse full and empty queue */ + s -= 2; + if (s < 0) + s = 0; + return s; +} + +/** + * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue + */ +void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, + struct iwl_rx_queue *q) +{ + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&q->lock, flags); + + if (q->need_update == 0) + goto exit_unlock; + + if (trans->cfg->base_params->shadow_reg_enable) { + /* shadow register enabled */ + /* Device expects a multiple of 8 */ + q->write_actual = (q->write & ~0x7); + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, q->write_actual); + } else { + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + /* If power-saving is in use, make sure device is awake */ + if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, + "Rx queue requesting wakeup," + " GP1 = 0x%x\n", reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + goto exit_unlock; + } + + q->write_actual = (q->write & ~0x7); + iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, + q->write_actual); + + /* Else device is assumed to be awake */ + } else { + /* Device expects a multiple of 8 */ + q->write_actual = (q->write & ~0x7); + iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, + q->write_actual); + } + } + q->need_update = 0; + + exit_unlock: + spin_unlock_irqrestore(&q->lock, flags); +} + +/** + * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) +{ + return cpu_to_le32((u32)(dma_addr >> 8)); +} + +/** + * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool + * + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can, pulling from rx_free. + * + * This moves the 'write' index forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +static void iwlagn_rx_queue_restock(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct list_head *element; + struct iwl_rx_mem_buffer *rxb; + unsigned long flags; + + spin_lock_irqsave(&rxq->lock, flags); + while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + /* The overwritten rxb must be a used one */ + rxb = rxq->queue[rxq->write]; + BUG_ON(rxb && rxb->page); + + /* Get next free Rx buffer, remove from free list */ + element = rxq->rx_free.next; + rxb = list_entry(element, struct iwl_rx_mem_buffer, list); + list_del(element); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma); + rxq->queue[rxq->write] = rxb; + rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; + rxq->free_count--; + } + spin_unlock_irqrestore(&rxq->lock, flags); + /* If the pre-allocated buffer pool is dropping low, schedule to + * refill it */ + if (rxq->free_count <= RX_LOW_WATERMARK) + schedule_work(&trans_pcie->rx_replenish); + + + /* If we've added more space for the firmware to place data, tell it. + * Increment device's write pointer in multiples of 8. */ + if (rxq->write_actual != (rxq->write & ~0x7)) { + spin_lock_irqsave(&rxq->lock, flags); + rxq->need_update = 1; + spin_unlock_irqrestore(&rxq->lock, flags); + iwl_rx_queue_update_write_ptr(trans, rxq); + } +} + +/** + * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free + * + * When moving to rx_free an SKB is allocated for the slot. + * + * Also restock the Rx queue via iwl_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct list_head *element; + struct iwl_rx_mem_buffer *rxb; + struct page *page; + unsigned long flags; + gfp_t gfp_mask = priority; + + while (1) { + spin_lock_irqsave(&rxq->lock, flags); + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + return; + } + spin_unlock_irqrestore(&rxq->lock, flags); + + if (rxq->free_count > RX_LOW_WATERMARK) + gfp_mask |= __GFP_NOWARN; + + if (trans_pcie->rx_page_order > 0) + gfp_mask |= __GFP_COMP; + + /* Alloc a new receive buffer */ + page = alloc_pages(gfp_mask, trans_pcie->rx_page_order); + if (!page) { + if (net_ratelimit()) + IWL_DEBUG_INFO(trans, "alloc_pages failed, " + "order: %d\n", + trans_pcie->rx_page_order); + + if ((rxq->free_count <= RX_LOW_WATERMARK) && + net_ratelimit()) + IWL_CRIT(trans, "Failed to alloc_pages with %s." + "Only %u free buffers remaining.\n", + priority == GFP_ATOMIC ? + "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); + /* We don't reschedule replenish work here -- we will + * call the restock method and if it still needs + * more buffers it will schedule replenish */ + return; + } + + spin_lock_irqsave(&rxq->lock, flags); + + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + __free_pages(page, trans_pcie->rx_page_order); + return; + } + element = rxq->rx_used.next; + rxb = list_entry(element, struct iwl_rx_mem_buffer, list); + list_del(element); + + spin_unlock_irqrestore(&rxq->lock, flags); + + BUG_ON(rxb->page); + rxb->page = page; + /* Get physical address of the RB */ + rxb->page_dma = + dma_map_page(trans->dev, page, 0, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + /* dma address must be no more than 36 bits */ + BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); + /* and also 256 byte aligned! */ + BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); + + spin_lock_irqsave(&rxq->lock, flags); + + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + + spin_unlock_irqrestore(&rxq->lock, flags); + } +} + +void iwlagn_rx_replenish(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + iwlagn_rx_allocate(trans, GFP_KERNEL); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwlagn_rx_queue_restock(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); +} + +static void iwlagn_rx_replenish_now(struct iwl_trans *trans) +{ + iwlagn_rx_allocate(trans, GFP_ATOMIC); + + iwlagn_rx_queue_restock(trans); +} + +void iwl_bg_rx_replenish(struct work_struct *data) +{ + struct iwl_trans_pcie *trans_pcie = + container_of(data, struct iwl_trans_pcie, rx_replenish); + + iwlagn_rx_replenish(trans_pcie->trans); +} + +static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + unsigned long flags; + bool page_stolen = false; + int max_len = PAGE_SIZE << trans_pcie->rx_page_order; + u32 offset = 0; + + if (WARN_ON(!rxb)) + return; + + dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE); + + while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) { + struct iwl_rx_packet *pkt; + struct iwl_device_cmd *cmd; + u16 sequence; + bool reclaim; + int index, cmd_index, err, len; + struct iwl_rx_cmd_buffer rxcb = { + ._offset = offset, + ._page = rxb->page, + ._page_stolen = false, + .truesize = max_len, + }; + + pkt = rxb_addr(&rxcb); + + if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) + break; + + IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n", + rxcb._offset, + trans_pcie_get_cmd_string(trans_pcie, pkt->hdr.cmd), + pkt->hdr.cmd); + + len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + len += sizeof(u32); /* account for status word */ + trace_iwlwifi_dev_rx(trans->dev, pkt, len); + + /* Reclaim a command buffer only if this packet is a response + * to a (driver-originated) command. + * If the packet (e.g. Rx frame) originated from uCode, + * there is no command buffer to reclaim. + * Ucode should set SEQ_RX_FRAME bit if ucode-originated, + * but apparently a few don't get set; catch them here. */ + reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); + if (reclaim) { + int i; + + for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { + if (trans_pcie->no_reclaim_cmds[i] == + pkt->hdr.cmd) { + reclaim = false; + break; + } + } + } + + sequence = le16_to_cpu(pkt->hdr.sequence); + index = SEQ_TO_INDEX(sequence); + cmd_index = get_cmd_index(&txq->q, index); + + if (reclaim) + cmd = txq->entries[cmd_index].cmd; + else + cmd = NULL; + + err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); + + /* + * After here, we should always check rxcb._page_stolen, + * if it is true then one of the handlers took the page. + */ + + if (reclaim) { + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking + * iwl_trans_send_cmd() + * as we reclaim the driver command queue */ + if (!rxcb._page_stolen) + iwl_tx_cmd_complete(trans, &rxcb, err); + else + IWL_WARN(trans, "Claim null rxb?\n"); + } + + page_stolen |= rxcb._page_stolen; + offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN); + } + + /* page was stolen from us -- free our reference */ + if (page_stolen) { + __free_pages(rxb->page, trans_pcie->rx_page_order); + rxb->page = NULL; + } + + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ + spin_lock_irqsave(&rxq->lock, flags); + if (rxb->page != NULL) { + rxb->page_dma = + dma_map_page(trans->dev, rxb->page, 0, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } else + list_add_tail(&rxb->list, &rxq->rx_used); + spin_unlock_irqrestore(&rxq->lock, flags); +} + +/** + * iwl_rx_handle - Main entry function for receiving responses from uCode + * + * Uses the priv->rx_handlers callback function array to invoke + * the appropriate handlers, including command responses, + * frame-received notifications, and other notifications. + */ +static void iwl_rx_handle(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + u32 r, i; + u8 fill_rx = 0; + u32 count = 8; + int total_empty; + + /* uCode's read index (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; + i = rxq->read; + + /* Rx interrupt, but nothing sent from uCode */ + if (i == r) + IWL_DEBUG_RX(trans, "HW = SW = %d\n", r); + + /* calculate total frames need to be restock after handling RX */ + total_empty = r - rxq->write_actual; + if (total_empty < 0) + total_empty += RX_QUEUE_SIZE; + + if (total_empty > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + + while (i != r) { + struct iwl_rx_mem_buffer *rxb; + + rxb = rxq->queue[i]; + rxq->queue[i] = NULL; + + IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", + r, i, rxb); + iwl_rx_handle_rxbuf(trans, rxb); + + i = (i + 1) & RX_QUEUE_MASK; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode wont assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + rxq->read = i; + iwlagn_rx_replenish_now(trans); + count = 0; + } + } + } + + /* Backtrack one entry */ + rxq->read = i; + if (fill_rx) + iwlagn_rx_replenish_now(trans); + else + iwlagn_rx_queue_restock(trans); +} + +/** + * iwl_irq_handle_error - called for HW or SW error interrupt from card + */ +static void iwl_irq_handle_error(struct iwl_trans *trans) +{ + /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ + if (trans->cfg->internal_wimax_coex && + (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & + APMS_CLK_VAL_MRB_FUNC_MODE) || + (iwl_read_prph(trans, APMG_PS_CTRL_REG) & + APMG_PS_CTRL_VAL_RESET_REQ))) { + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + iwl_op_mode_wimax_active(trans->op_mode); + wake_up(&trans->wait_command_queue); + return; + } + + iwl_dump_csr(trans); + iwl_dump_fh(trans, NULL, false); + + iwl_op_mode_nic_error(trans->op_mode); +} + +/* tasklet for iwlagn interrupt */ +void iwl_irq_tasklet(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + u32 inta = 0; + u32 handled = 0; + unsigned long flags; + u32 i; +#ifdef CONFIG_IWLWIFI_DEBUG + u32 inta_mask; +#endif + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + /* Ack/clear/reset pending uCode interrupts. + * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, + */ + /* There is a hardware bug in the interrupt mask function that some + * interrupts (i.e. CSR_INT_BIT_SCD) can still be generated even if + * they are disabled in the CSR_INT_MASK register. Furthermore the + * ICT interrupt handling mechanism has another bug that might cause + * these unmasked interrupts fail to be detected. We workaround the + * hardware bugs here by ACKing all the possible interrupts so that + * interrupt coalescing can still be achieved. + */ + iwl_write32(trans, CSR_INT, + trans_pcie->inta | ~trans_pcie->inta_mask); + + inta = trans_pcie->inta; + +#ifdef CONFIG_IWLWIFI_DEBUG + if (iwl_have_debug_level(IWL_DL_ISR)) { + /* just for debug */ + inta_mask = iwl_read32(trans, CSR_INT_MASK); + IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", + inta, inta_mask); + } +#endif + + /* saved interrupt in inta variable now we can reset trans_pcie->inta */ + trans_pcie->inta = 0; + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IWL_ERR(trans, "Hardware error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + iwl_disable_interrupts(trans); + + isr_stats->hw++; + iwl_irq_handle_error(trans); + + handled |= CSR_INT_BIT_HW_ERR; + + return; + } + +#ifdef CONFIG_IWLWIFI_DEBUG + if (iwl_have_debug_level(IWL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) { + IWL_DEBUG_ISR(trans, "Scheduler finished to transmit " + "the frame/frames.\n"); + isr_stats->sch++; + } + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) { + IWL_DEBUG_ISR(trans, "Alive interrupt\n"); + isr_stats->alive++; + } + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { + bool hw_rfkill; + + hw_rfkill = iwl_is_rfkill_set(trans); + IWL_WARN(trans, "RF_KILL bit toggled to %s.\n", + hw_rfkill ? "disable radio" : "enable radio"); + + isr_stats->rfkill++; + + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself */ + if (inta & CSR_INT_BIT_CT_KILL) { + IWL_ERR(trans, "Microcode CT kill error detected.\n"); + isr_stats->ctkill++; + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IWL_ERR(trans, "Microcode SW error detected. " + " Restarting 0x%X.\n", inta); + isr_stats->sw++; + iwl_irq_handle_error(trans); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* uCode wakes up after power-down sleep */ + if (inta & CSR_INT_BIT_WAKEUP) { + IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); + iwl_rx_queue_update_write_ptr(trans, &trans_pcie->rxq); + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) + iwl_txq_update_write_ptr(trans, + &trans_pcie->txq[i]); + + isr_stats->wakeup++; + + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX | + CSR_INT_BIT_RX_PERIODIC)) { + IWL_DEBUG_ISR(trans, "Rx interrupt\n"); + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + iwl_write32(trans, CSR_FH_INT_STATUS, + CSR_FH_INT_RX_MASK); + } + if (inta & CSR_INT_BIT_RX_PERIODIC) { + handled |= CSR_INT_BIT_RX_PERIODIC; + iwl_write32(trans, + CSR_INT, CSR_INT_BIT_RX_PERIODIC); + } + /* Sending RX interrupt require many steps to be done in the + * the device: + * 1- write interrupt to current index in ICT table. + * 2- dma RX frame. + * 3- update RX shared data to indicate last write index. + * 4- send interrupt. + * This could lead to RX race, driver could receive RX interrupt + * but the shared data changes does not reflect this; + * periodic interrupt will detect any dangling Rx activity. + */ + + /* Disable periodic interrupt; we use it as just a one-shot. */ + iwl_write8(trans, CSR_INT_PERIODIC_REG, + CSR_INT_PERIODIC_DIS); +#ifdef CONFIG_IWLWIFI_IDI + iwl_amfh_rx_handler(); +#else + iwl_rx_handle(trans); +#endif + /* + * Enable periodic interrupt in 8 msec only if we received + * real RX interrupt (instead of just periodic int), to catch + * any dangling Rx interrupt. If it was just the periodic + * interrupt, there was no dangling Rx activity, and no need + * to extend the periodic interrupt; one-shot is enough. + */ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) + iwl_write8(trans, CSR_INT_PERIODIC_REG, + CSR_INT_PERIODIC_ENA); + + isr_stats->rx++; + } + + /* This "Tx" DMA channel is used only for loading uCode */ + if (inta & CSR_INT_BIT_FH_TX) { + iwl_write32(trans, CSR_FH_INT_STATUS, CSR_FH_INT_TX_MASK); + IWL_DEBUG_ISR(trans, "uCode load interrupt\n"); + isr_stats->tx++; + handled |= CSR_INT_BIT_FH_TX; + /* Wake up uCode load routine, now that load is complete */ + trans_pcie->ucode_write_complete = true; + wake_up(&trans_pcie->ucode_write_waitq); + } + + if (inta & ~handled) { + IWL_ERR(trans, "Unhandled INTA bits 0x%08x\n", inta & ~handled); + isr_stats->unhandled++; + } + + if (inta & ~(trans_pcie->inta_mask)) { + IWL_WARN(trans, "Disabled INTA bits 0x%08x were pending\n", + inta & ~trans_pcie->inta_mask); + } + + /* Re-enable all interrupts */ + /* only Re-enable if disabled by irq */ + if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status)) + iwl_enable_interrupts(trans); + /* Re-enable RF_KILL if it occurred */ + else if (handled & CSR_INT_BIT_RF_KILL) + iwl_enable_rfkill_int(trans); +} + +/****************************************************************************** + * + * ICT functions + * + ******************************************************************************/ + +/* a device (PCI-E) page is 4096 bytes long */ +#define ICT_SHIFT 12 +#define ICT_SIZE (1 << ICT_SHIFT) +#define ICT_COUNT (ICT_SIZE / sizeof(u32)) + +/* Free dram table */ +void iwl_free_isr_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (trans_pcie->ict_tbl) { + dma_free_coherent(trans->dev, ICT_SIZE, + trans_pcie->ict_tbl, + trans_pcie->ict_tbl_dma); + trans_pcie->ict_tbl = NULL; + trans_pcie->ict_tbl_dma = 0; + } +} + + +/* + * allocate dram shared table, it is an aligned memory + * block of ICT_SIZE. + * also reset all data related to ICT table interrupt. + */ +int iwl_alloc_isr_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->ict_tbl = + dma_alloc_coherent(trans->dev, ICT_SIZE, + &trans_pcie->ict_tbl_dma, + GFP_KERNEL); + if (!trans_pcie->ict_tbl) + return -ENOMEM; + + /* just an API sanity check ... it is guaranteed to be aligned */ + if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { + iwl_free_isr_ict(trans); + return -EINVAL; + } + + IWL_DEBUG_ISR(trans, "ict dma addr %Lx\n", + (unsigned long long)trans_pcie->ict_tbl_dma); + + IWL_DEBUG_ISR(trans, "ict vir addr %p\n", trans_pcie->ict_tbl); + + /* reset table and index to all 0 */ + memset(trans_pcie->ict_tbl, 0, ICT_SIZE); + trans_pcie->ict_index = 0; + + /* add periodic RX interrupt */ + trans_pcie->inta_mask |= CSR_INT_BIT_RX_PERIODIC; + return 0; +} + +/* Device is going up inform it about using ICT interrupt table, + * also we need to tell the driver to start using ICT interrupt. + */ +void iwl_reset_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 val; + unsigned long flags; + + if (!trans_pcie->ict_tbl) + return; + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_disable_interrupts(trans); + + memset(trans_pcie->ict_tbl, 0, ICT_SIZE); + + val = trans_pcie->ict_tbl_dma >> ICT_SHIFT; + + val |= CSR_DRAM_INT_TBL_ENABLE; + val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; + + IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val); + + iwl_write32(trans, CSR_DRAM_INT_TBL_REG, val); + trans_pcie->use_ict = true; + trans_pcie->ict_index = 0; + iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); + iwl_enable_interrupts(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); +} + +/* Device is going down disable ict interrupt usage */ +void iwl_disable_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + trans_pcie->use_ict = false; + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); +} + +static irqreturn_t iwl_isr(int irq, void *data) +{ + struct iwl_trans *trans = data; + struct iwl_trans_pcie *trans_pcie; + u32 inta, inta_mask; + unsigned long flags; +#ifdef CONFIG_IWLWIFI_DEBUG + u32 inta_fh; +#endif + if (!trans) + return IRQ_NONE; + + trace_iwlwifi_dev_irq(trans->dev); + + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + /* Disable (but don't clear!) interrupts here to avoid + * back-to-back ISRs and sporadic interrupts from our NIC. + * If we have something to service, the tasklet will re-enable ints. + * If we *don't* have something, we'll re-enable before leaving here. */ + inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ + iwl_write32(trans, CSR_INT_MASK, 0x00000000); + + /* Discover which interrupts are active/pending */ + inta = iwl_read32(trans, CSR_INT); + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + if (!inta) { + IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); + goto none; + } + + if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { + /* Hardware disappeared. It might have already raised + * an interrupt */ + IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); + goto unplugged; + } + +#ifdef CONFIG_IWLWIFI_DEBUG + if (iwl_have_debug_level(IWL_DL_ISR)) { + inta_fh = iwl_read32(trans, CSR_FH_INT_STATUS); + IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x, " + "fh 0x%08x\n", inta, inta_mask, inta_fh); + } +#endif + + trans_pcie->inta |= inta; + /* iwl_irq_tasklet() will service interrupts and re-enable them */ + if (likely(inta)) + tasklet_schedule(&trans_pcie->irq_tasklet); + else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + !trans_pcie->inta) + iwl_enable_interrupts(trans); + + unplugged: + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + return IRQ_HANDLED; + + none: + /* re-enable interrupts here since we don't have anything to service. */ + /* only Re-enable if disabled by irq and no schedules tasklet. */ + if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + !trans_pcie->inta) + iwl_enable_interrupts(trans); + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + return IRQ_NONE; +} + +/* interrupt handler using ict table, with this interrupt driver will + * stop using INTA register to get device's interrupt, reading this register + * is expensive, device will write interrupts in ICT dram table, increment + * index then will fire interrupt to driver, driver will OR all ICT table + * entries from current index up to table entry with 0 value. the result is + * the interrupt we need to service, driver will set the entries back to 0 and + * set index. + */ +irqreturn_t iwl_isr_ict(int irq, void *data) +{ + struct iwl_trans *trans = data; + struct iwl_trans_pcie *trans_pcie; + u32 inta, inta_mask; + u32 val = 0; + u32 read; + unsigned long flags; + + if (!trans) + return IRQ_NONE; + + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* dram interrupt table not set yet, + * use legacy interrupt. + */ + if (!trans_pcie->use_ict) + return iwl_isr(irq, data); + + trace_iwlwifi_dev_irq(trans->dev); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + /* Disable (but don't clear!) interrupts here to avoid + * back-to-back ISRs and sporadic interrupts from our NIC. + * If we have something to service, the tasklet will re-enable ints. + * If we *don't* have something, we'll re-enable before leaving here. + */ + inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ + iwl_write32(trans, CSR_INT_MASK, 0x00000000); + + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); + if (!read) { + IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); + goto none; + } + + /* + * Collect all entries up to the first 0, starting from ict_index; + * note we already read at ict_index. + */ + do { + val |= read; + IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", + trans_pcie->ict_index, read); + trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; + trans_pcie->ict_index = + iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); + + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, + read); + } while (read); + + /* We should not get this value, just ignore it. */ + if (val == 0xffffffff) + val = 0; + + /* + * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit + * (bit 15 before shifting it to 31) to clear when using interrupt + * coalescing. fortunately, bits 18 and 19 stay set when this happens + * so we use them to decide on the real state of the Rx bit. + * In order words, bit 15 is set if bit 18 or bit 19 are set. + */ + if (val & 0xC0000) + val |= 0x8000; + + inta = (0xff & val) | ((0xff00 & val) << 16); + IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", + inta, inta_mask, val); + + inta &= trans_pcie->inta_mask; + trans_pcie->inta |= inta; + + /* iwl_irq_tasklet() will service interrupts and re-enable them */ + if (likely(inta)) + tasklet_schedule(&trans_pcie->irq_tasklet); + else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + !trans_pcie->inta) { + /* Allow interrupt if was disabled by this handler and + * no tasklet was schedules, We should not enable interrupt, + * tasklet will enable it. + */ + iwl_enable_interrupts(trans); + } + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + return IRQ_HANDLED; + + none: + /* re-enable interrupts here since we don't have anything to service. + * only Re-enable if disabled by irq. + */ + if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + !trans_pcie->inta) + iwl_enable_interrupts(trans); + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + return IRQ_NONE; +} diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c new file mode 100644 index 000000000000..e8a04a18a7d0 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -0,0 +1,2178 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#include "iwl-drv.h" +#include "iwl-trans.h" +#include "iwl-csr.h" +#include "iwl-prph.h" +#include "iwl-eeprom.h" +#include "iwl-agn-hw.h" +#include "internal.h" +/* FIXME: need to abstract out TX command (once we know what it looks like) */ +#include "iwl-commands.h" + +#define SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie) \ + (((1<cfg->base_params->num_of_queues) - 1) &\ + (~(1<<(trans_pcie)->cmd_queue))) + +static int iwl_trans_rx_alloc(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct device *dev = trans->dev; + + memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); + + spin_lock_init(&rxq->lock); + + if (WARN_ON(rxq->bd || rxq->rb_stts)) + return -EINVAL; + + /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ + rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + &rxq->bd_dma, GFP_KERNEL); + if (!rxq->bd) + goto err_bd; + + /*Allocate the driver's pointer to receive buffer status */ + rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), + &rxq->rb_stts_dma, GFP_KERNEL); + if (!rxq->rb_stts) + goto err_rb_stts; + + return 0; + +err_rb_stts: + dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd = NULL; +err_bd: + return -ENOMEM; +} + +static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + int i; + + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { + /* In the reset function, these buffers may have been allocated + * to an SKB, so we need to unmap and free potential storage */ + if (rxq->pool[i].page != NULL) { + dma_unmap_page(trans->dev, rxq->pool[i].page_dma, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + __free_pages(rxq->pool[i].page, + trans_pcie->rx_page_order); + rxq->pool[i].page = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } +} + +static void iwl_trans_rx_hw_init(struct iwl_trans *trans, + struct iwl_rx_queue *rxq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 rb_size; + const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ + u32 rb_timeout = RX_RB_TIMEOUT; /* FIXME: RX_RB_TIMEOUT for all devices? */ + + if (trans_pcie->rx_buf_size_8k) + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; + else + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + + /* Stop Rx DMA */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + + /* Reset driver's Rx queue write index */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, + (u32)(rxq->bd_dma >> 8)); + + /* Tell device where in DRAM to update its Rx status */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, + rxq->rb_stts_dma >> 4); + + /* Enable Rx DMA + * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in + * the credit mechanism in 5000 HW RX FIFO + * Direct rx interrupts to hosts + * Rx buffer size 4 or 8k + * RB timeout 0x10 + * 256 RBDs + */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | + FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | + FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + rb_size| + (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| + (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); + + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); +} + +static int iwl_rx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + + int i, err; + unsigned long flags; + + if (!rxq->bd) { + err = iwl_trans_rx_alloc(trans); + if (err) + return err; + } + + spin_lock_irqsave(&rxq->lock, flags); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + iwl_trans_rxq_free_rx_bufs(trans); + + for (i = 0; i < RX_QUEUE_SIZE; i++) + rxq->queue[i] = NULL; + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + spin_unlock_irqrestore(&rxq->lock, flags); + + iwlagn_rx_replenish(trans); + + iwl_trans_rx_hw_init(trans, rxq); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + rxq->need_update = 1; + iwl_rx_queue_update_write_ptr(trans, rxq); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + return 0; +} + +static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + unsigned long flags; + + /*if rxq->bd is NULL, it means that nothing has been allocated, + * exit now */ + if (!rxq->bd) { + IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); + return; + } + + spin_lock_irqsave(&rxq->lock, flags); + iwl_trans_rxq_free_rx_bufs(trans); + spin_unlock_irqrestore(&rxq->lock, flags); + + dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd = NULL; + + if (rxq->rb_stts) + dma_free_coherent(trans->dev, + sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + else + IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); + memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); + rxq->rb_stts = NULL; +} + +static int iwl_trans_rx_stop(struct iwl_trans *trans) +{ + + /* stop Rx DMA */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); +} + +static int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size) +{ + if (WARN_ON(ptr->addr)) + return -EINVAL; + + ptr->addr = dma_alloc_coherent(trans->dev, size, + &ptr->dma, GFP_KERNEL); + if (!ptr->addr) + return -ENOMEM; + ptr->size = size; + return 0; +} + +static void iwlagn_free_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr) +{ + if (unlikely(!ptr->addr)) + return; + + dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); + memset(ptr, 0, sizeof(*ptr)); +} + +static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) +{ + struct iwl_tx_queue *txq = (void *)data; + struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; + struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); + + spin_lock(&txq->lock); + /* check if triggered erroneously */ + if (txq->q.read_ptr == txq->q.write_ptr) { + spin_unlock(&txq->lock); + return; + } + spin_unlock(&txq->lock); + + + IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, + jiffies_to_msecs(trans_pcie->wd_timeout)); + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", + txq->q.read_ptr, txq->q.write_ptr); + IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n", + iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq->q.id)) + & (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id))); + + iwl_op_mode_nic_error(trans->op_mode); +} + +static int iwl_trans_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue *txq, int slots_num, + u32 txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; + int i; + + if (WARN_ON(txq->entries || txq->tfds)) + return -EINVAL; + + setup_timer(&txq->stuck_timer, iwl_trans_pcie_queue_stuck_timer, + (unsigned long)txq); + txq->trans_pcie = trans_pcie; + + txq->q.n_window = slots_num; + + txq->entries = kcalloc(slots_num, + sizeof(struct iwl_pcie_tx_queue_entry), + GFP_KERNEL); + + if (!txq->entries) + goto error; + + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < slots_num; i++) { + txq->entries[i].cmd = + kmalloc(sizeof(struct iwl_device_cmd), + GFP_KERNEL); + if (!txq->entries[i].cmd) + goto error; + } + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, + &txq->q.dma_addr, GFP_KERNEL); + if (!txq->tfds) { + IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); + goto error; + } + txq->q.id = txq_id; + + return 0; +error: + if (txq->entries && txq_id == trans_pcie->cmd_queue) + for (i = 0; i < slots_num; i++) + kfree(txq->entries[i].cmd); + kfree(txq->entries); + txq->entries = NULL; + + return -ENOMEM; + +} + +static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, + int slots_num, u32 txq_id) +{ + int ret; + + txq->need_update = 0; + + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise + * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + + /* Initialize queue's high/low-water marks, and head/tail indexes */ + ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, + txq_id); + if (ret) + return ret; + + spin_lock_init(&txq->lock); + + /* + * Tell nic where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * Circular buffer (TFD queue in DRAM) physical base address */ + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + + return 0; +} + +/** + * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's + */ +static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + enum dma_data_direction dma_dir; + + if (!q->n_bd) + return; + + /* In the command queue, all the TBs are mapped as BIDI + * so unmap them as such. + */ + if (txq_id == trans_pcie->cmd_queue) + dma_dir = DMA_BIDIRECTIONAL; + else + dma_dir = DMA_TO_DEVICE; + + spin_lock_bh(&txq->lock); + while (q->write_ptr != q->read_ptr) { + iwl_txq_free_tfd(trans, txq, dma_dir); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); + } + spin_unlock_bh(&txq->lock); +} + +/** + * iwl_tx_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + struct device *dev = trans->dev; + int i; + + if (WARN_ON(!txq)) + return; + + iwl_tx_queue_unmap(trans, txq_id); + + /* De-alloc array of command/tx buffers */ + + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < txq->q.n_window; i++) + kfree(txq->entries[i].cmd); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) { + dma_free_coherent(dev, sizeof(struct iwl_tfd) * + txq->q.n_bd, txq->tfds, txq->q.dma_addr); + memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); + } + + kfree(txq->entries); + txq->entries = NULL; + + del_timer_sync(&txq->stuck_timer); + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} + +/** + * iwl_trans_tx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +static void iwl_trans_pcie_tx_free(struct iwl_trans *trans) +{ + int txq_id; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* Tx queues */ + if (trans_pcie->txq) { + for (txq_id = 0; + txq_id < trans->cfg->base_params->num_of_queues; txq_id++) + iwl_tx_queue_free(trans, txq_id); + } + + kfree(trans_pcie->txq); + trans_pcie->txq = NULL; + + iwlagn_free_dma_ptr(trans, &trans_pcie->kw); + + iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); +} + +/** + * iwl_trans_tx_alloc - allocate TX context + * Allocate all Tx DMA structures and initialize them + * + * @param priv + * @return error code + */ +static int iwl_trans_tx_alloc(struct iwl_trans *trans) +{ + int ret; + int txq_id, slots_num; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * + sizeof(struct iwlagn_scd_bc_tbl); + + /*It is not allowed to alloc twice, so warn when this happens. + * We cannot rely on the previous allocation, so free and fail */ + if (WARN_ON(trans_pcie->txq)) { + ret = -EINVAL; + goto error; + } + + ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, + scd_bc_tbls_size); + if (ret) { + IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); + goto error; + } + + /* Alloc keep-warm buffer */ + ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); + if (ret) { + IWL_ERR(trans, "Keep Warm allocation failed\n"); + goto error; + } + + trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, + sizeof(struct iwl_tx_queue), GFP_KERNEL); + if (!trans_pcie->txq) { + IWL_ERR(trans, "Not enough memory for txq\n"); + ret = ENOMEM; + goto error; + } + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); + goto error; + } + } + + return 0; + +error: + iwl_trans_pcie_tx_free(trans); + + return ret; +} +static int iwl_tx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + int txq_id, slots_num; + unsigned long flags; + bool alloc = false; + + if (!trans_pcie->txq) { + ret = iwl_trans_tx_alloc(trans); + if (ret) + goto error; + alloc = true; + } + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + /* Turn off all Tx DMA fifos */ + iwl_write_prph(trans, SCD_TXFACT, 0); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return 0; +error: + /*Upon error, free only if we allocated something */ + if (alloc) + iwl_trans_pcie_tx_free(trans); + return ret; +} + +static void iwl_set_pwr_vmain(struct iwl_trans *trans) +{ +/* + * (for documentation purposes) + * to set power to V_AUX, do: + + if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) + iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + */ + + iwl_set_bits_mask_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); +} + +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 +#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 +#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 + +static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int pos; + u16 pci_lnk_ctl; + + struct pci_dev *pci_dev = trans_pcie->pci_dev; + + pos = pci_pcie_cap(pci_dev); + pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); + return pci_lnk_ctl; +} + +static void iwl_apm_config(struct iwl_trans *trans) +{ + /* + * HW bug W/A for instability in PCIe bus L0S->L1 transition. + * Check if BIOS (or OS) enabled L1-ASPM on this device. + * If so (likely), disable L0S, so device moves directly L0->L1; + * costs negligible amount of power savings. + * If not (unlikely), enable L0S, so there is at least some + * power savings, even without L1. + */ + u16 lctl = iwl_pciexp_link_ctrl(trans); + + if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == + PCI_CFG_LINK_CTRL_VAL_L1_EN) { + /* L1-ASPM enabled; disable(!) L0S */ + iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); + dev_printk(KERN_INFO, trans->dev, + "L1 Enabled; Disabling L0S\n"); + } else { + /* L1-ASPM disabled; enable(!) L0S */ + iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); + dev_printk(KERN_INFO, trans->dev, + "L1 Disabled; Enabling L0S\n"); + } + trans->pm_support = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); +} + +/* + * Start up NIC's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via iwl_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +static int iwl_apm_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret = 0; + IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); + + /* + * Use "set_bit" below rather than "write", to preserve any hardware + * bits already set by default after reset. + */ + + /* Disable L0S exit timer (platform NMI Work/Around) */ + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + + /* + * Disable L0s without affecting L1; + * don't wait for ICH L0s (ICH bug W/A) + */ + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + + /* Set FH wait threshold to maximum (HW error during stress W/A) */ + iwl_set_bit(trans, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); + + /* + * Enable HAP INTA (interrupt from management bus) to + * wake device's PCI Express link L1a -> L0s + */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + + iwl_apm_config(trans); + + /* Configure analog phase-lock-loop before activating to D0A */ + if (trans->cfg->base_params->pll_cfg_val) + iwl_set_bit(trans, CSR_ANA_PLL_CFG, + trans->cfg->base_params->pll_cfg_val); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. iwl_write_prph() + * and accesses to uCode SRAM. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (ret < 0) { + IWL_DEBUG_INFO(trans, "Failed to init the card\n"); + goto out; + } + + /* + * Enable DMA clock and wait for it to stabilize. + * + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits + * do not disable clocks. This preserves any hardware bits already + * set by default in "CLK_CTRL_REG" after reset. + */ + iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); + + /* Disable L1-Active */ + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + + set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + +out: + return ret; +} + +static int iwl_apm_stop_master(struct iwl_trans *trans) +{ + int ret = 0; + + /* stop device's busmaster DMA activity */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + + ret = iwl_poll_bit(trans, CSR_RESET, + CSR_RESET_REG_FLAG_MASTER_DISABLED, + CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + if (ret) + IWL_WARN(trans, "Master Disable Timed Out, 100 usec\n"); + + IWL_DEBUG_INFO(trans, "stop master\n"); + + return ret; +} + +static void iwl_apm_stop(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); + + clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + + /* Stop device's DMA activity */ + iwl_apm_stop_master(trans); + + /* Reset the entire device */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); +} + +static int iwl_nic_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + /* nic_init */ + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_apm_init(trans); + + /* Set interrupt coalescing calibration timer to default (512 usecs) */ + iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + iwl_set_pwr_vmain(trans); + + iwl_op_mode_nic_config(trans->op_mode); + +#ifndef CONFIG_IWLWIFI_IDI + /* Allocate the RX queue, or reset if it is already allocated */ + iwl_rx_init(trans); +#endif + + /* Allocate or reset and init all Tx and Command queues */ + if (iwl_tx_init(trans)) + return -ENOMEM; + + if (trans->cfg->base_params->shadow_reg_enable) { + /* enable shadow regs in HW */ + iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); + IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); + } + + return 0; +} + +#define HW_READY_TIMEOUT (50) + +/* Note: returns poll_bit return value, which is >= 0 if success */ +static int iwl_set_hw_ready(struct iwl_trans *trans) +{ + int ret; + + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + + /* See if we got it */ + ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + HW_READY_TIMEOUT); + + IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); + return ret; +} + +/* Note: returns standard 0/-ERROR code */ +static int iwl_prepare_card_hw(struct iwl_trans *trans) +{ + int ret; + + IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); + + ret = iwl_set_hw_ready(trans); + /* If the card is ready, exit 0 */ + if (ret >= 0) + return 0; + + /* If HW is not ready, prepare the conditions to check again */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE); + + ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, + ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, + CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + + if (ret < 0) + return ret; + + /* HW should be ready by now, check again. */ + ret = iwl_set_hw_ready(trans); + if (ret >= 0) + return 0; + return ret; +} + +/* + * ucode + */ +static int iwl_load_section(struct iwl_trans *trans, u8 section_num, + const struct fw_desc *section) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + dma_addr_t phy_addr = section->p_addr; + u32 byte_cnt = section->len; + u32 dst_addr = section->offset; + int ret; + + trans_pcie->ucode_write_complete = false; + + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); + + iwl_write_direct32(trans, + FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), + dst_addr); + + iwl_write_direct32(trans, + FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), + phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + + iwl_write_direct32(trans, + FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), + (iwl_get_dma_hi_addr(phy_addr) + << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), + 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | + 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | + FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); + + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | + FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); + + IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", + section_num); + ret = wait_event_timeout(trans_pcie->ucode_write_waitq, + trans_pcie->ucode_write_complete, 5 * HZ); + if (!ret) { + IWL_ERR(trans, "Could not load the [%d] uCode section\n", + section_num); + return -ETIMEDOUT; + } + + return 0; +} + +static int iwl_load_given_ucode(struct iwl_trans *trans, + const struct fw_img *image) +{ + int ret = 0; + int i; + + for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { + if (!image->sec[i].p_addr) + break; + + ret = iwl_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } + + /* Remove all resets to allow NIC to operate */ + iwl_write32(trans, CSR_RESET, 0); + + return 0; +} + +static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, + const struct fw_img *fw) +{ + int ret; + bool hw_rfkill; + + /* This may fail if AMT took ownership of the device */ + if (iwl_prepare_card_hw(trans)) { + IWL_WARN(trans, "Exit HW not ready\n"); + return -EIO; + } + + iwl_enable_rfkill_int(trans); + + /* If platform's RF_KILL switch is NOT set to KILL */ + hw_rfkill = iwl_is_rfkill_set(trans); + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + if (hw_rfkill) + return -ERFKILL; + + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + ret = iwl_nic_init(trans); + if (ret) { + IWL_ERR(trans, "Unable to init nic\n"); + return ret; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + iwl_enable_interrupts(trans); + + /* really make sure rfkill handshake bits are cleared */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Load the given image to the HW */ + return iwl_load_given_ucode(trans, fw); +} + +/* + * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask + * must be called under the irq lock and with MAC access + */ +static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) +{ + struct iwl_trans_pcie __maybe_unused *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + lockdep_assert_held(&trans_pcie->irq_lock); + + iwl_write_prph(trans, SCD_TXFACT, mask); +} + +static void iwl_tx_start(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 a; + unsigned long flags; + int i, chan; + u32 reg_val; + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + trans_pcie->scd_base_addr = + iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); + a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; + /* reset conext data memory */ + for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; + a += 4) + iwl_write_targ_mem(trans, a, 0); + /* reset tx status memory */ + for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; + a += 4) + iwl_write_targ_mem(trans, a, 0); + for (; a < trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE( + trans->cfg->base_params->num_of_queues); + a += 4) + iwl_write_targ_mem(trans, a, 0); + + iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, + trans_pcie->scd_bc_tbls.dma >> 10); + + /* Enable DMA channel */ + for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) + iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); + + /* Update FH chicken bits */ + reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); + iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, + reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + + iwl_write_prph(trans, SCD_QUEUECHAIN_SEL, + SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie)); + iwl_write_prph(trans, SCD_AGGR_SEL, 0); + + /* initiate the queues */ + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + iwl_write_prph(trans, SCD_QUEUE_RDPTR(i), 0); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, 0 | (i << 8)); + iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(i), 0); + iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(i) + + sizeof(u32), + ((SCD_WIN_SIZE << + SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((SCD_FRAME_LIMIT << + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + } + + iwl_write_prph(trans, SCD_INTERRUPT_MASK, + IWL_MASK(0, trans->cfg->base_params->num_of_queues)); + + /* Activate all Tx DMA/FIFO channels */ + iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); + + iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0); + + /* make sure all queue are not stopped/used */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { + int fifo = trans_pcie->setup_q_to_fifo[i]; + + set_bit(i, trans_pcie->queue_used); + + iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i], + fifo, true); + } + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + /* Enable L1-Active */ + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); +} + +static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) +{ + iwl_reset_ict(trans); + iwl_tx_start(trans); +} + +/** + * iwlagn_txq_ctx_stop - Stop all Tx DMA channels + */ +static int iwl_trans_tx_stop(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ch, txq_id, ret; + unsigned long flags; + + /* Turn off all Tx DMA fifos */ + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + iwl_trans_txq_set_sched(trans, 0); + + /* Stop each Tx DMA channel, and wait for it to be idle */ + for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); + if (ret < 0) + IWL_ERR(trans, + "Failing on timeout while stopping DMA channel %d [0x%08x]", + ch, + iwl_read_direct32(trans, + FH_TSSR_TX_STATUS_REG)); + } + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + if (!trans_pcie->txq) { + IWL_WARN(trans, "Stopping tx queues that aren't allocated..."); + return 0; + } + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) + iwl_tx_queue_unmap(trans, txq_id); + + return 0; +} + +static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + /* tell the device to stop sending interrupts */ + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_disable_interrupts(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + /* device going down, Stop using ICT table */ + iwl_disable_ict(trans); + + /* + * If a HW restart happens during firmware loading, + * then the firmware loading might call this function + * and later it might be called again due to the + * restart. So don't process again if the device is + * already dead. + */ + if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { + iwl_trans_tx_stop(trans); +#ifndef CONFIG_IWLWIFI_IDI + iwl_trans_rx_stop(trans); +#endif + /* Power-down device's busmaster DMA clocks */ + iwl_write_prph(trans, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(5); + } + + /* Make sure (redundant) we've released our request to stay awake */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* Stop the device, and put it in low power state */ + iwl_apm_stop(trans); + + /* Upon stop, the APM issues an interrupt if HW RF kill is set. + * Clean again the interrupt here + */ + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_disable_interrupts(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + iwl_enable_rfkill_int(trans); + + /* wait to make sure we flush pending tasklet*/ + synchronize_irq(trans_pcie->irq); + tasklet_kill(&trans_pcie->irq_tasklet); + + cancel_work_sync(&trans_pcie->rx_replenish); + + /* stop and reset the on-board processor */ + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* clear all status bits */ + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); + clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); +} + +static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) +{ + /* let the ucode operate on its own */ + iwl_write32(trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + iwl_disable_interrupts(trans); + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); +} + +static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; + struct iwl_cmd_meta *out_meta; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + dma_addr_t phys_addr = 0; + dma_addr_t txcmd_phys; + dma_addr_t scratch_phys; + u16 len, firstlen, secondlen; + u8 wait_write_ptr = 0; + __le16 fc = hdr->frame_control; + u8 hdr_len = ieee80211_hdrlen(fc); + u16 __maybe_unused wifi_seq; + + txq = &trans_pcie->txq[txq_id]; + q = &txq->q; + + if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { + WARN_ON_ONCE(1); + return -EINVAL; + } + + spin_lock(&txq->lock); + + /* Set up driver data for this TFD */ + txq->entries[q->write_ptr].skb = skb; + txq->entries[q->write_ptr].cmd = dev_cmd; + + dev_cmd->hdr.cmd = REPLY_TX; + dev_cmd->hdr.sequence = + cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_meta = &txq->entries[q->write_ptr].meta; + + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = sizeof(struct iwl_tx_cmd) + + sizeof(struct iwl_cmd_header) + hdr_len; + firstlen = (len + 3) & ~3; + + /* Tell NIC about any 2-byte padding after MAC header */ + if (firstlen != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = dma_map_single(trans->dev, + &dev_cmd->hdr, firstlen, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) + goto out_err; + dma_unmap_addr_set(out_meta, mapping, txcmd_phys); + dma_unmap_len_set(out_meta, len, firstlen); + + if (!ieee80211_has_morefrags(fc)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; + } + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames have no payload). */ + secondlen = skb->len - hdr_len; + if (secondlen > 0) { + phys_addr = dma_map_single(trans->dev, skb->data + hdr_len, + secondlen, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { + dma_unmap_single(trans->dev, + dma_unmap_addr(out_meta, mapping), + dma_unmap_len(out_meta, len), + DMA_BIDIRECTIONAL); + goto out_err; + } + } + + /* Attach buffers to TFD */ + iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1); + if (secondlen > 0) + iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, + secondlen, 0); + + scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); + + /* take back ownership of DMA buffer to enable update */ + dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, + DMA_BIDIRECTIONAL); + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); + + IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", + le16_to_cpu(dev_cmd->hdr.sequence)); + IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); + + /* Set up entry for this TFD in Tx byte-count array */ + iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); + + dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, + DMA_BIDIRECTIONAL); + + trace_iwlwifi_dev_tx(trans->dev, + &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &dev_cmd->hdr, firstlen, + skb->data + hdr_len, secondlen); + + /* start timer if queue currently empty */ + if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + + /* Tell device the write index *just past* this latest filled TFD */ + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl_txq_update_write_ptr(trans, txq); + + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually, + * regardless of the value of ret. "ret" only indicates + * whether or not we should update the write pointer. + */ + if (iwl_queue_space(q) < q->high_mark) { + if (wait_write_ptr) { + txq->need_update = 1; + iwl_txq_update_write_ptr(trans, txq); + } else { + iwl_stop_queue(trans, txq); + } + } + spin_unlock(&txq->lock); + return 0; + out_err: + spin_unlock(&txq->lock); + return -1; +} + +static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int err; + bool hw_rfkill; + + trans_pcie->inta_mask = CSR_INI_SET_MASK; + + if (!trans_pcie->irq_requested) { + tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) + iwl_irq_tasklet, (unsigned long)trans); + + iwl_alloc_isr_ict(trans); + + err = request_irq(trans_pcie->irq, iwl_isr_ict, IRQF_SHARED, + DRV_NAME, trans); + if (err) { + IWL_ERR(trans, "Error allocating IRQ %d\n", + trans_pcie->irq); + goto error; + } + + INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish); + trans_pcie->irq_requested = true; + } + + err = iwl_prepare_card_hw(trans); + if (err) { + IWL_ERR(trans, "Error while preparing HW: %d", err); + goto err_free_irq; + } + + iwl_apm_init(trans); + + /* From now on, the op_mode will be kept updated about RF kill state */ + iwl_enable_rfkill_int(trans); + + hw_rfkill = iwl_is_rfkill_set(trans); + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + + return err; + +err_free_irq: + free_irq(trans_pcie->irq, trans); +error: + iwl_free_isr_ict(trans); + tasklet_kill(&trans_pcie->irq_tasklet); + return err; +} + +static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, + bool op_mode_leaving) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + bool hw_rfkill; + unsigned long flags; + + iwl_apm_stop(trans); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_disable_interrupts(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + iwl_write32(trans, CSR_INT, 0xFFFFFFFF); + + if (!op_mode_leaving) { + /* + * Even if we stop the HW, we still want the RF kill + * interrupt + */ + iwl_enable_rfkill_int(trans); + + /* + * Check again since the RF kill state may have changed while + * all the interrupts were disabled, in this case we couldn't + * receive the RF kill interrupt and update the state in the + * op_mode. + */ + hw_rfkill = iwl_is_rfkill_set(trans); + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + } +} + +static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, + struct sk_buff_head *skbs) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + /* n_bd is usually 256 => n_bd - 1 = 0xff */ + int tfd_num = ssn & (txq->q.n_bd - 1); + int freed = 0; + + spin_lock(&txq->lock); + + if (txq->q.read_ptr != tfd_num) { + IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", + txq_id, txq->q.read_ptr, tfd_num, ssn); + freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); + if (iwl_queue_space(&txq->q) > txq->q.low_mark) + iwl_wake_queue(trans, txq); + } + + spin_unlock(&txq->lock); +} + +static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) +{ + writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); +} + +static void iwl_trans_pcie_write32(struct iwl_trans *trans, u32 ofs, u32 val) +{ + writel(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); +} + +static u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) +{ + return readl(IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); +} + +static void iwl_trans_pcie_configure(struct iwl_trans *trans, + const struct iwl_trans_config *trans_cfg) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans_pcie->cmd_queue = trans_cfg->cmd_queue; + if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) + trans_pcie->n_no_reclaim_cmds = 0; + else + trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; + if (trans_pcie->n_no_reclaim_cmds) + memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, + trans_pcie->n_no_reclaim_cmds * sizeof(u8)); + + trans_pcie->n_q_to_fifo = trans_cfg->n_queue_to_fifo; + + if (WARN_ON(trans_pcie->n_q_to_fifo > IWL_MAX_HW_QUEUES)) + trans_pcie->n_q_to_fifo = IWL_MAX_HW_QUEUES; + + /* at least the command queue must be mapped */ + WARN_ON(!trans_pcie->n_q_to_fifo); + + memcpy(trans_pcie->setup_q_to_fifo, trans_cfg->queue_to_fifo, + trans_pcie->n_q_to_fifo * sizeof(u8)); + + trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; + if (trans_pcie->rx_buf_size_8k) + trans_pcie->rx_page_order = get_order(8 * 1024); + else + trans_pcie->rx_page_order = get_order(4 * 1024); + + trans_pcie->wd_timeout = + msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); + + trans_pcie->command_names = trans_cfg->command_names; +} + +void iwl_trans_pcie_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_trans_pcie_tx_free(trans); +#ifndef CONFIG_IWLWIFI_IDI + iwl_trans_pcie_rx_free(trans); +#endif + if (trans_pcie->irq_requested == true) { + free_irq(trans_pcie->irq, trans); + iwl_free_isr_ict(trans); + } + + pci_disable_msi(trans_pcie->pci_dev); + iounmap(trans_pcie->hw_base); + pci_release_regions(trans_pcie->pci_dev); + pci_disable_device(trans_pcie->pci_dev); + + kfree(trans); +} + +static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (state) + set_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + else + clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); +} + +#ifdef CONFIG_PM_SLEEP +static int iwl_trans_pcie_suspend(struct iwl_trans *trans) +{ + return 0; +} + +static int iwl_trans_pcie_resume(struct iwl_trans *trans) +{ + bool hw_rfkill; + + iwl_enable_rfkill_int(trans); + + hw_rfkill = iwl_is_rfkill_set(trans); + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + + if (!hw_rfkill) + iwl_enable_interrupts(trans); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#define IWL_FLUSH_WAIT_MS 2000 + +static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq; + struct iwl_queue *q; + int cnt; + unsigned long now = jiffies; + int ret = 0; + + /* waiting for all the tx frames complete might take a while */ + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + if (cnt == trans_pcie->cmd_queue) + continue; + txq = &trans_pcie->txq[cnt]; + q = &txq->q; + while (q->read_ptr != q->write_ptr && !time_after(jiffies, + now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) + msleep(1); + + if (q->read_ptr != q->write_ptr) { + IWL_ERR(trans, "fail to flush all tx fifo queues\n"); + ret = -ETIMEDOUT; + break; + } + } + return ret; +} + +static const char *get_fh_string(int cmd) +{ +#define IWL_CMD(x) case x: return #x + switch (cmd) { + IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); + IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); + IWL_CMD(FH_RSCSR_CHNL0_WPTR); + IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); + IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); + IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); + IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +#undef IWL_CMD +} + +int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display) +{ + int i; +#ifdef CONFIG_IWLWIFI_DEBUG + int pos = 0; + size_t bufsz = 0; +#endif + static const u32 fh_tbl[] = { + FH_RSCSR_CHNL0_STTS_WPTR_REG, + FH_RSCSR_CHNL0_RBDCB_BASE_REG, + FH_RSCSR_CHNL0_WPTR, + FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_MEM_RSSR_SHARED_CTRL_REG, + FH_MEM_RSSR_RX_STATUS_REG, + FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG + }; +#ifdef CONFIG_IWLWIFI_DEBUG + if (display) { + bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + pos += scnprintf(*buf + pos, bufsz - pos, + "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + pos += scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(trans, fh_tbl[i])); + } + return pos; + } +#endif + IWL_ERR(trans, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + IWL_ERR(trans, " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(trans, fh_tbl[i])); + } + return 0; +} + +static const char *get_csr_string(int cmd) +{ +#define IWL_CMD(x) case x: return #x + switch (cmd) { + IWL_CMD(CSR_HW_IF_CONFIG_REG); + IWL_CMD(CSR_INT_COALESCING); + IWL_CMD(CSR_INT); + IWL_CMD(CSR_INT_MASK); + IWL_CMD(CSR_FH_INT_STATUS); + IWL_CMD(CSR_GPIO_IN); + IWL_CMD(CSR_RESET); + IWL_CMD(CSR_GP_CNTRL); + IWL_CMD(CSR_HW_REV); + IWL_CMD(CSR_EEPROM_REG); + IWL_CMD(CSR_EEPROM_GP); + IWL_CMD(CSR_OTP_GP_REG); + IWL_CMD(CSR_GIO_REG); + IWL_CMD(CSR_GP_UCODE_REG); + IWL_CMD(CSR_GP_DRIVER_REG); + IWL_CMD(CSR_UCODE_DRV_GP1); + IWL_CMD(CSR_UCODE_DRV_GP2); + IWL_CMD(CSR_LED_REG); + IWL_CMD(CSR_DRAM_INT_TBL_REG); + IWL_CMD(CSR_GIO_CHICKEN_BITS); + IWL_CMD(CSR_ANA_PLL_CFG); + IWL_CMD(CSR_HW_REV_WA_REG); + IWL_CMD(CSR_DBG_HPET_MEM_REG); + default: + return "UNKNOWN"; + } +#undef IWL_CMD +} + +void iwl_dump_csr(struct iwl_trans *trans) +{ + int i; + static const u32 csr_tbl[] = { + CSR_HW_IF_CONFIG_REG, + CSR_INT_COALESCING, + CSR_INT, + CSR_INT_MASK, + CSR_FH_INT_STATUS, + CSR_GPIO_IN, + CSR_RESET, + CSR_GP_CNTRL, + CSR_HW_REV, + CSR_EEPROM_REG, + CSR_EEPROM_GP, + CSR_OTP_GP_REG, + CSR_GIO_REG, + CSR_GP_UCODE_REG, + CSR_GP_DRIVER_REG, + CSR_UCODE_DRV_GP1, + CSR_UCODE_DRV_GP2, + CSR_LED_REG, + CSR_DRAM_INT_TBL_REG, + CSR_GIO_CHICKEN_BITS, + CSR_ANA_PLL_CFG, + CSR_HW_REV_WA_REG, + CSR_DBG_HPET_MEM_REG + }; + IWL_ERR(trans, "CSR values:\n"); + IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " + "CSR_INT_PERIODIC_REG)\n"); + for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { + IWL_ERR(trans, " %25s: 0X%08x\n", + get_csr_string(csr_tbl[i]), + iwl_read32(trans, csr_tbl[i])); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, trans, \ + &iwl_dbgfs_##name##_ops)) \ + return -ENOMEM; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FUNC(name) \ +static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_WRITE_FUNC(name) \ +static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos); + + +#define DEBUGFS_READ_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq; + struct iwl_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + size_t bufsz; + + bufsz = sizeof(char) * 64 * trans->cfg->base_params->num_of_queues; + + if (!trans_pcie->txq) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + txq = &trans_pcie->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u use=%d stop=%d\n", + cnt, q->read_ptr, q->write_ptr, + !!test_bit(cnt, trans_pcie->queue_used), + !!test_bit(cnt, trans_pcie->queue_stopped)); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + if (rxq->rb_stts) { + pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "closed_rb_num: Not Allocated\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_interrupt_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + + int pos = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, + "Interrupt Statistics Report:\n"); + + pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", + isr_stats->hw); + pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", + isr_stats->sw); + if (isr_stats->sw || isr_stats->hw) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tLast Restarting Code: 0x%X\n", + isr_stats->err_code); + } +#ifdef CONFIG_IWLWIFI_DEBUG + pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", + isr_stats->sch); + pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", + isr_stats->alive); +#endif + pos += scnprintf(buf + pos, bufsz - pos, + "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); + + pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", + isr_stats->ctkill); + + pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", + isr_stats->wakeup); + + pos += scnprintf(buf + pos, bufsz - pos, + "Rx command responses:\t\t %u\n", isr_stats->rx); + + pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", + isr_stats->tx); + + pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", + isr_stats->unhandled); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_interrupt_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + + char buf[8]; + int buf_size; + u32 reset_flag; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + memset(isr_stats, 0, sizeof(*isr_stats)); + + return count; +} + +static ssize_t iwl_dbgfs_csr_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char buf[8]; + int buf_size; + int csr; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &csr) != 1) + return -EFAULT; + + iwl_dump_csr(trans); + + return count; +} + +static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -EFAULT; + + ret = pos = iwl_dump_fh(trans, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, + count, ppos, buf, pos); + kfree(buf); + } + + return ret; +} + +static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + + if (!trans->op_mode) + return -EAGAIN; + + iwl_op_mode_nic_error(trans->op_mode); + + return count; +} + +DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(fh_reg); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_WRITE_FILE_OPS(csr); +DEBUGFS_WRITE_FILE_OPS(fw_restart); + +/* + * Create the debugfs files and directories + * + */ +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ + DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); + DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); + DEBUGFS_ADD_FILE(fw_restart, dir, S_IWUSR); + return 0; +} +#else +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ + return 0; +} +#endif /*CONFIG_IWLWIFI_DEBUGFS */ + +static const struct iwl_trans_ops trans_ops_pcie = { + .start_hw = iwl_trans_pcie_start_hw, + .stop_hw = iwl_trans_pcie_stop_hw, + .fw_alive = iwl_trans_pcie_fw_alive, + .start_fw = iwl_trans_pcie_start_fw, + .stop_device = iwl_trans_pcie_stop_device, + + .wowlan_suspend = iwl_trans_pcie_wowlan_suspend, + + .send_cmd = iwl_trans_pcie_send_cmd, + + .tx = iwl_trans_pcie_tx, + .reclaim = iwl_trans_pcie_reclaim, + + .tx_agg_disable = iwl_trans_pcie_tx_agg_disable, + .tx_agg_setup = iwl_trans_pcie_tx_agg_setup, + + .dbgfs_register = iwl_trans_pcie_dbgfs_register, + + .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, + +#ifdef CONFIG_PM_SLEEP + .suspend = iwl_trans_pcie_suspend, + .resume = iwl_trans_pcie_resume, +#endif + .write8 = iwl_trans_pcie_write8, + .write32 = iwl_trans_pcie_write32, + .read32 = iwl_trans_pcie_read32, + .configure = iwl_trans_pcie_configure, + .set_pmi = iwl_trans_pcie_set_pmi, +}; + +struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, + const struct pci_device_id *ent, + const struct iwl_cfg *cfg) +{ + struct iwl_trans_pcie *trans_pcie; + struct iwl_trans *trans; + u16 pci_cmd; + int err; + + trans = kzalloc(sizeof(struct iwl_trans) + + sizeof(struct iwl_trans_pcie), GFP_KERNEL); + + if (WARN_ON(!trans)) + return NULL; + + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + trans->ops = &trans_ops_pcie; + trans->cfg = cfg; + trans_pcie->trans = trans; + spin_lock_init(&trans_pcie->irq_lock); + init_waitqueue_head(&trans_pcie->ucode_write_waitq); + + /* W/A - seems to solve weird behavior. We need to remove this if we + * don't want to stay in L1 all the time. This wastes a lot of power */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_no_pci; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); + if (err) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)); + /* both attempts failed: */ + if (err) { + dev_printk(KERN_ERR, &pdev->dev, + "No suitable DMA available.\n"); + goto out_pci_disable_device; + } + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) { + dev_printk(KERN_ERR, &pdev->dev, "pci_request_regions failed"); + goto out_pci_disable_device; + } + + trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); + if (!trans_pcie->hw_base) { + dev_printk(KERN_ERR, &pdev->dev, "pci_ioremap_bar failed"); + err = -ENODEV; + goto out_pci_release_regions; + } + + dev_printk(KERN_INFO, &pdev->dev, + "pci_resource_len = 0x%08llx\n", + (unsigned long long) pci_resource_len(pdev, 0)); + dev_printk(KERN_INFO, &pdev->dev, + "pci_resource_base = %p\n", trans_pcie->hw_base); + + dev_printk(KERN_INFO, &pdev->dev, + "HW Revision ID = 0x%X\n", pdev->revision); + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + err = pci_enable_msi(pdev); + if (err) + dev_printk(KERN_ERR, &pdev->dev, + "pci_enable_msi failed(0X%x)", err); + + trans->dev = &pdev->dev; + trans_pcie->irq = pdev->irq; + trans_pcie->pci_dev = pdev; + trans->hw_rev = iwl_read32(trans, CSR_HW_REV); + trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; + snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), + "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); + + /* TODO: Move this away, not needed if not MSI */ + /* enable rfkill interrupt: hw bug w/a */ + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); + } + + /* Initialize the wait queue for commands */ + init_waitqueue_head(&trans->wait_command_queue); + spin_lock_init(&trans->reg_lock); + + return trans; + +out_pci_release_regions: + pci_release_regions(pdev); +out_pci_disable_device: + pci_disable_device(pdev); +out_no_pci: + kfree(trans); + return NULL; +} diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c new file mode 100644 index 000000000000..0b8bbb0afcab --- /dev/null +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -0,0 +1,984 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include + +#include "iwl-debug.h" +#include "iwl-csr.h" +#include "iwl-prph.h" +#include "iwl-io.h" +#include "iwl-agn-hw.h" +#include "iwl-op-mode.h" +#include "internal.h" +/* FIXME: need to abstract out TX command (once we know what it looks like) */ +#include "iwl-commands.h" + +#define IWL_TX_CRC_SIZE 4 +#define IWL_TX_DELIMITER_SIZE 4 + +/** + * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array + */ +void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_tx_queue *txq, + u16 byte_cnt) +{ + struct iwlagn_scd_bc_tbl *scd_bc_tbl; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int write_ptr = txq->q.write_ptr; + int txq_id = txq->q.id; + u8 sec_ctl = 0; + u8 sta_id = 0; + u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; + __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (void *) txq->entries[txq->q.write_ptr].cmd->payload; + + scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; + + WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); + + sta_id = tx_cmd->sta_id; + sec_ctl = tx_cmd->sec_ctl; + + switch (sec_ctl & TX_CMD_SEC_MSK) { + case TX_CMD_SEC_CCM: + len += CCMP_MIC_LEN; + break; + case TX_CMD_SEC_TKIP: + len += TKIP_ICV_LEN; + break; + case TX_CMD_SEC_WEP: + len += WEP_IV_LEN + WEP_ICV_LEN; + break; + } + + bc_ent = cpu_to_le16((len & 0xFFF) | (sta_id << 12)); + + scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; + + if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) + scd_bc_tbl[txq_id]. + tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; +} + +/** + * iwl_txq_update_write_ptr - Send new write index to hardware + */ +void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq) +{ + u32 reg = 0; + int txq_id = txq->q.id; + + if (txq->need_update == 0) + return; + + if (trans->cfg->base_params->shadow_reg_enable) { + /* shadow register enabled */ + iwl_write32(trans, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + } else { + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + /* if we're trying to save power */ + if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { + /* wake up nic if it's powered down ... + * uCode will wake up, and interrupt us again, so next + * time we'll skip this part. */ + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, + "Tx queue %d requesting wakeup," + " GP1 = 0x%x\n", txq_id, reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + return; + } + + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + + /* + * else not in power-save mode, + * uCode will never sleep when we're + * trying to tx (during RFKILL, we're not trying to tx). + */ + } else + iwl_write32(trans, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + } + txq->need_update = 0; +} + +static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + dma_addr_t addr = get_unaligned_le32(&tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + addr |= + ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; + + return addr; +} + +static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + return le16_to_cpu(tb->hi_n_len) >> 4; +} + +static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, + dma_addr_t addr, u16 len) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + u16 hi_n_len = len << 4; + + put_unaligned_le32(addr, &tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + hi_n_len |= ((addr >> 16) >> 16) & 0xF; + + tb->hi_n_len = cpu_to_le16(hi_n_len); + + tfd->num_tbs = idx + 1; +} + +static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) +{ + return tfd->num_tbs & 0x1f; +} + +static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, + struct iwl_tfd *tfd, enum dma_data_direction dma_dir) +{ + int i; + int num_tbs; + + /* Sanity check on number of chunks */ + num_tbs = iwl_tfd_get_num_tbs(tfd); + + if (num_tbs >= IWL_NUM_OF_TBS) { + IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); + /* @todo issue fatal error, it is quite serious situation */ + return; + } + + /* Unmap tx_cmd */ + if (num_tbs) + dma_unmap_single(trans->dev, + dma_unmap_addr(meta, mapping), + dma_unmap_len(meta, len), + DMA_BIDIRECTIONAL); + + /* Unmap chunks, if any. */ + for (i = 1; i < num_tbs; i++) + dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i), + iwl_tfd_tb_get_len(tfd, i), dma_dir); + + tfd->num_tbs = 0; +} + +/** + * iwl_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] + * @trans - transport private data + * @txq - tx queue + * @dma_dir - the direction of the DMA mapping + * + * Does NOT advance any TFD circular buffer read/write indexes + * Does NOT free the TFD itself (which is within circular buffer) + */ +void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, + enum dma_data_direction dma_dir) +{ + struct iwl_tfd *tfd_tmp = txq->tfds; + + /* rd_ptr is bounded by n_bd and idx is bounded by n_window */ + int rd_ptr = txq->q.read_ptr; + int idx = get_cmd_index(&txq->q, rd_ptr); + + lockdep_assert_held(&txq->lock); + + /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ + iwl_unmap_tfd(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], + dma_dir); + + /* free SKB */ + if (txq->entries) { + struct sk_buff *skb; + + skb = txq->entries[idx].skb; + + /* Can be called from irqs-disabled context + * If skb is not NULL, it means that the whole queue is being + * freed and that the queue is not empty - free the skb + */ + if (skb) { + iwl_op_mode_free_skb(trans->op_mode, skb); + txq->entries[idx].skb = NULL; + } + } +} + +int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, + struct iwl_tx_queue *txq, + dma_addr_t addr, u16 len, + u8 reset) +{ + struct iwl_queue *q; + struct iwl_tfd *tfd, *tfd_tmp; + u32 num_tbs; + + q = &txq->q; + tfd_tmp = txq->tfds; + tfd = &tfd_tmp[q->write_ptr]; + + if (reset) + memset(tfd, 0, sizeof(*tfd)); + + num_tbs = iwl_tfd_get_num_tbs(tfd); + + /* Each TFD can point to a maximum 20 Tx buffers */ + if (num_tbs >= IWL_NUM_OF_TBS) { + IWL_ERR(trans, "Error can not send more than %d chunks\n", + IWL_NUM_OF_TBS); + return -EINVAL; + } + + if (WARN_ON(addr & ~DMA_BIT_MASK(36))) + return -EINVAL; + + if (unlikely(addr & ~IWL_TX_DMA_MASK)) + IWL_ERR(trans, "Unaligned address = %llx\n", + (unsigned long long)addr); + + iwl_tfd_set_tb(tfd, num_tbs, addr, len); + + return 0; +} + +/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** + * DMA services + * + * Theory of operation + * + * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer + * of buffer descriptors, each of which points to one or more data buffers for + * the device to read from or fill. Driver and device exchange status of each + * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty + * entries in each circular buffer, to protect against confusing empty and full + * queue states. + * + * The device reads or writes the data in the queues via the device's several + * DMA/FIFO channels. Each queue is mapped to a single DMA channel. + * + * For Tx queue, there are low mark and high mark limits. If, after queuing + * the packet for Tx, free space become < low mark, Tx queue stopped. When + * reclaiming packets (on 'tx done IRQ), if free space become > high mark, + * Tx queue resumed. + * + ***************************************************/ + +int iwl_queue_space(const struct iwl_queue *q) +{ + int s = q->read_ptr - q->write_ptr; + + if (q->read_ptr > q->write_ptr) + s -= q->n_bd; + + if (s <= 0) + s += q->n_window; + /* keep some reserve to not confuse empty and full situations */ + s -= 2; + if (s < 0) + s = 0; + return s; +} + +/** + * iwl_queue_init - Initialize queue's high/low-water and read/write indexes + */ +int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) +{ + q->n_bd = count; + q->n_window = slots_num; + q->id = id; + + /* count must be power-of-two size, otherwise iwl_queue_inc_wrap + * and iwl_queue_dec_wrap are broken. */ + if (WARN_ON(!is_power_of_2(count))) + return -EINVAL; + + /* slots_num must be power-of-two size, otherwise + * get_cmd_index is broken. */ + if (WARN_ON(!is_power_of_2(slots_num))) + return -EINVAL; + + q->low_mark = q->n_window / 4; + if (q->low_mark < 4) + q->low_mark = 4; + + q->high_mark = q->n_window / 8; + if (q->high_mark < 2) + q->high_mark = 2; + + q->write_ptr = q->read_ptr = 0; + + return 0; +} + +static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_tx_queue *txq) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; + int txq_id = txq->q.id; + int read_ptr = txq->q.read_ptr; + u8 sta_id = 0; + __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (void *)txq->entries[txq->q.read_ptr].cmd->payload; + + WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); + + if (txq_id != trans_pcie->cmd_queue) + sta_id = tx_cmd->sta_id; + + bc_ent = cpu_to_le16(1 | (sta_id << 12)); + scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; + + if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) + scd_bc_tbl[txq_id]. + tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; +} + +static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid, + u16 txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 tbl_dw_addr; + u32 tbl_dw; + u16 scd_q2ratid; + + scd_q2ratid = ra_tid & SCD_QUEUE_RA_TID_MAP_RATID_MSK; + + tbl_dw_addr = trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(txq_id); + + tbl_dw = iwl_read_targ_mem(trans, tbl_dw_addr); + + if (txq_id & 0x1) + tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); + else + tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); + + iwl_write_targ_mem(trans, tbl_dw_addr, tbl_dw); + + return 0; +} + +static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id) +{ + /* Simply stop the queue, but don't change any configuration; + * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ + iwl_write_prph(trans, + SCD_QUEUE_STATUS_BITS(txq_id), + (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| + (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + +void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index) +{ + IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d\n", txq_id, index & 0xff); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (index & 0xff) | (txq_id << 8)); + iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), index); +} + +void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, + struct iwl_tx_queue *txq, + int tx_fifo_id, bool active) +{ + int txq_id = txq->q.id; + + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << SCD_QUEUE_STTS_REG_POS_WSL) | + SCD_QUEUE_STTS_REG_MSK); + + if (active) + IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d\n", + txq_id, tx_fifo_id); + else + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); +} + +void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + u16 ra_tid = BUILD_RAxTID(sta_id, tid); + + if (test_and_set_bit(txq_id, trans_pcie->queue_used)) + WARN_ONCE(1, "queue %d already used - expect issues", txq_id); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + /* Stop this Tx queue before configuring it */ + iwlagn_tx_queue_stop_scheduler(trans, txq_id); + + /* Map receiver-address / traffic-ID to this queue */ + iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id); + + /* Set this queue as a chain-building queue */ + iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); + + /* enable aggregations for the queue */ + iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + + /* Place first TFD at index corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) */ + trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); + trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); + iwl_trans_set_wr_ptrs(trans, txq_id, ssn); + + /* Set up Tx window size and frame limit for this queue */ + iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), + ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + + iwl_set_bits_prph(trans, SCD_INTERRUPT_MASK, (1 << txq_id)); + + /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ + iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], + fifo, true); + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); +} + +void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { + WARN_ONCE(1, "queue %d not used", txq_id); + return; + } + + iwlagn_tx_queue_stop_scheduler(trans, txq_id); + + iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + + trans_pcie->txq[txq_id].q.read_ptr = 0; + trans_pcie->txq[txq_id].q.write_ptr = 0; + iwl_trans_set_wr_ptrs(trans, txq_id, 0); + + iwl_clear_bits_prph(trans, SCD_INTERRUPT_MASK, BIT(txq_id)); + + iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], + 0, false); +} + +/*************** HOST COMMAND QUEUE FUNCTIONS *****/ + +/** + * iwl_enqueue_hcmd - enqueue a uCode command + * @priv: device private data point + * @cmd: a point to the ucode command structure + * + * The function returns < 0 values to indicate the operation is + * failed. On success, it turns the index (> 0) of command in the + * command queue. + */ +static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_queue *q = &txq->q; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; + dma_addr_t phys_addr; + u32 idx; + u16 copy_size, cmd_size; + bool had_nocopy = false; + int i; + u8 *cmd_dest; +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; + int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; + int trace_idx; +#endif + + copy_size = sizeof(out_cmd->hdr); + cmd_size = sizeof(out_cmd->hdr); + + /* need one for the header if the first is NOCOPY */ + BUILD_BUG_ON(IWL_MAX_CMD_TFDS > IWL_NUM_OF_TBS - 1); + + for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + if (!cmd->len[i]) + continue; + if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { + had_nocopy = true; + } else { + /* NOCOPY must not be followed by normal! */ + if (WARN_ON(had_nocopy)) + return -EINVAL; + copy_size += cmd->len[i]; + } + cmd_size += cmd->len[i]; + } + + /* + * If any of the command structures end up being larger than + * the TFD_MAX_PAYLOAD_SIZE and they aren't dynamically + * allocated into separate TFDs, then we will need to + * increase the size of the buffers. + */ + if (WARN_ON(copy_size > TFD_MAX_PAYLOAD_SIZE)) + return -EINVAL; + + spin_lock_bh(&txq->lock); + + if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { + spin_unlock_bh(&txq->lock); + + IWL_ERR(trans, "No space in command queue\n"); + iwl_op_mode_cmd_queue_full(trans->op_mode); + return -ENOSPC; + } + + idx = get_cmd_index(q, q->write_ptr); + out_cmd = txq->entries[idx].cmd; + out_meta = &txq->entries[idx].meta; + + memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + + /* set up the header */ + + out_cmd->hdr.cmd = cmd->id; + out_cmd->hdr.flags = 0; + out_cmd->hdr.sequence = + cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->cmd_queue) | + INDEX_TO_SEQ(q->write_ptr)); + + /* and copy the data that needs to be copied */ + + cmd_dest = out_cmd->payload; + for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + if (!cmd->len[i]) + continue; + if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) + break; + memcpy(cmd_dest, cmd->data[i], cmd->len[i]); + cmd_dest += cmd->len[i]; + } + + IWL_DEBUG_HC(trans, + "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", + trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), + out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), + cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); + + phys_addr = dma_map_single(trans->dev, &out_cmd->hdr, copy_size, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { + idx = -ENOMEM; + goto out; + } + + dma_unmap_addr_set(out_meta, mapping, phys_addr); + dma_unmap_len_set(out_meta, len, copy_size); + + iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + trace_bufs[0] = &out_cmd->hdr; + trace_lens[0] = copy_size; + trace_idx = 1; +#endif + + for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + if (!cmd->len[i]) + continue; + if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) + continue; + phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], + cmd->len[i], DMA_BIDIRECTIONAL); + if (dma_mapping_error(trans->dev, phys_addr)) { + iwl_unmap_tfd(trans, out_meta, + &txq->tfds[q->write_ptr], + DMA_BIDIRECTIONAL); + idx = -ENOMEM; + goto out; + } + + iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, + cmd->len[i], 0); +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + trace_bufs[trace_idx] = cmd->data[i]; + trace_lens[trace_idx] = cmd->len[i]; + trace_idx++; +#endif + } + + out_meta->flags = cmd->flags; + + txq->need_update = 1; + + /* check that tracing gets all possible blocks */ + BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + trace_iwlwifi_dev_hcmd(trans->dev, cmd->flags, + trace_bufs[0], trace_lens[0], + trace_bufs[1], trace_lens[1], + trace_bufs[2], trace_lens[2]); +#endif + + /* start timer if queue currently empty */ + if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + + /* Increment and update queue's write index */ + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl_txq_update_write_ptr(trans, txq); + + out: + spin_unlock_bh(&txq->lock); + return idx; +} + +static inline void iwl_queue_progress(struct iwl_trans_pcie *trans_pcie, + struct iwl_tx_queue *txq) +{ + if (!trans_pcie->wd_timeout) + return; + + /* + * if empty delete timer, otherwise move timer forward + * since we're making progress on this queue + */ + if (txq->q.read_ptr == txq->q.write_ptr) + del_timer(&txq->stuck_timer); + else + mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); +} + +/** + * iwl_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd + * + * When FW advances 'R' index, all entries between old and new 'R' index + * need to be reclaimed. As result, some free space forms. If there is + * enough free space (> low mark), wake the stack that feeds us. + */ +static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, + int idx) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + int nfreed = 0; + + lockdep_assert_held(&txq->lock); + + if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) { + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, idx, q->n_bd, + q->write_ptr, q->read_ptr); + return; + } + + for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + if (nfreed++ > 0) { + IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", + idx, q->write_ptr, q->read_ptr); + iwl_op_mode_nic_error(trans->op_mode); + } + + } + + iwl_queue_progress(trans_pcie, txq); +} + +/** + * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them + * @rxb: Rx buffer to reclaim + * @handler_status: return value of the handler of the command + * (put in setup_rx_handlers) + * + * If an Rx buffer has an async callback associated with it the callback + * will be executed. The attached skb (if present) will only be freed + * if the callback returns 1 + */ +void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, + int handler_status) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int index = SEQ_TO_INDEX(sequence); + int cmd_index; + struct iwl_device_cmd *cmd; + struct iwl_cmd_meta *meta; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + + /* If a Tx command is being handled and it isn't in the actual + * command queue then there a command routing bug has been introduced + * in the queue management code. */ + if (WARN(txq_id != trans_pcie->cmd_queue, + "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", + txq_id, trans_pcie->cmd_queue, sequence, + trans_pcie->txq[trans_pcie->cmd_queue].q.read_ptr, + trans_pcie->txq[trans_pcie->cmd_queue].q.write_ptr)) { + iwl_print_hex_error(trans, pkt, 32); + return; + } + + spin_lock(&txq->lock); + + cmd_index = get_cmd_index(&txq->q, index); + cmd = txq->entries[cmd_index].cmd; + meta = &txq->entries[cmd_index].meta; + + iwl_unmap_tfd(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); + + /* Input error checking is done when commands are added to queue. */ + if (meta->flags & CMD_WANT_SKB) { + struct page *p = rxb_steal_page(rxb); + + meta->source->resp_pkt = pkt; + meta->source->_rx_page_addr = (unsigned long)page_address(p); + meta->source->_rx_page_order = trans_pcie->rx_page_order; + meta->source->handler_status = handler_status; + } + + iwl_hcmd_queue_reclaim(trans, txq_id, index); + + if (!(meta->flags & CMD_ASYNC)) { + if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { + IWL_WARN(trans, + "HCMD_ACTIVE already clear for command %s\n", + trans_pcie_get_cmd_string(trans_pcie, + cmd->hdr.cmd)); + } + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", + trans_pcie_get_cmd_string(trans_pcie, + cmd->hdr.cmd)); + wake_up(&trans->wait_command_queue); + } + + meta->flags = 0; + + spin_unlock(&txq->lock); +} + +#define HOST_COMPLETE_TIMEOUT (2 * HZ) + +static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + + /* An asynchronous command can not expect an SKB to be set. */ + if (WARN_ON(cmd->flags & CMD_WANT_SKB)) + return -EINVAL; + + + ret = iwl_enqueue_hcmd(trans, cmd); + if (ret < 0) { + IWL_ERR(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); + return ret; + } + return 0; +} + +static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int cmd_idx; + int ret; + + IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + + if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status))) { + IWL_ERR(trans, "Command %s: a command is already active!\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + return -EIO; + } + + IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + + cmd_idx = iwl_enqueue_hcmd(trans, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + IWL_ERR(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); + return ret; + } + + ret = wait_event_timeout(trans->wait_command_queue, + !test_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status), + HOST_COMPLETE_TIMEOUT); + if (!ret) { + if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { + struct iwl_tx_queue *txq = + &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_queue *q = &txq->q; + + IWL_ERR(trans, + "Error sending %s: time out after %dms.\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id), + jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + + IWL_ERR(trans, + "Current CMD queue read_ptr %d write_ptr %d\n", + q->read_ptr, q->write_ptr); + + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + IWL_DEBUG_INFO(trans, + "Clearing HCMD_ACTIVE for command %s\n", + trans_pcie_get_cmd_string(trans_pcie, + cmd->id)); + ret = -ETIMEDOUT; + goto cancel; + } + } + + if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { + IWL_ERR(trans, "Error: Response NULL in '%s'\n", + trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + ret = -EIO; + goto cancel; + } + + return 0; + +cancel: + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the + * TX cmd queue. Otherwise in case the cmd comes + * in later, it will possibly set an invalid + * address (cmd->meta.source). + */ + trans_pcie->txq[trans_pcie->cmd_queue]. + entries[cmd_idx].meta.flags &= ~CMD_WANT_SKB; + } + + if (cmd->resp_pkt) { + iwl_free_resp(cmd); + cmd->resp_pkt = NULL; + } + + return ret; +} + +int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +{ + if (cmd->flags & CMD_ASYNC) + return iwl_send_cmd_async(trans, cmd); + + return iwl_send_cmd_sync(trans, cmd); +} + +/* Frees buffers until index _not_ inclusive */ +int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, + struct sk_buff_head *skbs) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + int last_to_free; + int freed = 0; + + /* This function is not meant to release cmd queue*/ + if (WARN_ON(txq_id == trans_pcie->cmd_queue)) + return 0; + + lockdep_assert_held(&txq->lock); + + /*Since we free until index _not_ inclusive, the one before index is + * the last we will free. This one must be used */ + last_to_free = iwl_queue_dec_wrap(index, q->n_bd); + + if ((index >= q->n_bd) || + (iwl_queue_used(q, last_to_free) == 0)) { + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, last_to_free, q->n_bd, + q->write_ptr, q->read_ptr); + return 0; + } + + if (WARN_ON(!skb_queue_empty(skbs))) + return 0; + + for (; + q->read_ptr != index; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) + continue; + + __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); + + txq->entries[txq->q.read_ptr].skb = NULL; + + iwlagn_txq_inval_byte_cnt_tbl(trans, txq); + + iwl_txq_free_tfd(trans, txq, DMA_TO_DEVICE); + freed++; + } + + iwl_queue_progress(trans_pcie, txq); + + return freed; +} -- cgit v1.2.3 From 20041ea622fcb1992df536d253de5120808e64a5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 May 2012 12:56:31 +0200 Subject: iwlwifi: trust mac80211 HT40 setting When mac80211 asks us to do HT40, it'll not do so on a channel that we marked as not having HT40+/- with IEEE80211_CHAN_NO_HT40PLUS (or MINUS). Thus, there's no need to verify it again in the driver. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-agn-sta.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c index 36055ed1c069..01fbf06980c8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c @@ -171,26 +171,6 @@ int iwl_send_add_sta(struct iwl_priv *priv, return cmd.handler_status; } -static bool iwl_is_channel_extension(struct iwl_priv *priv, - enum ieee80211_band band, - u16 channel, u8 extension_chan_offset) -{ - const struct iwl_channel_info *ch_info; - - ch_info = iwl_get_channel_info(priv, band, channel); - if (!is_channel_valid(ch_info)) - return false; - - if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - return !(ch_info->ht40_extension_channel & - IEEE80211_CHAN_NO_HT40PLUS); - else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) - return !(ch_info->ht40_extension_channel & - IEEE80211_CHAN_NO_HT40MINUS); - - return false; -} - bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_sta_ht_cap *ht_cap) @@ -210,9 +190,7 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, return false; #endif - return iwl_is_channel_extension(priv, priv->band, - le16_to_cpu(ctx->staging.channel), - ctx->ht.extension_chan_offset); + return true; } static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, -- cgit v1.2.3 From 1023fdc4858b6b8cb88ff28cafd425b77555be9f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 15 May 2012 12:16:34 +0200 Subject: iwlwifi: move DVM code into subdirectory Since we're working on another mode/driver inside iwlwifi, move the current one into a subdirectory to more cleanly separate the code. While at it, rename all the files. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/Makefile | 15 +- drivers/net/wireless/iwlwifi/dvm/Makefile | 13 + drivers/net/wireless/iwlwifi/dvm/agn.h | 527 ++++ drivers/net/wireless/iwlwifi/dvm/calib.c | 1113 +++++++ drivers/net/wireless/iwlwifi/dvm/calib.h | 74 + drivers/net/wireless/iwlwifi/dvm/commands.h | 3958 ++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/dvm/debugfs.c | 2433 +++++++++++++++ drivers/net/wireless/iwlwifi/dvm/dev.h | 1073 +++++++ drivers/net/wireless/iwlwifi/dvm/devices.c | 740 +++++ drivers/net/wireless/iwlwifi/dvm/eeprom.c | 1146 +++++++ drivers/net/wireless/iwlwifi/dvm/eeprom.h | 269 ++ drivers/net/wireless/iwlwifi/dvm/led.c | 224 ++ drivers/net/wireless/iwlwifi/dvm/led.h | 43 + drivers/net/wireless/iwlwifi/dvm/lib.c | 1291 ++++++++ drivers/net/wireless/iwlwifi/dvm/mac80211.c | 1631 ++++++++++ drivers/net/wireless/iwlwifi/dvm/main.c | 2371 ++++++++++++++ drivers/net/wireless/iwlwifi/dvm/power.c | 388 +++ drivers/net/wireless/iwlwifi/dvm/power.h | 47 + drivers/net/wireless/iwlwifi/dvm/rs.c | 3367 ++++++++++++++++++++ drivers/net/wireless/iwlwifi/dvm/rs.h | 433 +++ drivers/net/wireless/iwlwifi/dvm/rx.c | 1167 +++++++ drivers/net/wireless/iwlwifi/dvm/rxon.c | 1577 ++++++++++ drivers/net/wireless/iwlwifi/dvm/scan.c | 1169 +++++++ drivers/net/wireless/iwlwifi/dvm/sta.c | 1479 +++++++++ drivers/net/wireless/iwlwifi/dvm/testmode.c | 1113 +++++++ drivers/net/wireless/iwlwifi/dvm/testmode.h | 309 ++ drivers/net/wireless/iwlwifi/dvm/tt.c | 694 +++++ drivers/net/wireless/iwlwifi/dvm/tt.h | 128 + drivers/net/wireless/iwlwifi/dvm/tx.c | 1385 +++++++++ drivers/net/wireless/iwlwifi/dvm/ucode.c | 533 ++++ drivers/net/wireless/iwlwifi/iwl-agn-calib.c | 1112 ------- drivers/net/wireless/iwlwifi/iwl-agn-calib.h | 74 - drivers/net/wireless/iwlwifi/iwl-agn-devices.c | 739 ----- drivers/net/wireless/iwlwifi/iwl-agn-lib.c | 1290 -------- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 3369 -------------------- drivers/net/wireless/iwlwifi/iwl-agn-rs.h | 432 --- drivers/net/wireless/iwlwifi/iwl-agn-rx.c | 1168 ------- drivers/net/wireless/iwlwifi/iwl-agn-rxon.c | 1577 ---------- drivers/net/wireless/iwlwifi/iwl-agn-sta.c | 1480 --------- drivers/net/wireless/iwlwifi/iwl-agn-tt.c | 696 ----- drivers/net/wireless/iwlwifi/iwl-agn-tt.h | 128 - drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 1386 --------- drivers/net/wireless/iwlwifi/iwl-agn.c | 2370 -------------- drivers/net/wireless/iwlwifi/iwl-agn.h | 526 ---- drivers/net/wireless/iwlwifi/iwl-commands.h | 3958 ------------------------ drivers/net/wireless/iwlwifi/iwl-debugfs.c | 2437 --------------- drivers/net/wireless/iwlwifi/iwl-dev.h | 1072 ------- drivers/net/wireless/iwlwifi/iwl-devtrace.h | 1 + drivers/net/wireless/iwlwifi/iwl-eeprom.c | 1148 ------- drivers/net/wireless/iwlwifi/iwl-eeprom.h | 269 -- drivers/net/wireless/iwlwifi/iwl-led.c | 225 -- drivers/net/wireless/iwlwifi/iwl-led.h | 43 - drivers/net/wireless/iwlwifi/iwl-mac80211.c | 1630 ---------- drivers/net/wireless/iwlwifi/iwl-power.c | 390 --- drivers/net/wireless/iwlwifi/iwl-power.h | 47 - drivers/net/wireless/iwlwifi/iwl-scan.c | 1171 ------- drivers/net/wireless/iwlwifi/iwl-testmode.c | 1114 ------- drivers/net/wireless/iwlwifi/iwl-testmode.h | 309 -- drivers/net/wireless/iwlwifi/iwl-ucode.c | 532 ---- drivers/net/wireless/iwlwifi/pcie/2000.c | 2 +- drivers/net/wireless/iwlwifi/pcie/6000.c | 2 +- drivers/net/wireless/iwlwifi/pcie/trans.c | 3 +- drivers/net/wireless/iwlwifi/pcie/tx.c | 3 +- 63 files changed, 30702 insertions(+), 30711 deletions(-) create mode 100644 drivers/net/wireless/iwlwifi/dvm/Makefile create mode 100644 drivers/net/wireless/iwlwifi/dvm/agn.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/calib.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/calib.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/commands.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/debugfs.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/dev.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/devices.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/eeprom.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/eeprom.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/led.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/led.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/lib.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/mac80211.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/main.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/power.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/power.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/rs.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/rs.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/rx.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/rxon.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/scan.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/sta.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/testmode.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/testmode.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/tt.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/tt.h create mode 100644 drivers/net/wireless/iwlwifi/dvm/tx.c create mode 100644 drivers/net/wireless/iwlwifi/dvm/ucode.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-calib.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-calib.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-devices.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-lib.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-rs.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-rs.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-rx.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-rxon.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-sta.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-tt.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-tt.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-tx.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-agn.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-commands.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-debugfs.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-dev.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-led.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-led.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-mac80211.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-power.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-power.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-scan.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-testmode.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-testmode.h delete mode 100644 drivers/net/wireless/iwlwifi/iwl-ucode.c (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index f155126e9a21..7ba6b8dc1cbd 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,19 +1,8 @@ -# DVM -obj-$(CONFIG_IWLDVM) += iwldvm.o -iwldvm-objs := iwl-agn.o iwl-agn-rs.o iwl-mac80211.o -iwldvm-objs += iwl-ucode.o iwl-agn-tx.o -iwldvm-objs += iwl-agn-lib.o iwl-agn-calib.o -iwldvm-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o -iwldvm-objs += iwl-eeprom.o iwl-power.o -iwldvm-objs += iwl-scan.o iwl-led.o -iwldvm-objs += iwl-agn-rxon.o iwl-agn-devices.o - -iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o -iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o +obj-$(CONFIG_IWLDVM) += dvm/ CFLAGS_iwl-devtrace.o := -I$(src) -# WIFI +# common obj-$(CONFIG_IWLWIFI) += iwlwifi.o iwlwifi-objs += iwl-io.o iwlwifi-objs += iwl-drv.o diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/iwlwifi/dvm/Makefile new file mode 100644 index 000000000000..13f8282f4d0e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/Makefile @@ -0,0 +1,13 @@ +# DVM +obj-$(CONFIG_IWLDVM) += iwldvm.o +iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o +iwldvm-objs += lib.o calib.o tt.o sta.o rx.o + +iwldvm-objs += eeprom.o power.o +iwldvm-objs += scan.o led.o +iwldvm-objs += rxon.o devices.o + +iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o +iwldvm-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += testmode.o + +ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h new file mode 100644 index 000000000000..9a8dd1a579d0 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -0,0 +1,527 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_agn_h__ +#define __iwl_agn_h__ + +#include "iwl-config.h" + +#include "dev.h" + +/* The first 11 queues (0-10) are used otherwise */ +#define IWLAGN_FIRST_AMPDU_QUEUE 11 + +/* AUX (TX during scan dwell) queue */ +#define IWL_AUX_QUEUE 10 + +/* device operations */ +extern struct iwl_lib_ops iwl1000_lib; +extern struct iwl_lib_ops iwl2000_lib; +extern struct iwl_lib_ops iwl2030_lib; +extern struct iwl_lib_ops iwl5000_lib; +extern struct iwl_lib_ops iwl5150_lib; +extern struct iwl_lib_ops iwl6000_lib; +extern struct iwl_lib_ops iwl6030_lib; + + +#define TIME_UNIT 1024 + +/***************************************************** +* DRIVER STATUS FUNCTIONS +******************************************************/ +#define STATUS_RF_KILL_HW 0 +#define STATUS_CT_KILL 1 +#define STATUS_ALIVE 2 +#define STATUS_READY 3 +#define STATUS_GEO_CONFIGURED 4 +#define STATUS_EXIT_PENDING 5 +#define STATUS_STATISTICS 6 +#define STATUS_SCANNING 7 +#define STATUS_SCAN_ABORTING 8 +#define STATUS_SCAN_HW 9 +#define STATUS_FW_ERROR 10 +#define STATUS_CHANNEL_SWITCH_PENDING 11 +#define STATUS_SCAN_COMPLETE 12 +#define STATUS_POWER_PMI 13 +#define STATUS_SCAN_ROC_EXPIRED 14 + +struct iwl_ucode_capabilities; + +extern struct ieee80211_ops iwlagn_hw_ops; + +static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) +{ + hdr->op_code = cmd; + hdr->first_group = 0; + hdr->groups_num = 1; + hdr->data_valid = 1; +} + +void iwl_down(struct iwl_priv *priv); +void iwl_cancel_deferred_work(struct iwl_priv *priv); +void iwlagn_prepare_restart(struct iwl_priv *priv); +int __must_check iwl_rx_dispatch(struct iwl_op_mode *op_mode, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + +bool iwl_check_for_ct_kill(struct iwl_priv *priv); + +void iwlagn_lift_passive_no_rx(struct iwl_priv *priv); + +/* MAC80211 */ +struct ieee80211_hw *iwl_alloc_all(void); +int iwlagn_mac_setup_register(struct iwl_priv *priv, + const struct iwl_ucode_capabilities *capa); +void iwlagn_mac_unregister(struct iwl_priv *priv); + +/* commands */ +int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); +int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, + u32 flags, u16 len, const void *data); + +/* RXON */ +void iwl_connection_init_rx_config(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwlagn_set_pan_params(struct iwl_priv *priv); +int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed); +void iwlagn_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes); +void iwlagn_config_ht40(struct ieee80211_conf *conf, + struct iwl_rxon_context *ctx); +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf); +void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx); +void iwl_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + enum ieee80211_band band, + struct ieee80211_vif *vif); + +/* uCode */ +int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); +void iwl_send_prio_tbl(struct iwl_priv *priv); +int iwl_init_alive_start(struct iwl_priv *priv); +int iwl_run_init_ucode(struct iwl_priv *priv); +int iwl_load_ucode_wait_alive(struct iwl_priv *priv, + enum iwl_ucode_type ucode_type); +int iwl_send_calib_results(struct iwl_priv *priv); +int iwl_calib_set(struct iwl_priv *priv, + const struct iwl_calib_hdr *cmd, int len); +void iwl_calib_free_results(struct iwl_priv *priv); +int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf, bool display); +int iwlagn_hw_valid_rtc_data_addr(u32 addr); + +/* lib */ +int iwlagn_send_tx_power(struct iwl_priv *priv); +void iwlagn_temperature(struct iwl_priv *priv); +int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); +int iwlagn_send_beacon_cmd(struct iwl_priv *priv); +int iwl_send_statistics_request(struct iwl_priv *priv, + u8 flags, bool clear); + +static inline const struct ieee80211_supported_band *iwl_get_hw_mode( + struct iwl_priv *priv, enum ieee80211_band band) +{ + return priv->hw->wiphy->bands[band]; +} + +#ifdef CONFIG_PM_SLEEP +int iwlagn_send_patterns(struct iwl_priv *priv, + struct cfg80211_wowlan *wowlan); +int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan); +#endif + +/* rx */ +int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); +void iwl_setup_rx_handlers(struct iwl_priv *priv); +void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); + + +/* tx */ +int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); +int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn); +int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size); +int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); +int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + +static inline u32 iwl_tx_status_to_mac80211(u32 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + return IEEE80211_TX_STAT_ACK; + case TX_STATUS_FAIL_DEST_PS: + case TX_STATUS_FAIL_PASSIVE_NO_RX: + return IEEE80211_TX_STAT_TX_FILTERED; + default: + return 0; + } +} + +static inline bool iwl_is_tx_success(u32 status) +{ + status &= TX_STATUS_MSK; + return (status == TX_STATUS_SUCCESS) || + (status == TX_STATUS_DIRECT_DONE); +} + +u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); + +/* scan */ +void iwlagn_post_scan(struct iwl_priv *priv); +void iwlagn_disable_roc(struct iwl_priv *priv); +int iwl_force_rf_reset(struct iwl_priv *priv, bool external); +void iwl_init_scan_params(struct iwl_priv *priv); +int iwl_scan_cancel(struct iwl_priv *priv); +void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); +void iwl_force_scan_end(struct iwl_priv *priv); +void iwl_internal_short_hw_scan(struct iwl_priv *priv); +void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); +void iwl_setup_scan_deferred_work(struct iwl_priv *priv); +void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); +int __must_check iwl_scan_initiate(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum iwl_scan_type scan_type, + enum ieee80211_band band); + +void iwl_scan_roc_expired(struct iwl_priv *priv); +void iwl_scan_offchannel_skb(struct iwl_priv *priv); +void iwl_scan_offchannel_skb_status(struct iwl_priv *priv); + +/* For faster active scanning, scan will move to the next channel if fewer than + * PLCP_QUIET_THRESH packets are heard on this channel within + * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell + * time if it's a quiet channel (nothing responded to our probe, and there's + * no other traffic). + * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ +#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ +#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ + +#define IWL_SCAN_CHECK_WATCHDOG (HZ * 7) + + +/* bt coex */ +void iwlagn_send_advance_bt_config(struct iwl_priv *priv); +int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv); +void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv); +void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv); +void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv); +void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); + +static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) +{ + return priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist; +} + +#ifdef CONFIG_IWLWIFI_DEBUG +const char *iwl_get_tx_fail_reason(u32 status); +const char *iwl_get_agg_tx_fail_reason(u16 status); +#else +static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } +static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; } +#endif + + +/* station management */ +int iwlagn_manage_ibss_station(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add); +#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ +#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ +#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of + being activated */ +#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211; + (this is for the IBSS BSSID stations) */ +#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */ + + +void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +void iwl_clear_ucode_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +void iwl_dealloc_bcast_stations(struct iwl_priv *priv); +int iwl_get_free_ucode_key_offset(struct iwl_priv *priv); +int iwl_send_add_sta(struct iwl_priv *priv, + struct iwl_addsta_cmd *sta, u8 flags); +int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r); +int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr); +void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr); +u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, struct ieee80211_sta *sta); + +int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq, u8 flags, bool init); +int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_sta *sta); + +bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta_ht_cap *ht_cap); + +static inline int iwl_sta_id(struct ieee80211_sta *sta) +{ + if (WARN_ON(!sta)) + return IWL_INVALID_STATION; + + return ((struct iwl_station_priv *)sta->drv_priv)->sta_id; +} + +int iwlagn_alloc_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, u8 *sta_id_r); +int iwl_remove_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key); +int iwl_set_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key); +int iwl_restore_default_wep_keys(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key, + struct ieee80211_sta *sta); +int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key, + struct ieee80211_sta *sta); +void iwl_update_tkip_key(struct iwl_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); +int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); +int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid, u16 ssn); +int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid); +void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); +int iwl_update_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl_update_bcast_stations(struct iwl_priv *priv); + +/* rate */ +static inline u32 iwl_ant_idx_to_flags(u8 ant_idx) +{ + return BIT(ant_idx) << RATE_MCS_ANT_POS; +} + +static inline u8 iwl_hw_get_rate(__le32 rate_n_flags) +{ + return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK; +} + +static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) +{ + return cpu_to_le32(flags|(u32)rate); +} + +extern int iwl_alive_start(struct iwl_priv *priv); +/* svtool */ +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE +extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, + int len); +extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len); +extern void iwl_testmode_init(struct iwl_priv *priv); +extern void iwl_testmode_cleanup(struct iwl_priv *priv); +#else +static inline +int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) +{ + return -ENOSYS; +} +static inline +int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len) +{ + return -ENOSYS; +} +static inline +void iwl_testmode_init(struct iwl_priv *priv) +{ +} +static inline +void iwl_testmode_cleanup(struct iwl_priv *priv) +{ +} +#endif + +#ifdef CONFIG_IWLWIFI_DEBUG +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid); +#else +static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ +} +#endif + +/* status checks */ + +static inline int iwl_is_ready(struct iwl_priv *priv) +{ + /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are + * set but EXIT_PENDING is not */ + return test_bit(STATUS_READY, &priv->status) && + test_bit(STATUS_GEO_CONFIGURED, &priv->status) && + !test_bit(STATUS_EXIT_PENDING, &priv->status); +} + +static inline int iwl_is_alive(struct iwl_priv *priv) +{ + return test_bit(STATUS_ALIVE, &priv->status); +} + +static inline int iwl_is_rfkill(struct iwl_priv *priv) +{ + return test_bit(STATUS_RF_KILL_HW, &priv->status); +} + +static inline int iwl_is_ctkill(struct iwl_priv *priv) +{ + return test_bit(STATUS_CT_KILL, &priv->status); +} + +static inline int iwl_is_ready_rf(struct iwl_priv *priv) +{ + if (iwl_is_rfkill(priv)) + return 0; + + return iwl_is_ready(priv); +} + +static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) +{ + if (state) + set_bit(STATUS_POWER_PMI, &priv->status); + else + clear_bit(STATUS_POWER_PMI, &priv->status); + iwl_trans_set_pmi(priv->trans, state); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_dbgfs_register(struct iwl_priv *priv, const char *name); +void iwl_dbgfs_unregister(struct iwl_priv *priv); +#else +static inline int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) +{ + return 0; +} +static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) +{ +} +#endif /* CONFIG_IWLWIFI_DEBUGFS */ + +#ifdef CONFIG_IWLWIFI_DEBUG +#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ +do { \ + if (!iwl_is_rfkill((m))) \ + IWL_ERR(m, fmt, ##args); \ + else \ + __iwl_err((m)->dev, true, \ + !iwl_have_debug_level(IWL_DL_RADIO), \ + fmt, ##args); \ +} while (0) +#else +#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ +do { \ + if (!iwl_is_rfkill((m))) \ + IWL_ERR(m, fmt, ##args); \ + else \ + __iwl_err((m)->dev, true, true, fmt, ##args); \ +} while (0) +#endif /* CONFIG_IWLWIFI_DEBUG */ + +extern const char *iwl_dvm_cmd_strings[REPLY_MAX]; + +static inline const char *iwl_dvm_get_cmd_string(u8 cmd) +{ + const char *s = iwl_dvm_cmd_strings[cmd]; + if (s) + return s; + return "UNKNOWN"; +} +#endif /* __iwl_agn_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c new file mode 100644 index 000000000000..7eb3fa3df7c4 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -0,0 +1,1113 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include + +#include "iwl-trans.h" + +#include "dev.h" +#include "calib.h" +#include "agn.h" + +/***************************************************************************** + * INIT calibrations framework + *****************************************************************************/ + +/* Opaque calibration results */ +struct iwl_calib_result { + struct list_head list; + size_t cmd_len; + struct iwl_calib_hdr hdr; + /* data follows */ +}; + +struct statistics_general_data { + u32 beacon_silence_rssi_a; + u32 beacon_silence_rssi_b; + u32 beacon_silence_rssi_c; + u32 beacon_energy_a; + u32 beacon_energy_b; + u32 beacon_energy_c; +}; + +int iwl_send_calib_results(struct iwl_priv *priv) +{ + struct iwl_host_cmd hcmd = { + .id = REPLY_PHY_CALIBRATION_CMD, + .flags = CMD_SYNC, + }; + struct iwl_calib_result *res; + + list_for_each_entry(res, &priv->calib_results, list) { + int ret; + + hcmd.len[0] = res->cmd_len; + hcmd.data[0] = &res->hdr; + hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + ret = iwl_dvm_send_cmd(priv, &hcmd); + if (ret) { + IWL_ERR(priv, "Error %d on calib cmd %d\n", + ret, res->hdr.op_code); + return ret; + } + } + + return 0; +} + +int iwl_calib_set(struct iwl_priv *priv, + const struct iwl_calib_hdr *cmd, int len) +{ + struct iwl_calib_result *res, *tmp; + + res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr), + GFP_ATOMIC); + if (!res) + return -ENOMEM; + memcpy(&res->hdr, cmd, len); + res->cmd_len = len; + + list_for_each_entry(tmp, &priv->calib_results, list) { + if (tmp->hdr.op_code == res->hdr.op_code) { + list_replace(&tmp->list, &res->list); + kfree(tmp); + return 0; + } + } + + /* wasn't in list already */ + list_add_tail(&res->list, &priv->calib_results); + + return 0; +} + +void iwl_calib_free_results(struct iwl_priv *priv) +{ + struct iwl_calib_result *res, *tmp; + + list_for_each_entry_safe(res, tmp, &priv->calib_results, list) { + list_del(&res->list); + kfree(res); + } +} + +/***************************************************************************** + * RUNTIME calibrations framework + *****************************************************************************/ + +/* "false alarms" are signals that our DSP tries to lock onto, + * but then determines that they are either noise, or transmissions + * from a distant wireless network (also "noise", really) that get + * "stepped on" by stronger transmissions within our own network. + * This algorithm attempts to set a sensitivity level that is high + * enough to receive all of our own network traffic, but not so + * high that our DSP gets too busy trying to lock onto non-network + * activity/noise. */ +static int iwl_sens_energy_cck(struct iwl_priv *priv, + u32 norm_fa, + u32 rx_enable_time, + struct statistics_general_data *rx_info) +{ + u32 max_nrg_cck = 0; + int i = 0; + u8 max_silence_rssi = 0; + u32 silence_ref = 0; + u8 silence_rssi_a = 0; + u8 silence_rssi_b = 0; + u8 silence_rssi_c = 0; + u32 val; + + /* "false_alarms" values below are cross-multiplications to assess the + * numbers of false alarms within the measured period of actual Rx + * (Rx is off when we're txing), vs the min/max expected false alarms + * (some should be expected if rx is sensitive enough) in a + * hypothetical listening period of 200 time units (TU), 204.8 msec: + * + * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time + * + * */ + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; + u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + data = &(priv->sensitivity_data); + + data->nrg_auto_corr_silence_diff = 0; + + /* Find max silence rssi among all 3 receivers. + * This is background noise, which may include transmissions from other + * networks, measured during silence before our network's beacon */ + silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & + ALL_BAND_FILTER) >> 8); + silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & + ALL_BAND_FILTER) >> 8); + silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & + ALL_BAND_FILTER) >> 8); + + val = max(silence_rssi_b, silence_rssi_c); + max_silence_rssi = max(silence_rssi_a, (u8) val); + + /* Store silence rssi in 20-beacon history table */ + data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; + data->nrg_silence_idx++; + if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) + data->nrg_silence_idx = 0; + + /* Find max silence rssi across 20 beacon history */ + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { + val = data->nrg_silence_rssi[i]; + silence_ref = max(silence_ref, val); + } + IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", + silence_rssi_a, silence_rssi_b, silence_rssi_c, + silence_ref); + + /* Find max rx energy (min value!) among all 3 receivers, + * measured during beacon frame. + * Save it in 10-beacon history table. */ + i = data->nrg_energy_idx; + val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); + data->nrg_value[i] = min(rx_info->beacon_energy_a, val); + + data->nrg_energy_idx++; + if (data->nrg_energy_idx >= 10) + data->nrg_energy_idx = 0; + + /* Find min rx energy (max value) across 10 beacon history. + * This is the minimum signal level that we want to receive well. + * Add backoff (margin so we don't miss slightly lower energy frames). + * This establishes an upper bound (min value) for energy threshold. */ + max_nrg_cck = data->nrg_value[0]; + for (i = 1; i < 10; i++) + max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); + max_nrg_cck += 6; + + IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", + rx_info->beacon_energy_a, rx_info->beacon_energy_b, + rx_info->beacon_energy_c, max_nrg_cck - 6); + + /* Count number of consecutive beacons with fewer-than-desired + * false alarms. */ + if (false_alarms < min_false_alarms) + data->num_in_cck_no_fa++; + else + data->num_in_cck_no_fa = 0; + IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", + data->num_in_cck_no_fa); + + /* If we got too many false alarms this time, reduce sensitivity */ + if ((false_alarms > max_false_alarms) && + (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { + IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", + false_alarms, max_false_alarms); + IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); + data->nrg_curr_state = IWL_FA_TOO_MANY; + /* Store for "fewer than desired" on later beacon */ + data->nrg_silence_ref = silence_ref; + + /* increase energy threshold (reduce nrg value) + * to decrease sensitivity */ + data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; + /* Else if we got fewer than desired, increase sensitivity */ + } else if (false_alarms < min_false_alarms) { + data->nrg_curr_state = IWL_FA_TOO_FEW; + + /* Compare silence level with silence level for most recent + * healthy number or too many false alarms */ + data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - + (s32)silence_ref; + + IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n", + false_alarms, min_false_alarms, + data->nrg_auto_corr_silence_diff); + + /* Increase value to increase sensitivity, but only if: + * 1a) previous beacon did *not* have *too many* false alarms + * 1b) AND there's a significant difference in Rx levels + * from a previous beacon with too many, or healthy # FAs + * OR 2) We've seen a lot of beacons (100) with too few + * false alarms */ + if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && + ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || + (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { + + IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); + /* Increase nrg value to increase sensitivity */ + val = data->nrg_th_cck + NRG_STEP_CCK; + data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); + } else { + IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n"); + } + + /* Else we got a healthy number of false alarms, keep status quo */ + } else { + IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); + data->nrg_curr_state = IWL_FA_GOOD_RANGE; + + /* Store for use in "fewer than desired" with later beacon */ + data->nrg_silence_ref = silence_ref; + + /* If previous beacon had too many false alarms, + * give it some extra margin by reducing sensitivity again + * (but don't go below measured energy of desired Rx) */ + if (IWL_FA_TOO_MANY == data->nrg_prev_state) { + IWL_DEBUG_CALIB(priv, "... increasing margin\n"); + if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) + data->nrg_th_cck -= NRG_MARGIN; + else + data->nrg_th_cck = max_nrg_cck; + } + } + + /* Make sure the energy threshold does not go above the measured + * energy of the desired Rx signals (reduced by backoff margin), + * or else we might start missing Rx frames. + * Lower value is higher energy, so we use max()! + */ + data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); + IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); + + data->nrg_prev_state = data->nrg_curr_state; + + /* Auto-correlation CCK algorithm */ + if (false_alarms > min_false_alarms) { + + /* increase auto_corr values to decrease sensitivity + * so the DSP won't be disturbed by the noise + */ + if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) + data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; + else { + val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + min((u32)ranges->auto_corr_max_cck, val); + } + val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + min((u32)ranges->auto_corr_max_cck_mrc, val); + } else if ((false_alarms < min_false_alarms) && + ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || + (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { + + /* Decrease auto_corr values to increase sensitivity */ + val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + max((u32)ranges->auto_corr_min_cck, val); + val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + max((u32)ranges->auto_corr_min_cck_mrc, val); + } + + return 0; +} + + +static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, + u32 norm_fa, + u32 rx_enable_time) +{ + u32 val; + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; + u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + data = &(priv->sensitivity_data); + + /* If we got too many false alarms this time, reduce sensitivity */ + if (false_alarms > max_false_alarms) { + + IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", + false_alarms, max_false_alarms); + + val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + min((u32)ranges->auto_corr_max_ofdm, val); + + val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + min((u32)ranges->auto_corr_max_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + min((u32)ranges->auto_corr_max_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); + } + + /* Else if we got fewer than desired, increase sensitivity */ + else if (false_alarms < min_false_alarms) { + + IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", + false_alarms, min_false_alarms); + + val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + max((u32)ranges->auto_corr_min_ofdm, val); + + val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + max((u32)ranges->auto_corr_min_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + max((u32)ranges->auto_corr_min_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); + } else { + IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", + min_false_alarms, false_alarms, max_false_alarms); + } + return 0; +} + +static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, + struct iwl_sensitivity_data *data, + __le16 *tbl) +{ + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm); + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_mrc); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_x1); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); + + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_cck); + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_cck_mrc); + + tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = + cpu_to_le16((u16)data->nrg_th_cck); + tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = + cpu_to_le16((u16)data->nrg_th_ofdm); + + tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = + cpu_to_le16(data->barker_corr_th_min); + tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16(data->barker_corr_th_min_mrc); + tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = + cpu_to_le16(data->nrg_th_cca); + + IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", + data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, + data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, + data->nrg_th_ofdm); + + IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", + data->auto_corr_cck, data->auto_corr_cck_mrc, + data->nrg_th_cck); +} + +/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ +static int iwl_sensitivity_write(struct iwl_priv *priv) +{ + struct iwl_sensitivity_cmd cmd; + struct iwl_sensitivity_data *data = NULL; + struct iwl_host_cmd cmd_out = { + .id = SENSITIVITY_CMD, + .len = { sizeof(struct iwl_sensitivity_cmd), }, + .flags = CMD_ASYNC, + .data = { &cmd, }, + }; + + data = &(priv->sensitivity_data); + + memset(&cmd, 0, sizeof(cmd)); + + iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); + + /* Update uCode's "work" table, and copy it to DSP */ + cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; + + /* Don't send command to uCode if nothing has changed */ + if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), + sizeof(u16)*HD_TABLE_SIZE)) { + IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); + return 0; + } + + /* Copy table for comparison next time */ + memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), + sizeof(u16)*HD_TABLE_SIZE); + + return iwl_dvm_send_cmd(priv, &cmd_out); +} + +/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ +static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) +{ + struct iwl_enhance_sensitivity_cmd cmd; + struct iwl_sensitivity_data *data = NULL; + struct iwl_host_cmd cmd_out = { + .id = SENSITIVITY_CMD, + .len = { sizeof(struct iwl_enhance_sensitivity_cmd), }, + .flags = CMD_ASYNC, + .data = { &cmd, }, + }; + + data = &(priv->sensitivity_data); + + memset(&cmd, 0, sizeof(cmd)); + + iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); + + if (priv->cfg->base_params->hd_v2) { + cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = + HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; + cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = + HD_INA_NON_SQUARE_DET_CCK_DATA_V2; + cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = + HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2; + } else { + cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = + HD_INA_NON_SQUARE_DET_OFDM_DATA_V1; + cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = + HD_INA_NON_SQUARE_DET_CCK_DATA_V1; + cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = + HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = + HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1; + cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = + HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1; + cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = + HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1; + } + + /* Update uCode's "work" table, and copy it to DSP */ + cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; + + /* Don't send command to uCode if nothing has changed */ + if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]), + sizeof(u16)*HD_TABLE_SIZE) && + !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX], + &(priv->enhance_sensitivity_tbl[0]), + sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) { + IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); + return 0; + } + + /* Copy table for comparison next time */ + memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]), + sizeof(u16)*HD_TABLE_SIZE); + memcpy(&(priv->enhance_sensitivity_tbl[0]), + &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]), + sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES); + + return iwl_dvm_send_cmd(priv, &cmd_out); +} + +void iwl_init_sensitivity(struct iwl_priv *priv) +{ + int ret = 0; + int i; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) + return; + + IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n"); + + /* Clear driver's sensitivity algo data */ + data = &(priv->sensitivity_data); + + if (ranges == NULL) + return; + + memset(data, 0, sizeof(struct iwl_sensitivity_data)); + + data->num_in_cck_no_fa = 0; + data->nrg_curr_state = IWL_FA_TOO_MANY; + data->nrg_prev_state = IWL_FA_TOO_MANY; + data->nrg_silence_ref = 0; + data->nrg_silence_idx = 0; + data->nrg_energy_idx = 0; + + for (i = 0; i < 10; i++) + data->nrg_value[i] = 0; + + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) + data->nrg_silence_rssi[i] = 0; + + data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; + data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; + data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; + data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; + data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; + data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; + data->nrg_th_cck = ranges->nrg_th_cck; + data->nrg_th_ofdm = ranges->nrg_th_ofdm; + data->barker_corr_th_min = ranges->barker_corr_th_min; + data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; + data->nrg_th_cca = ranges->nrg_th_cca; + + data->last_bad_plcp_cnt_ofdm = 0; + data->last_fa_cnt_ofdm = 0; + data->last_bad_plcp_cnt_cck = 0; + data->last_fa_cnt_cck = 0; + + if (priv->fw->enhance_sensitivity_table) + ret |= iwl_enhance_sensitivity_write(priv); + else + ret |= iwl_sensitivity_write(priv); + IWL_DEBUG_CALIB(priv, "<calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) + return; + + data = &(priv->sensitivity_data); + + if (!iwl_is_any_associated(priv)) { + IWL_DEBUG_CALIB(priv, "<< - not associated\n"); + return; + } + + spin_lock_bh(&priv->statistics.lock); + rx_info = &priv->statistics.rx_non_phy; + ofdm = &priv->statistics.rx_ofdm; + cck = &priv->statistics.rx_cck; + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); + spin_unlock_bh(&priv->statistics.lock); + return; + } + + /* Extract Statistics: */ + rx_enable_time = le32_to_cpu(rx_info->channel_load); + fa_cck = le32_to_cpu(cck->false_alarm_cnt); + fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); + bad_plcp_cck = le32_to_cpu(cck->plcp_err); + bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); + + statis.beacon_silence_rssi_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a); + statis.beacon_silence_rssi_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b); + statis.beacon_silence_rssi_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c); + statis.beacon_energy_a = + le32_to_cpu(rx_info->beacon_energy_a); + statis.beacon_energy_b = + le32_to_cpu(rx_info->beacon_energy_b); + statis.beacon_energy_c = + le32_to_cpu(rx_info->beacon_energy_c); + + spin_unlock_bh(&priv->statistics.lock); + + IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); + + if (!rx_enable_time) { + IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); + return; + } + + /* These statistics increase monotonically, and do not reset + * at each beacon. Calculate difference from last value, or just + * use the new statistics value if it has reset or wrapped around. */ + if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) + data->last_bad_plcp_cnt_cck = bad_plcp_cck; + else { + bad_plcp_cck -= data->last_bad_plcp_cnt_cck; + data->last_bad_plcp_cnt_cck += bad_plcp_cck; + } + + if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) + data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; + else { + bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; + data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; + } + + if (data->last_fa_cnt_ofdm > fa_ofdm) + data->last_fa_cnt_ofdm = fa_ofdm; + else { + fa_ofdm -= data->last_fa_cnt_ofdm; + data->last_fa_cnt_ofdm += fa_ofdm; + } + + if (data->last_fa_cnt_cck > fa_cck) + data->last_fa_cnt_cck = fa_cck; + else { + fa_cck -= data->last_fa_cnt_cck; + data->last_fa_cnt_cck += fa_cck; + } + + /* Total aborted signal locks */ + norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; + norm_fa_cck = fa_cck + bad_plcp_cck; + + IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, + bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); + + iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); + iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); + if (priv->fw->enhance_sensitivity_table) + iwl_enhance_sensitivity_write(priv); + else + iwl_sensitivity_write(priv); +} + +static inline u8 find_first_chain(u8 mask) +{ + if (mask & ANT_A) + return CHAIN_A; + if (mask & ANT_B) + return CHAIN_B; + return CHAIN_C; +} + +/** + * Run disconnected antenna algorithm to find out which antennas are + * disconnected. + */ +static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, + struct iwl_chain_noise_data *data) +{ + u32 active_chains = 0; + u32 max_average_sig; + u16 max_average_sig_antenna_i; + u8 num_tx_chains; + u8 first_chain; + u16 i = 0; + + average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; + average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; + average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; + + if (average_sig[0] >= average_sig[1]) { + max_average_sig = average_sig[0]; + max_average_sig_antenna_i = 0; + active_chains = (1 << max_average_sig_antenna_i); + } else { + max_average_sig = average_sig[1]; + max_average_sig_antenna_i = 1; + active_chains = (1 << max_average_sig_antenna_i); + } + + if (average_sig[2] >= max_average_sig) { + max_average_sig = average_sig[2]; + max_average_sig_antenna_i = 2; + active_chains = (1 << max_average_sig_antenna_i); + } + + IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", + average_sig[0], average_sig[1], average_sig[2]); + IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", + max_average_sig, max_average_sig_antenna_i); + + /* Compare signal strengths for all 3 receivers. */ + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (i != max_average_sig_antenna_i) { + s32 rssi_delta = (max_average_sig - average_sig[i]); + + /* If signal is very weak, compared with + * strongest, mark it as disconnected. */ + if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) + data->disconn_array[i] = 1; + else + active_chains |= (1 << i); + IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " + "disconn_array[i] = %d\n", + i, rssi_delta, data->disconn_array[i]); + } + } + + /* + * The above algorithm sometimes fails when the ucode + * reports 0 for all chains. It's not clear why that + * happens to start with, but it is then causing trouble + * because this can make us enable more chains than the + * hardware really has. + * + * To be safe, simply mask out any chains that we know + * are not on the device. + */ + active_chains &= priv->hw_params.valid_rx_ant; + + num_tx_chains = 0; + for (i = 0; i < NUM_RX_CHAINS; i++) { + /* loops on all the bits of + * priv->hw_setting.valid_tx_ant */ + u8 ant_msk = (1 << i); + if (!(priv->hw_params.valid_tx_ant & ant_msk)) + continue; + + num_tx_chains++; + if (data->disconn_array[i] == 0) + /* there is a Tx antenna connected */ + break; + if (num_tx_chains == priv->hw_params.tx_chains_num && + data->disconn_array[i]) { + /* + * If all chains are disconnected + * connect the first valid tx chain + */ + first_chain = + find_first_chain(priv->hw_params.valid_tx_ant); + data->disconn_array[first_chain] = 0; + active_chains |= BIT(first_chain); + IWL_DEBUG_CALIB(priv, + "All Tx chains are disconnected W/A - declare %d as connected\n", + first_chain); + break; + } + } + + if (active_chains != priv->hw_params.valid_rx_ant && + active_chains != priv->chain_noise_data.active_chains) + IWL_DEBUG_CALIB(priv, + "Detected that not all antennas are connected! " + "Connected: %#x, valid: %#x.\n", + active_chains, + priv->hw_params.valid_rx_ant); + + /* Save for use within RXON, TX, SCAN commands, etc. */ + data->active_chains = active_chains; + IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", + active_chains); +} + +static void iwlagn_gain_computation(struct iwl_priv *priv, + u32 average_noise[NUM_RX_CHAINS], + u8 default_chain) +{ + int i; + s32 delta_g; + struct iwl_chain_noise_data *data = &priv->chain_noise_data; + + /* + * Find Gain Code for the chains based on "default chain" + */ + for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { + if ((data->disconn_array[i])) { + data->delta_gain_code[i] = 0; + continue; + } + + delta_g = (priv->cfg->base_params->chain_noise_scale * + ((s32)average_noise[default_chain] - + (s32)average_noise[i])) / 1500; + + /* bound gain by 2 bits value max, 3rd bit is sign */ + data->delta_gain_code[i] = + min(abs(delta_g), + (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); + + if (delta_g < 0) + /* + * set negative sign ... + * note to Intel developers: This is uCode API format, + * not the format of any internal device registers. + * Do not change this format for e.g. 6050 or similar + * devices. Change format only if more resolution + * (i.e. more than 2 bits magnitude) is needed. + */ + data->delta_gain_code[i] |= (1 << 2); + } + + IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n", + data->delta_gain_code[1], data->delta_gain_code[2]); + + if (!data->radio_write) { + struct iwl_calib_chain_noise_gain_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + iwl_set_calib_hdr(&cmd.hdr, + priv->phy_calib_chain_noise_gain_cmd); + cmd.delta_gain_1 = data->delta_gain_code[1]; + cmd.delta_gain_2 = data->delta_gain_code[2]; + iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, + CMD_ASYNC, sizeof(cmd), &cmd); + + data->radio_write = 1; + data->state = IWL_CHAIN_NOISE_CALIBRATED; + } +} + +/* + * Accumulate 16 beacons of signal and noise statistics for each of + * 3 receivers/antennas/rx-chains, then figure out: + * 1) Which antennas are connected. + * 2) Differential rx gain settings to balance the 3 receivers. + */ +void iwl_chain_noise_calibration(struct iwl_priv *priv) +{ + struct iwl_chain_noise_data *data = NULL; + + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_sig_a; + u32 chain_sig_b; + u32 chain_sig_c; + u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; + u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; + u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; + u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; + u16 i = 0; + u16 rxon_chnum = INITIALIZATION_VALUE; + u16 stat_chnum = INITIALIZATION_VALUE; + u8 rxon_band24; + u8 stat_band24; + struct statistics_rx_non_phy *rx_info; + + /* + * MULTI-FIXME: + * When we support multiple interfaces on different channels, + * this must be modified/fixed. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) + return; + + data = &(priv->chain_noise_data); + + /* + * Accumulate just the first "chain_noise_num_beacons" after + * the first association, then we're done forever. + */ + if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { + if (data->state == IWL_CHAIN_NOISE_ALIVE) + IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); + return; + } + + spin_lock_bh(&priv->statistics.lock); + + rx_info = &priv->statistics.rx_non_phy; + + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); + spin_unlock_bh(&priv->statistics.lock); + return; + } + + rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); + rxon_chnum = le16_to_cpu(ctx->staging.channel); + stat_band24 = + !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK); + stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16; + + /* Make sure we accumulate data for just the associated channel + * (even if scanning). */ + if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { + IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", + rxon_chnum, rxon_band24); + spin_unlock_bh(&priv->statistics.lock); + return; + } + + /* + * Accumulate beacon statistics values across + * "chain_noise_num_beacons" + */ + chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & + IN_BAND_FILTER; + chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & + IN_BAND_FILTER; + chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & + IN_BAND_FILTER; + + chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; + chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; + chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; + + spin_unlock_bh(&priv->statistics.lock); + + data->beacon_count++; + + data->chain_noise_a = (chain_noise_a + data->chain_noise_a); + data->chain_noise_b = (chain_noise_b + data->chain_noise_b); + data->chain_noise_c = (chain_noise_c + data->chain_noise_c); + + data->chain_signal_a = (chain_sig_a + data->chain_signal_a); + data->chain_signal_b = (chain_sig_b + data->chain_signal_b); + data->chain_signal_c = (chain_sig_c + data->chain_signal_c); + + IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", + rxon_chnum, rxon_band24, data->beacon_count); + IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", + chain_sig_a, chain_sig_b, chain_sig_c); + IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", + chain_noise_a, chain_noise_b, chain_noise_c); + + /* If this is the "chain_noise_num_beacons", determine: + * 1) Disconnected antennas (using signal strengths) + * 2) Differential gain (using silence noise) to balance receivers */ + if (data->beacon_count != IWL_CAL_NUM_BEACONS) + return; + + /* Analyze signal for disconnected antenna */ + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + /* Disable disconnected antenna algorithm for advanced + bt coex, assuming valid antennas are connected */ + data->active_chains = priv->hw_params.valid_rx_ant; + for (i = 0; i < NUM_RX_CHAINS; i++) + if (!(data->active_chains & (1<disconn_array[i] = 1; + } else + iwl_find_disconn_antenna(priv, average_sig, data); + + /* Analyze noise for rx balance */ + average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; + average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; + average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; + + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (!(data->disconn_array[i]) && + (average_noise[i] <= min_average_noise)) { + /* This means that chain i is active and has + * lower noise values so far: */ + min_average_noise = average_noise[i]; + min_average_noise_antenna_i = i; + } + } + + IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", + average_noise[0], average_noise[1], + average_noise[2]); + + IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", + min_average_noise, min_average_noise_antenna_i); + + iwlagn_gain_computation(priv, average_noise, + find_first_chain(priv->hw_params.valid_rx_ant)); + + /* Some power changes may have been made during the calibration. + * Update and commit the RXON + */ + iwl_update_chain_flags(priv); + + data->state = IWL_CHAIN_NOISE_DONE; + iwl_power_update_mode(priv, false); +} + +void iwl_reset_run_time_calib(struct iwl_priv *priv) +{ + int i; + memset(&(priv->sensitivity_data), 0, + sizeof(struct iwl_sensitivity_data)); + memset(&(priv->chain_noise_data), 0, + sizeof(struct iwl_chain_noise_data)); + for (i = 0; i < NUM_RX_CHAINS; i++) + priv->chain_noise_data.delta_gain_code[i] = + CHAIN_NOISE_DELTA_GAIN_INIT_VAL; + + /* Ask for statistics now, the uCode will send notification + * periodically after association */ + iwl_send_statistics_request(priv, CMD_ASYNC, true); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/iwlwifi/dvm/calib.h new file mode 100644 index 000000000000..2349f393cc42 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/calib.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifndef __iwl_calib_h__ +#define __iwl_calib_h__ + +#include "dev.h" +#include "commands.h" + +void iwl_chain_noise_calibration(struct iwl_priv *priv); +void iwl_sensitivity_calibration(struct iwl_priv *priv); + +void iwl_init_sensitivity(struct iwl_priv *priv); +void iwl_reset_run_time_calib(struct iwl_priv *priv); + +#endif /* __iwl_calib_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h new file mode 100644 index 000000000000..64811cd91635 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -0,0 +1,3958 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +/* + * Please use this file (commands.h) only for uCode API definitions. + * Please use iwl-xxxx-hw.h for hardware-related definitions. + * Please use dev.h for driver implementation definitions. + */ + +#ifndef __iwl_commands_h__ +#define __iwl_commands_h__ + +#include +#include + + +enum { + REPLY_ALIVE = 0x1, + REPLY_ERROR = 0x2, + REPLY_ECHO = 0x3, /* test command */ + + /* RXON and QOS commands */ + REPLY_RXON = 0x10, + REPLY_RXON_ASSOC = 0x11, + REPLY_QOS_PARAM = 0x13, + REPLY_RXON_TIMING = 0x14, + + /* Multi-Station support */ + REPLY_ADD_STA = 0x18, + REPLY_REMOVE_STA = 0x19, + REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ + REPLY_TXFIFO_FLUSH = 0x1e, + + /* Security */ + REPLY_WEPKEY = 0x20, + + /* RX, TX, LEDs */ + REPLY_TX = 0x1c, + REPLY_LEDS_CMD = 0x48, + REPLY_TX_LINK_QUALITY_CMD = 0x4e, + + /* WiMAX coexistence */ + COEX_PRIORITY_TABLE_CMD = 0x5a, + COEX_MEDIUM_NOTIFICATION = 0x5b, + COEX_EVENT_CMD = 0x5c, + + /* Calibration */ + TEMPERATURE_NOTIFICATION = 0x62, + CALIBRATION_CFG_CMD = 0x65, + CALIBRATION_RES_NOTIFICATION = 0x66, + CALIBRATION_COMPLETE_NOTIFICATION = 0x67, + + /* 802.11h related */ + REPLY_QUIET_CMD = 0x71, /* not used */ + REPLY_CHANNEL_SWITCH = 0x72, + CHANNEL_SWITCH_NOTIFICATION = 0x73, + REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74, + SPECTRUM_MEASURE_NOTIFICATION = 0x75, + + /* Power Management */ + POWER_TABLE_CMD = 0x77, + PM_SLEEP_NOTIFICATION = 0x7A, + PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, + + /* Scan commands and notifications */ + REPLY_SCAN_CMD = 0x80, + REPLY_SCAN_ABORT_CMD = 0x81, + SCAN_START_NOTIFICATION = 0x82, + SCAN_RESULTS_NOTIFICATION = 0x83, + SCAN_COMPLETE_NOTIFICATION = 0x84, + + /* IBSS/AP commands */ + BEACON_NOTIFICATION = 0x90, + REPLY_TX_BEACON = 0x91, + WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */ + + /* Miscellaneous commands */ + REPLY_TX_POWER_DBM_CMD = 0x95, + QUIET_NOTIFICATION = 0x96, /* not used */ + REPLY_TX_PWR_TABLE_CMD = 0x97, + REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */ + TX_ANT_CONFIGURATION_CMD = 0x98, + MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */ + + /* Bluetooth device coexistence config command */ + REPLY_BT_CONFIG = 0x9b, + + /* Statistics */ + REPLY_STATISTICS_CMD = 0x9c, + STATISTICS_NOTIFICATION = 0x9d, + + /* RF-KILL commands and notifications */ + REPLY_CARD_STATE_CMD = 0xa0, + CARD_STATE_NOTIFICATION = 0xa1, + + /* Missed beacons notification */ + MISSED_BEACONS_NOTIFICATION = 0xa2, + + REPLY_CT_KILL_CONFIG_CMD = 0xa4, + SENSITIVITY_CMD = 0xa8, + REPLY_PHY_CALIBRATION_CMD = 0xb0, + REPLY_RX_PHY_CMD = 0xc0, + REPLY_RX_MPDU_CMD = 0xc1, + REPLY_RX = 0xc3, + REPLY_COMPRESSED_BA = 0xc5, + + /* BT Coex */ + REPLY_BT_COEX_PRIO_TABLE = 0xcc, + REPLY_BT_COEX_PROT_ENV = 0xcd, + REPLY_BT_COEX_PROFILE_NOTIF = 0xce, + + /* PAN commands */ + REPLY_WIPAN_PARAMS = 0xb2, + REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */ + REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */ + REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */ + REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */ + REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */ + REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9, + REPLY_WIPAN_NOA_NOTIFICATION = 0xbc, + REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd, + + REPLY_WOWLAN_PATTERNS = 0xe0, + REPLY_WOWLAN_WAKEUP_FILTER = 0xe1, + REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2, + REPLY_WOWLAN_TKIP_PARAMS = 0xe3, + REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, + REPLY_WOWLAN_GET_STATUS = 0xe5, + REPLY_D3_CONFIG = 0xd3, + + REPLY_MAX = 0xff +}; + +/****************************************************************************** + * (0) + * Commonly used structures and definitions: + * Command header, rate_n_flags, txpower + * + *****************************************************************************/ + +/** + * iwlagn rate_n_flags bit fields + * + * rate_n_flags format is used in following iwlagn commands: + * REPLY_RX (response only) + * REPLY_RX_MPDU (response only) + * REPLY_TX (both command and response) + * REPLY_TX_LINK_QUALITY_CMD + * + * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): + * 2-0: 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * + * 4-3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + * + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data + * + * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * + * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): + * 6-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + */ +#define RATE_MCS_CODE_MSK 0x7 +#define RATE_MCS_SPATIAL_POS 3 +#define RATE_MCS_SPATIAL_MSK 0x18 +#define RATE_MCS_HT_DUP_POS 5 +#define RATE_MCS_HT_DUP_MSK 0x20 +/* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */ +#define RATE_MCS_RATE_MSK 0xff + +/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ +#define RATE_MCS_FLAGS_POS 8 +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK 0x100 + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK 0x200 + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_MCS_GF_POS 10 +#define RATE_MCS_GF_MSK 0x400 + +/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_HT40_POS 11 +#define RATE_MCS_HT40_MSK 0x800 + +/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ +#define RATE_MCS_DUP_POS 12 +#define RATE_MCS_DUP_MSK 0x1000 + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK 0x2000 + +/** + * rate_n_flags Tx antenna masks + * 4965 has 2 transmitters + * 5100 has 1 transmitter B + * 5150 has 1 transmitter A + * 5300 has 3 transmitters + * 5350 has 3 transmitters + * bit14:16 + */ +#define RATE_MCS_ANT_POS 14 +#define RATE_MCS_ANT_A_MSK 0x04000 +#define RATE_MCS_ANT_B_MSK 0x08000 +#define RATE_MCS_ANT_C_MSK 0x10000 +#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) +#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) +#define RATE_ANT_NUM 3 + +#define POWER_TABLE_NUM_ENTRIES 33 +#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 +#define POWER_TABLE_CCK_ENTRY 32 + +#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 +#define IWL_PWR_CCK_ENTRIES 2 + +/** + * struct tx_power_dual_stream + * + * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + * + * Same format as iwl_tx_power_dual_stream, but __le32 + */ +struct tx_power_dual_stream { + __le32 dw; +} __packed; + +/** + * Command REPLY_TX_POWER_DBM_CMD = 0x98 + * struct iwlagn_tx_power_dbm_cmd + */ +#define IWLAGN_TX_POWER_AUTO 0x7f +#define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6) + +struct iwlagn_tx_power_dbm_cmd { + s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ + u8 flags; + s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ + u8 reserved; +} __packed; + +/** + * Command TX_ANT_CONFIGURATION_CMD = 0x98 + * This command is used to configure valid Tx antenna. + * By default uCode concludes the valid antenna according to the radio flavor. + * This command enables the driver to override/modify this conclusion. + */ +struct iwl_tx_ant_config_cmd { + __le32 valid; +} __packed; + +/****************************************************************************** + * (0a) + * Alive and Error Commands & Responses: + * + *****************************************************************************/ + +#define UCODE_VALID_OK cpu_to_le32(0x1) + +/** + * REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "alive" notification once the runtime image is ready + * to receive commands from the driver. This is the *second* "alive" + * notification that the driver will receive after rebooting uCode; + * this "alive" is indicated by subtype field != 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * This response includes two pointers to structures within the device's + * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: + * + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * Its header format is: + * + * __le32 log_size; log capacity (in number of entries) + * __le32 type; (1) timestamp with each entry, (0) no timestamp + * __le32 wraps; # times uCode has wrapped to top of circular buffer + * __le32 write_index; next circular buffer entry that uCode would fill + * + * The header is followed by the circular buffer of log entries. Entries + * with timestamps have the following format: + * + * __le32 event_id; range 0 - 1500 + * __le32 timestamp; low 32 bits of TSF (of network, if associated) + * __le32 data; event_id-specific data value + * + * Entries without timestamps contain only event_id and data. + * + * + * 2) error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. For agn, the format + * of the error log is defined by struct iwl_error_event_table. + * + * The Linux driver can print both logs to the system log when a uCode error + * occurs. + */ + +/* + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwl_error_event_table { + u32 valid; /* (nonzero) valid, (0) log is empty */ + u32 error_id; /* type of error */ + u32 pc; /* program counter */ + u32 blink1; /* branch link */ + u32 blink2; /* branch link */ + u32 ilink1; /* interrupt link */ + u32 ilink2; /* interrupt link */ + u32 data1; /* error-specific data */ + u32 data2; /* error-specific data */ + u32 line; /* source code line of error */ + u32 bcon_time; /* beacon timer */ + u32 tsf_low; /* network timestamp function timer */ + u32 tsf_hi; /* network timestamp function timer */ + u32 gp1; /* GP1 timer register */ + u32 gp2; /* GP2 timer register */ + u32 gp3; /* GP3 timer register */ + u32 ucode_ver; /* uCode version */ + u32 hw_ver; /* HW Silicon version */ + u32 brd_ver; /* HW board version */ + u32 log_pc; /* log program counter */ + u32 frame_ptr; /* frame pointer */ + u32 stack_ptr; /* stack pointer */ + u32 hcmd; /* last host command header */ + u32 isr0; /* isr status register LMPM_NIC_ISR0: + * rxtx_flag */ + u32 isr1; /* isr status register LMPM_NIC_ISR1: + * host_flag */ + u32 isr2; /* isr status register LMPM_NIC_ISR2: + * enc_flag */ + u32 isr3; /* isr status register LMPM_NIC_ISR3: + * time_flag */ + u32 isr4; /* isr status register LMPM_NIC_ISR4: + * wico interrupt */ + u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + u32 wait_event; /* wait event() caller address */ + u32 l2p_control; /* L2pControlField */ + u32 l2p_duration; /* L2pDurationField */ + u32 l2p_mhvalid; /* L2pMhValidBits */ + u32 l2p_addr_match; /* L2pAddrMatchStat */ + u32 lmpm_pmg_sel; /* indicate which clocks are turned on + * (LMPM_PMG_SEL) */ + u32 u_timestamp; /* indicate when the date and time of the + * compilation */ + u32 flow_handler; /* FH read/write pointers, RX credit */ +} __packed; + +struct iwl_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* not "9" for runtime alive */ + __le16 reserved2; + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 timestamp; + __le32 is_valid; +} __packed; + +/* + * REPLY_ERROR = 0x2 (response only, not a command) + */ +struct iwl_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le32 error_info; + __le64 timestamp; +} __packed; + +/****************************************************************************** + * (1) + * RXON Commands & Responses: + * + *****************************************************************************/ + +/* + * Rx config defines & structure + */ +/* rx_config device types */ +enum { + RXON_DEV_TYPE_AP = 1, + RXON_DEV_TYPE_ESS = 3, + RXON_DEV_TYPE_IBSS = 4, + RXON_DEV_TYPE_SNIFFER = 6, + RXON_DEV_TYPE_CP = 7, + RXON_DEV_TYPE_2STA = 8, + RXON_DEV_TYPE_P2P = 9, +}; + + +#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) +#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) +#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) +#define RXON_RX_CHAIN_VALID_POS (1) +#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) +#define RXON_RX_CHAIN_FORCE_SEL_POS (4) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) +#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) +#define RXON_RX_CHAIN_CNT_POS (10) +#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) +#define RXON_RX_CHAIN_MIMO_CNT_POS (12) +#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) +#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) + +/* rx_config flags */ +/* band & modulation selection */ +#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) +#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) +/* auto detection enable */ +#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) +/* TGg protection when tx */ +#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) +/* cck short slot & preamble */ +#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) +#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) +/* antenna selection */ +#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) +#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) +#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) +#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) +/* radar detection enable */ +#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) +#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) +/* rx response to host with 8-byte TSF +* (according to ON_AIR deassertion) */ +#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) + + +/* HT flags */ +#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) +#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) + +#define RXON_FLG_HT_OPERATING_MODE_POS (23) + +#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) +#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) + +#define RXON_FLG_CHANNEL_MODE_POS (25) +#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) + +/* channel mode */ +enum { + CHANNEL_MODE_LEGACY = 0, + CHANNEL_MODE_PURE_40 = 1, + CHANNEL_MODE_MIXED = 2, + CHANNEL_MODE_RESERVED = 3, +}; +#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) + +/* CTS to self (if spec allows) flag */ +#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) + +/* rx_config filter flags */ +/* accept all data frames */ +#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) +/* pass control & management to host */ +#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) +/* accept multi-cast */ +#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) +/* don't decrypt uni-cast frames */ +#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) +/* don't decrypt multi-cast frames */ +#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) +/* STA is associated */ +#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) +/* transfer to host non bssid beacons in associated state */ +#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) + +/** + * REPLY_RXON = 0x10 (command, has simple generic response) + * + * RXON tunes the radio tuner to a service channel, and sets up a number + * of parameters that are used primarily for Rx, but also for Tx operations. + * + * NOTE: When tuning to a new channel, driver must set the + * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent + * info within the device, including the station tables, tx retry + * rate tables, and txpower tables. Driver must build a new station + * table and txpower table before transmitting anything on the RXON + * channel. + * + * NOTE: All RXONs wipe clean the internal txpower table. Driver must + * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), + * regardless of whether RXON_FILTER_ASSOC_MSK is set. + */ + +struct iwl_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + u8 ofdm_ht_triple_stream_basic_rates; + u8 reserved5; + __le16 acquisition_data; + __le16 reserved6; +} __packed; + +/* + * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) + */ +struct iwl_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 reserved1; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + u8 ofdm_ht_triple_stream_basic_rates; + u8 reserved2; + __le16 rx_chain_select_flags; + __le16 acquisition_data; + __le32 reserved3; +} __packed; + +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 +#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ + +/* + * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) + */ +struct iwl_rxon_time_cmd { + __le64 timestamp; + __le16 beacon_interval; + __le16 atim_window; + __le32 beacon_init_val; + __le16 listen_interval; + u8 dtim_period; + u8 delta_cp_bss_tbtts; +} __packed; + +/* + * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) + */ +/** + * struct iwl5000_channel_switch_cmd + * @band: 0- 5.2GHz, 1- 2.4GHz + * @expect_beacon: 0- resume transmits after channel switch + * 1- wait for beacon to resume transmits + * @channel: new channel number + * @rxon_flags: Rx on flags + * @rxon_filter_flags: filtering parameters + * @switch_time: switch time in extended beacon format + * @reserved: reserved bytes + */ +struct iwl5000_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; +} __packed; + +/** + * struct iwl6000_channel_switch_cmd + * @band: 0- 5.2GHz, 1- 2.4GHz + * @expect_beacon: 0- resume transmits after channel switch + * 1- wait for beacon to resume transmits + * @channel: new channel number + * @rxon_flags: Rx on flags + * @rxon_filter_flags: filtering parameters + * @switch_time: switch time in extended beacon format + * @reserved: reserved bytes + */ +struct iwl6000_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; +} __packed; + +/* + * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) + */ +struct iwl_csa_notification { + __le16 band; + __le16 channel; + __le32 status; /* 0 - OK, 1 - fail */ +} __packed; + +/****************************************************************************** + * (2) + * Quality-of-Service (QOS) Commands & Responses: + * + *****************************************************************************/ + +/** + * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM + * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd + * + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl_ac_qos { + __le16 cw_min; + __le16 cw_max; + u8 aifsn; + u8 reserved1; + __le16 edca_txop; +} __packed; + +/* QoS flags defines */ +#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) +#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) +#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) + +/* Number of Access Categories (AC) (EDCA), queues 0..3 */ +#define AC_NUM 4 + +/* + * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) + * + * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs + * 0: Background, 1: Best Effort, 2: Video, 3: Voice. + */ +struct iwl_qosparam_cmd { + __le32 qos_flags; + struct iwl_ac_qos ac[AC_NUM]; +} __packed; + +/****************************************************************************** + * (3) + * Add/Modify Stations Commands & Responses: + * + *****************************************************************************/ +/* + * Multi station support + */ + +/* Special, dedicated locations within device's station table */ +#define IWL_AP_ID 0 +#define IWL_AP_ID_PAN 1 +#define IWL_STA_ID 2 +#define IWLAGN_PAN_BCAST_ID 14 +#define IWLAGN_BROADCAST_ID 15 +#define IWLAGN_STATION_COUNT 16 + +#define IWL_INVALID_STATION 255 +#define IWL_MAX_TID_COUNT 8 +#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT + +#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) +#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) +#define STA_FLG_PAN_STATION cpu_to_le32(1 << 13) +#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) +#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) +#define STA_FLG_MAX_AGG_SIZE_POS (19) +#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) +#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) +#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) +#define STA_FLG_AGG_MPDU_DENSITY_POS (23) +#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) + +/* Use in mode field. 1: modify existing entry, 0: add new station entry */ +#define STA_CONTROL_MODIFY_MSK 0x01 + +/* key flags __le16*/ +#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) +#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) +#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) +#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) +#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) + +#define STA_KEY_FLG_KEYID_POS 8 +#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) +/* wep key is either from global key (0) or from station info array (1) */ +#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) + +/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ +#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) +#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) +#define STA_KEY_MAX_NUM 8 +#define STA_KEY_MAX_NUM_PAN 16 +/* must not match WEP_INVALID_OFFSET */ +#define IWLAGN_HW_KEY_DEFAULT 0xfe + +/* Flags indicate whether to modify vs. don't change various station params */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define STA_MODIFY_TX_RATE_MSK 0x04 +#define STA_MODIFY_ADDBA_TID_MSK 0x08 +#define STA_MODIFY_DELBA_TID_MSK 0x10 +#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 + +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + +/* agn */ +struct iwl_keyinfo { + __le16 key_flags; + u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ + u8 reserved1; + __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ + u8 key_offset; + u8 reserved2; + u8 key[16]; /* 16-byte unicast decryption key */ + __le64 tx_secur_seq_cnt; + __le64 hw_tkip_mic_rx_key; + __le64 hw_tkip_mic_tx_key; +} __packed; + +/** + * struct sta_id_modify + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change + * + * Driver selects unused table index when adding new station, + * or the index to a pre-existing station entry when modifying that station. + * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). + * + * modify_mask flags select which parameters to modify vs. leave alone. + */ +struct sta_id_modify { + u8 addr[ETH_ALEN]; + __le16 reserved1; + u8 sta_id; + u8 modify_mask; + __le16 reserved2; +} __packed; + +/* + * REPLY_ADD_STA = 0x18 (command) + * + * The device contains an internal table of per-station information, + * with info on security keys, aggregation parameters, and Tx rates for + * initial Tx attempt and any retries (agn devices uses + * REPLY_TX_LINK_QUALITY_CMD, + * + * REPLY_ADD_STA sets up the table entry for one station, either creating + * a new entry, or modifying a pre-existing one. + * + * NOTE: RXON command (without "associated" bit set) wipes the station table + * clean. Moving into RF_KILL state does this also. Driver must set up + * new station table before transmitting anything on the RXON channel + * (except active scans or active measurements; those commands carry + * their own txpower/rate setup data). + * + * When getting started on a new channel, driver must set up the + * IWL_BROADCAST_ID entry (last entry in the table). For a client + * station in a BSS, once an AP is selected, driver sets up the AP STA + * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP + * are all that are needed for a BSS client station. If the device is + * used as AP, or in an IBSS network, driver must set up station table + * entries for all STAs in network, starting with index IWL_STA_ID. + */ + +struct iwl_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + __le16 legacy_reserved; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; +} __packed; + + +#define ADD_STA_SUCCESS_MSK 0x1 +#define ADD_STA_NO_ROOM_IN_TABLE 0x2 +#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 +#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 +/* + * REPLY_ADD_STA = 0x18 (response) + */ +struct iwl_add_sta_resp { + u8 status; /* ADD_STA_* */ +} __packed; + +#define REM_STA_SUCCESS_MSK 0x1 +/* + * REPLY_REM_STA = 0x19 (response) + */ +struct iwl_rem_sta_resp { + u8 status; +} __packed; + +/* + * REPLY_REM_STA = 0x19 (command) + */ +struct iwl_rem_sta_cmd { + u8 num_sta; /* number of removed stations */ + u8 reserved[3]; + u8 addr[ETH_ALEN]; /* MAC addr of the first station */ + u8 reserved2[2]; +} __packed; + + +/* WiFi queues mask */ +#define IWL_SCD_BK_MSK cpu_to_le32(BIT(0)) +#define IWL_SCD_BE_MSK cpu_to_le32(BIT(1)) +#define IWL_SCD_VI_MSK cpu_to_le32(BIT(2)) +#define IWL_SCD_VO_MSK cpu_to_le32(BIT(3)) +#define IWL_SCD_MGMT_MSK cpu_to_le32(BIT(3)) + +/* PAN queues mask */ +#define IWL_PAN_SCD_BK_MSK cpu_to_le32(BIT(4)) +#define IWL_PAN_SCD_BE_MSK cpu_to_le32(BIT(5)) +#define IWL_PAN_SCD_VI_MSK cpu_to_le32(BIT(6)) +#define IWL_PAN_SCD_VO_MSK cpu_to_le32(BIT(7)) +#define IWL_PAN_SCD_MGMT_MSK cpu_to_le32(BIT(7)) +#define IWL_PAN_SCD_MULTICAST_MSK cpu_to_le32(BIT(8)) + +#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) + +#define IWL_DROP_SINGLE 0 +#define IWL_DROP_ALL (BIT(IWL_RXON_CTX_BSS) | BIT(IWL_RXON_CTX_PAN)) + +/* + * REPLY_TXFIFO_FLUSH = 0x1e(command and response) + * + * When using full FIFO flush this command checks the scheduler HW block WR/RD + * pointers to check if all the frames were transferred by DMA into the + * relevant TX FIFO queue. Only when the DMA is finished and the queue is + * empty the command can finish. + * This command is used to flush the TXFIFO from transmit commands, it may + * operate on single or multiple queues, the command queue can't be flushed by + * this command. The command response is returned when all the queue flush + * operations are done. Each TX command flushed return response with the FLUSH + * status set in the TX response status. When FIFO flush operation is used, + * the flush operation ends when both the scheduler DMA done and TXFIFO empty + * are set. + * + * @fifo_control: bit mask for which queues to flush + * @flush_control: flush controls + * 0: Dump single MSDU + * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. + * 2: Dump all FIFO + */ +struct iwl_txfifo_flush_cmd { + __le32 fifo_control; + __le16 flush_control; + __le16 reserved; +} __packed; + +/* + * REPLY_WEP_KEY = 0x20 + */ +struct iwl_wep_key { + u8 key_index; + u8 key_offset; + u8 reserved1[2]; + u8 key_size; + u8 reserved2[3]; + u8 key[16]; +} __packed; + +struct iwl_wep_cmd { + u8 num_keys; + u8 global_key_type; + u8 flags; + u8 reserved; + struct iwl_wep_key key[0]; +} __packed; + +#define WEP_KEY_WEP_TYPE 1 +#define WEP_KEYS_MAX 4 +#define WEP_INVALID_OFFSET 0xff +#define WEP_KEY_LEN_64 5 +#define WEP_KEY_LEN_128 13 + +/****************************************************************************** + * (4) + * Rx Responses: + * + *****************************************************************************/ + +#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) +#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) + +#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) +#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) +#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) +#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 +#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 + +#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) +#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) +#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) +#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) +#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) +#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) + +#define RX_RES_STATUS_STATION_FOUND (1<<6) +#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) + +#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) +#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) +#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) +#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) +#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) + +#define RX_MPDU_RES_STATUS_ICV_OK (0x20) +#define RX_MPDU_RES_STATUS_MIC_OK (0x40) +#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) +#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) + + +#define IWLAGN_RX_RES_PHY_CNT 8 +#define IWLAGN_RX_RES_AGC_IDX 1 +#define IWLAGN_RX_RES_RSSI_AB_IDX 2 +#define IWLAGN_RX_RES_RSSI_C_IDX 3 +#define IWLAGN_OFDM_AGC_MSK 0xfe00 +#define IWLAGN_OFDM_AGC_BIT_POS 9 +#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff +#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00 +#define IWLAGN_OFDM_RSSI_A_BIT_POS 0 +#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000 +#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000 +#define IWLAGN_OFDM_RSSI_B_BIT_POS 16 +#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff +#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00 +#define IWLAGN_OFDM_RSSI_C_BIT_POS 0 + +struct iwlagn_non_cfg_phy { + __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */ +} __packed; + + +/* + * REPLY_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +struct iwl_rx_phy_res { + u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ + u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ + u8 stat_id; /* configurable DSP phy data set ID */ + u8 reserved1; + __le64 timestamp; /* TSF at on air rise */ + __le32 beacon_time_stamp; /* beacon at on-air rise */ + __le16 phy_flags; /* general phy flags: band, modulation, ... */ + __le16 channel; /* channel number */ + u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ + __le32 rate_n_flags; /* RATE_MCS_* */ + __le16 byte_count; /* frame's byte-count */ + __le16 frame_time; /* frame's time on the air */ +} __packed; + +struct iwl_rx_mpdu_res_start { + __le16 byte_count; + __le16 reserved; +} __packed; + + +/****************************************************************************** + * (5) + * Tx Commands & Responses: + * + * Driver must place each REPLY_TX command into one of the prioritized Tx + * queues in host DRAM, shared between driver and device (see comments for + * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode + * are preparing to transmit, the device pulls the Tx command over the PCI + * bus via one of the device's Tx DMA channels, to fill an internal FIFO + * from which data will be transmitted. + * + * uCode handles all timing and protocol related to control frames + * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler + * handle reception of block-acks; uCode updates the host driver via + * REPLY_COMPRESSED_BA. + * + * uCode handles retrying Tx when an ACK is expected but not received. + * This includes trying lower data rates than the one requested in the Tx + * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn). + * + * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. + * This command must be executed after every RXON command, before Tx can occur. + *****************************************************************************/ + +/* REPLY_TX Tx flags field */ + +/* + * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it + * before this frame. if CTS-to-self required check + * RXON_FLG_SELF_CTS_EN status. + */ +#define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0) + +/* 1: Expect ACK from receiving station + * 0: Don't expect ACK (MAC header's duration field s/b 0) + * Set this for unicast frames, but not broadcast/multicast. */ +#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) + +/* For agn devices: + * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). + * Tx command's initial_rate_index indicates first rate to try; + * uCode walks through table for additional Tx attempts. + * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. + * This rate will be used for all Tx attempts; it will not be scaled. */ +#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) + +/* 1: Expect immediate block-ack. + * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ +#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) + +/* Tx antenna selection field; reserved (0) for agn devices. */ +#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) + +/* 1: Ignore Bluetooth priority for this frame. + * 0: Delay Tx until Bluetooth device is done (normal usage). */ +#define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12) + +/* 1: uCode overrides sequence control field in MAC header. + * 0: Driver provides sequence control field in MAC header. + * Set this for management frames, non-QOS data frames, non-unicast frames, + * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ +#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) + +/* 1: This frame is non-last MPDU; more fragments are coming. + * 0: Last fragment, or not using fragmentation. */ +#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) + +/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. + * 0: No TSF required in outgoing frame. + * Set this for transmitting beacons and probe responses. */ +#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) + +/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword + * alignment of frame's payload data field. + * 0: No pad + * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 + * field (but not both). Driver must align frame data (i.e. data following + * MAC header) to DWORD boundary. */ +#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) + +/* accelerate aggregation support + * 0 - no CCMP encryption; 1 - CCMP encryption */ +#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) + +/* HCCA-AP - disable duration overwriting. */ +#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) + + +/* + * TX command security control + */ +#define TX_CMD_SEC_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * security overhead sizes + */ +#define WEP_IV_LEN 4 +#define WEP_ICV_LEN 4 +#define CCMP_MIC_LEN 8 +#define TKIP_ICV_LEN 4 + +/* + * REPLY_TX = 0x1c (command) + */ + +/* + * 4965 uCode updates these Tx attempt count values in host DRAM. + * Used for managing Tx retries when expecting block-acks. + * Driver should set these fields to 0. + */ +struct iwl_dram_scratch { + u8 try_cnt; /* Tx attempts */ + u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ + __le16 reserved; +} __packed; + +struct iwl_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + /* uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct iwl_dram_scratch scratch; + + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* Index of destination station in uCode's station table */ + u8 sta_id; + + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ + + /* + * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_index; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; + +/* + * TX command response is sent after *agn* transmission attempts. + * + * both postpone and abort status are expected behavior from uCode. there is + * no special operation required from driver; except for RFKILL_FLUSH, + * which required tx flush host command to flush all the tx frames in queues + */ +enum { + TX_STATUS_SUCCESS = 0x01, + TX_STATUS_DIRECT_DONE = 0x02, + /* postpone TX */ + TX_STATUS_POSTPONE_DELAY = 0x40, + TX_STATUS_POSTPONE_FEW_BYTES = 0x41, + TX_STATUS_POSTPONE_BT_PRIO = 0x42, + TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, + TX_STATUS_POSTPONE_CALC_TTAK = 0x44, + /* abort TX */ + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, + TX_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, + TX_STATUS_FAIL_DRAIN_FLOW = 0x85, + TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, + TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_STATUS_FAIL_DEST_PS = 0x88, + TX_STATUS_FAIL_HOST_ABORTED = 0x89, + TX_STATUS_FAIL_BT_RETRY = 0x8a, + TX_STATUS_FAIL_STA_INVALID = 0x8b, + TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, + TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, + TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, + TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, +}; + +#define TX_PACKET_MODE_REGULAR 0x0000 +#define TX_PACKET_MODE_BURST_SEQ 0x0100 +#define TX_PACKET_MODE_BURST_FIRST 0x0200 + +enum { + TX_POWER_PA_NOT_ACTIVE = 0x0, +}; + +enum { + TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ + TX_STATUS_DELAY_MSK = 0x00000040, + TX_STATUS_ABORT_MSK = 0x00000080, + TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ + TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ + TX_RESERVED = 0x00780000, /* bits 19:22 */ + TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ + TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ +}; + +/* ******************************* + * TX aggregation status + ******************************* */ + +enum { + AGG_TX_STATE_TRANSMITTED = 0x00, + AGG_TX_STATE_UNDERRUN_MSK = 0x01, + AGG_TX_STATE_BT_PRIO_MSK = 0x02, + AGG_TX_STATE_FEW_BYTES_MSK = 0x04, + AGG_TX_STATE_ABORT_MSK = 0x08, + AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, + AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40, + AGG_TX_STATE_SCD_QUERY_MSK = 0x80, + AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, + AGG_TX_STATE_RESPONSE_MSK = 0x1ff, + AGG_TX_STATE_DUMP_TX_MSK = 0x200, + AGG_TX_STATE_DELAY_TX_MSK = 0x400 +}; + +#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ +#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ + +#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \ + AGG_TX_STATE_LAST_SENT_BT_KILL_MSK) + +/* # tx attempts for first frame in aggregation */ +#define AGG_TX_STATE_TRY_CNT_POS 12 +#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 + +/* Command ID and sequence number of Tx command for this frame */ +#define AGG_TX_STATE_SEQ_NUM_POS 16 +#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 + +/* + * REPLY_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for + * a single frame. Multiple attempts, at various bit rates, may have + * been made for this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for + * 2 or more frames that used block-acknowledge. All frames were + * transmitted at same rate. Rate scaling may have been used if first + * frame in this new agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered here; + * block-ack has not been received by the time the agn device records + * this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the sending station (this agn device), rather than whether it was + * received successfully by the destination station. + */ +struct agg_tx_status { + __le16 status; + __le16 sequence; +} __packed; + +/* + * definitions for initial rate index field + * bits [3:0] initial rate index + * bits [6:4] rate table color, used for the initial rate + * bit-7 invalid rate indication + * i.e. rate was not chosen from rate table + * or rate table color was changed during frame retries + * refer tlc rate info + */ + +#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0 +#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f +#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4 +#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70 +#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80 + +/* refer to ra_tid */ +#define IWLAGN_TX_RES_TID_POS 0 +#define IWLAGN_TX_RES_TID_MSK 0x0f +#define IWLAGN_TX_RES_RA_POS 4 +#define IWLAGN_TX_RES_RA_MSK 0xf0 + +struct iwlagn_tx_resp { + u8 frame_count; /* 1 no aggregation, >1 aggregation */ + u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ + u8 failure_rts; /* # failures due to unsuccessful RTS */ + u8 failure_frame; /* # failures due to no ACK (unused for agg) */ + + /* For non-agg: Rate at which frame was successful. + * For agg: Rate at which all frames were transmitted. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* For non-agg: RTS + CTS + frame tx attempts time + ACK. + * For agg: RTS + CTS + aggregation tx time + block-ack time. */ + __le16 wireless_media_time; /* uSecs */ + + u8 pa_status; /* RF power amplifier measurement (not used) */ + u8 pa_integ_res_a[3]; + u8 pa_integ_res_b[3]; + u8 pa_integ_res_C[3]; + + __le32 tfd_info; + __le16 seq_ctl; + __le16 byte_cnt; + u8 tlc_info; + u8 ra_tid; /* tid (0:3), sta_id (4:7) */ + __le16 frame_ctrl; + /* + * For non-agg: frame status TX_STATUS_* + * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status + * fields follow this one, up to frame_count. + * Bit fields: + * 11- 0: AGG_TX_STATE_* status code + * 15-12: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a + * member of a previous aggregation block). If rate + * scaling is used, retry count indicates the rate + * table entry used for all frames in the new agg. + * 31-16: Sequence # for this frame's Tx cmd (not SSN!) + */ + struct agg_tx_status status; /* TX status (in aggregation - + * status of 1st frame) */ +} __packed; +/* + * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) + * + * Reports Block-Acknowledge from recipient station + */ +struct iwl_compressed_ba_resp { + __le32 sta_addr_lo32; + __le16 sta_addr_hi16; + __le16 reserved; + + /* Index of recipient (BA-sending) station in uCode's station table */ + u8 sta_id; + u8 tid; + __le16 seq_ctl; + __le64 bitmap; + __le16 scd_flow; + __le16 scd_ssn; + u8 txed; /* number of frames sent */ + u8 txed_2_done; /* number of frames acked */ +} __packed; + +/* + * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) + * + */ + +/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ +#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) + +/* # of EDCA prioritized tx fifos */ +#define LINK_QUAL_AC_NUM AC_NUM + +/* # entries in rate scale table to support Tx retries */ +#define LINK_QUAL_MAX_RETRY_NUM 16 + +/* Tx antenna selection values */ +#define LINK_QUAL_ANT_A_MSK (1 << 0) +#define LINK_QUAL_ANT_B_MSK (1 << 1) +#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) + + +/** + * struct iwl_link_qual_general_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl_link_qual_general_params { + u8 flags; + + /* No entries at or above this (driver chosen) index contain MIMO */ + u8 mimo_delimiter; + + /* Best single antenna to use for single stream (legacy, SISO). */ + u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* Best antennas to use for MIMO (unused for 4965, assumes both). */ + u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* + * If driver needs to use different initial rates for different + * EDCA QOS access categories (as implemented by tx fifos 0-3), + * this table will set that up, by indicating the indexes in the + * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. + * Otherwise, driver should set all entries to 0. + * + * Entry usage: + * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice + * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. + */ + u8 start_rate_index[LINK_QUAL_AC_NUM]; +} __packed; + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ +#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) +#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) + +#define LINK_QUAL_AGG_DISABLE_START_DEF (3) +#define LINK_QUAL_AGG_DISABLE_START_MAX (255) +#define LINK_QUAL_AGG_DISABLE_START_MIN (0) + +#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) + +/** + * struct iwl_link_qual_agg_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl_link_qual_agg_params { + + /* + *Maximum number of uSec in aggregation. + * default set to 4000 (4 milliseconds) if not configured in .cfg + */ + __le16 agg_time_limit; + + /* + * Number of Tx retries allowed for a frame, before that frame will + * no longer be considered for the start of an aggregation sequence + * (scheduler will then try to tx it as single frame). + * Driver should set this to 3. + */ + u8 agg_dis_start_th; + + /* + * Maximum number of frames in aggregation. + * 0 = no limit (default). 1 = no aggregation. + * Other values = max # frames in aggregation. + */ + u8 agg_frame_cnt_limit; + + __le32 reserved; +} __packed; + +/* + * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) + * + * For agn devices + * + * Each station in the agn device's internal station table has its own table + * of 16 + * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when + * an ACK is not received. This command replaces the entire table for + * one station. + * + * NOTE: Station must already be in agn device's station table. + * Use REPLY_ADD_STA. + * + * The rate scaling procedures described below work well. Of course, other + * procedures are possible, and may work better for particular environments. + * + * + * FILLING THE RATE TABLE + * + * Given a particular initial rate and mode, as determined by the rate + * scaling algorithm described below, the Linux driver uses the following + * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the + * Link Quality command: + * + * + * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: + * a) Use this same initial rate for first 3 entries. + * b) Find next lower available rate using same mode (SISO or MIMO), + * use for next 3 entries. If no lower rate available, switch to + * legacy mode (no HT40 channel, no MIMO, no short guard interval). + * c) If using MIMO, set command's mimo_delimiter to number of entries + * using MIMO (3 or 6). + * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, + * no MIMO, no short guard interval), at the next lower bit rate + * (e.g. if second HT bit rate was 54, try 48 legacy), and follow + * legacy procedure for remaining table entries. + * + * 2) If using legacy initial rate: + * a) Use the initial rate for only one entry. + * b) For each following entry, reduce the rate to next lower available + * rate, until reaching the lowest available rate. + * c) When reducing rate, also switch antenna selection. + * d) Once lowest available rate is reached, repeat this rate until + * rate table is filled (16 entries), switching antenna each entry. + * + * + * ACCUMULATING HISTORY + * + * The rate scaling algorithm for agn devices, as implemented in Linux driver, + * uses two sets of frame Tx success history: One for the current/active + * modulation mode, and one for a speculative/search mode that is being + * attempted. If the speculative mode turns out to be more effective (i.e. + * actual transfer rate is better), then the driver continues to use the + * speculative mode as the new current active mode. + * + * Each history set contains, separately for each possible rate, data for a + * sliding window of the 62 most recent tx attempts at that rate. The data + * includes a shifting bitmap of success(1)/failure(0), and sums of successful + * and attempted frames, from which the driver can additionally calculate a + * success ratio (success / attempted) and number of failures + * (attempted - success), and control the size of the window (attempted). + * The driver uses the bit map to remove successes from the success sum, as + * the oldest tx attempts fall out of the window. + * + * When the agn device makes multiple tx attempts for a given frame, each + * attempt might be at a different rate, and have different modulation + * characteristics (e.g. antenna, fat channel, short guard interval), as set + * up in the rate scaling table in the Link Quality command. The driver must + * determine which rate table entry was used for each tx attempt, to determine + * which rate-specific history to update, and record only those attempts that + * match the modulation characteristics of the history set. + * + * When using block-ack (aggregation), all frames are transmitted at the same + * rate, since there is no per-attempt acknowledgment from the destination + * station. The Tx response struct iwl_tx_resp indicates the Tx rate in + * rate_n_flags field. After receiving a block-ack, the driver can update + * history for the entire block all at once. + * + * + * FINDING BEST STARTING RATE: + * + * When working with a selected initial modulation mode (see below), the + * driver attempts to find a best initial rate. The initial rate is the + * first entry in the Link Quality command's rate table. + * + * 1) Calculate actual throughput (success ratio * expected throughput, see + * table below) for current initial rate. Do this only if enough frames + * have been attempted to make the value meaningful: at least 6 failed + * tx attempts, or at least 8 successes. If not enough, don't try rate + * scaling yet. + * + * 2) Find available rates adjacent to current initial rate. Available means: + * a) supported by hardware && + * b) supported by association && + * c) within any constraints selected by user + * + * 3) Gather measured throughputs for adjacent rates. These might not have + * enough history to calculate a throughput. That's okay, we might try + * using one of them anyway! + * + * 4) Try decreasing rate if, for current rate: + * a) success ratio is < 15% || + * b) lower adjacent rate has better measured throughput || + * c) higher adjacent rate has worse throughput, and lower is unmeasured + * + * As a sanity check, if decrease was determined above, leave rate + * unchanged if: + * a) lower rate unavailable + * b) success ratio at current rate > 85% (very good) + * c) current measured throughput is better than expected throughput + * of lower rate (under perfect 100% tx conditions, see table below) + * + * 5) Try increasing rate if, for current rate: + * a) success ratio is < 15% || + * b) both adjacent rates' throughputs are unmeasured (try it!) || + * b) higher adjacent rate has better measured throughput || + * c) lower adjacent rate has worse throughput, and higher is unmeasured + * + * As a sanity check, if increase was determined above, leave rate + * unchanged if: + * a) success ratio at current rate < 70%. This is not particularly + * good performance; higher rate is sure to have poorer success. + * + * 6) Re-evaluate the rate after each tx frame. If working with block- + * acknowledge, history and statistics may be calculated for the entire + * block (including prior history that fits within the history windows), + * before re-evaluation. + * + * FINDING BEST STARTING MODULATION MODE: + * + * After working with a modulation mode for a "while" (and doing rate scaling), + * the driver searches for a new initial mode in an attempt to improve + * throughput. The "while" is measured by numbers of attempted frames: + * + * For legacy mode, search for new mode after: + * 480 successful frames, or 160 failed frames + * For high-throughput modes (SISO or MIMO), search for new mode after: + * 4500 successful frames, or 400 failed frames + * + * Mode switch possibilities are (3 for each mode): + * + * For legacy: + * Change antenna, try SISO (if HT association), try MIMO (if HT association) + * For SISO: + * Change antenna, try MIMO, try shortened guard interval (SGI) + * For MIMO: + * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) + * + * When trying a new mode, use the same bit rate as the old/current mode when + * trying antenna switches and shortened guard interval. When switching to + * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate + * for which the expected throughput (under perfect conditions) is about the + * same or slightly better than the actual measured throughput delivered by + * the old/current mode. + * + * Actual throughput can be estimated by multiplying the expected throughput + * by the success ratio (successful / attempted tx frames). Frame size is + * not considered in this calculation; it assumes that frame size will average + * out to be fairly consistent over several samples. The following are + * metric values for expected throughput assuming 100% success ratio. + * Only G band has support for CCK rates: + * + * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 + * + * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 + * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 + * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 + * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 + * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 + * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 + * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 + * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 + * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 + * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 + * + * After the new mode has been tried for a short while (minimum of 6 failed + * frames or 8 successful frames), compare success ratio and actual throughput + * estimate of the new mode with the old. If either is better with the new + * mode, continue to use the new mode. + * + * Continue comparing modes until all 3 possibilities have been tried. + * If moving from legacy to HT, try all 3 possibilities from the new HT + * mode. After trying all 3, a best mode is found. Continue to use this mode + * for the longer "while" described above (e.g. 480 successful frames for + * legacy), and then repeat the search process. + * + */ +struct iwl_link_quality_cmd { + + /* Index of destination/recipient station in uCode's station table */ + u8 sta_id; + u8 reserved1; + __le16 control; /* not used */ + struct iwl_link_qual_general_params general_params; + struct iwl_link_qual_agg_params agg_params; + + /* + * Rate info; when using rate-scaling, Tx command's initial_rate_index + * specifies 1st Tx rate attempted, via index into this table. + * agn devices works its way through table when retrying Tx. + */ + struct { + __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ + } rs_table[LINK_QUAL_MAX_RETRY_NUM]; + __le32 reserved2; +} __packed; + +/* + * BT configuration enable flags: + * bit 0 - 1: BT channel announcement enabled + * 0: disable + * bit 1 - 1: priority of BT device enabled + * 0: disable + * bit 2 - 1: BT 2 wire support enabled + * 0: disable + */ +#define BT_COEX_DISABLE (0x0) +#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) +#define BT_ENABLE_PRIORITY BIT(1) +#define BT_ENABLE_2_WIRE BIT(2) + +#define BT_COEX_DISABLE (0x0) +#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) + +#define BT_LEAD_TIME_MIN (0x0) +#define BT_LEAD_TIME_DEF (0x1E) +#define BT_LEAD_TIME_MAX (0xFF) + +#define BT_MAX_KILL_MIN (0x1) +#define BT_MAX_KILL_DEF (0x5) +#define BT_MAX_KILL_MAX (0xFF) + +#define BT_DURATION_LIMIT_DEF 625 +#define BT_DURATION_LIMIT_MAX 1250 +#define BT_DURATION_LIMIT_MIN 625 + +#define BT_ON_THRESHOLD_DEF 4 +#define BT_ON_THRESHOLD_MAX 1000 +#define BT_ON_THRESHOLD_MIN 1 + +#define BT_FRAG_THRESHOLD_DEF 0 +#define BT_FRAG_THRESHOLD_MAX 0 +#define BT_FRAG_THRESHOLD_MIN 0 + +#define BT_AGG_THRESHOLD_DEF 1200 +#define BT_AGG_THRESHOLD_MAX 8000 +#define BT_AGG_THRESHOLD_MIN 400 + +/* + * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) + * + * agn devices support hardware handshake with Bluetooth device on + * same platform. Bluetooth device alerts wireless device when it will Tx; + * wireless device can delay or kill its own Tx to accommodate. + */ +struct iwl_bt_cmd { + u8 flags; + u8 lead_time; + u8 max_kill; + u8 reserved; + __le32 kill_ack_mask; + __le32 kill_cts_mask; +} __packed; + +#define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0) + +#define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5)) +#define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3 +#define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0 +#define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1 +#define IWLAGN_BT_FLAG_COEX_MODE_3W 2 +#define IWLAGN_BT_FLAG_COEX_MODE_4W 3 + +#define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6) +/* Disable Sync PSPoll on SCO/eSCO */ +#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7) + +#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */ +#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */ + +#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF +#define IWLAGN_BT_PRIO_BOOST_MIN 0x00 +#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0 + +#define IWLAGN_BT_MAX_KILL_DEFAULT 5 + +#define IWLAGN_BT3_T7_DEFAULT 1 + +enum iwl_bt_kill_idx { + IWL_BT_KILL_DEFAULT = 0, + IWL_BT_KILL_OVERRIDE = 1, + IWL_BT_KILL_REDUCE = 2, +}; + +#define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000) +#define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000) +#define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff) +#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0) + +#define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2 + +#define IWLAGN_BT3_T2_DEFAULT 0xc + +#define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0)) +#define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1)) +#define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2)) +#define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3)) +#define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4)) +#define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5)) +#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6)) +#define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7)) + +#define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \ + IWLAGN_BT_VALID_BOOST | \ + IWLAGN_BT_VALID_MAX_KILL | \ + IWLAGN_BT_VALID_3W_TIMERS | \ + IWLAGN_BT_VALID_KILL_ACK_MASK | \ + IWLAGN_BT_VALID_KILL_CTS_MASK | \ + IWLAGN_BT_VALID_REDUCED_TX_PWR | \ + IWLAGN_BT_VALID_3W_LUT) + +#define IWLAGN_BT_REDUCED_TX_PWR BIT(0) + +#define IWLAGN_BT_DECISION_LUT_SIZE 12 + +struct iwl_basic_bt_cmd { + u8 flags; + u8 ledtime; /* unused */ + u8 max_kill; + u8 bt3_timer_t7_value; + __le32 kill_ack_mask; + __le32 kill_cts_mask; + u8 bt3_prio_sample_time; + u8 bt3_timer_t2_value; + __le16 bt4_reaction_time; /* unused */ + __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; + /* + * bit 0: use reduced tx power for control frame + * bit 1 - 7: reserved + */ + u8 reduce_txpower; + u8 reserved; + __le16 valid; +}; + +struct iwl_bt_cmd_v1 { + struct iwl_basic_bt_cmd basic; + u8 prio_boost; + /* + * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask + * if configure the following patterns + */ + u8 tx_prio_boost; /* SW boost of WiFi tx priority */ + __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ +}; + +struct iwl_bt_cmd_v2 { + struct iwl_basic_bt_cmd basic; + __le32 prio_boost; + /* + * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask + * if configure the following patterns + */ + u8 reserved; + u8 tx_prio_boost; /* SW boost of WiFi tx priority */ + __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ +}; + +#define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0)) + +struct iwlagn_bt_sco_cmd { + __le32 flags; +}; + +/****************************************************************************** + * (6) + * Spectrum Management (802.11h) Commands, Responses, Notifications: + * + *****************************************************************************/ + +/* + * Spectrum Management + */ +#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ + RXON_FILTER_CTL2HOST_MSK | \ + RXON_FILTER_ACCEPT_GRP_MSK | \ + RXON_FILTER_DIS_DECRYPT_MSK | \ + RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ + RXON_FILTER_ASSOC_MSK | \ + RXON_FILTER_BCON_AWARE_MSK) + +struct iwl_measure_channel { + __le32 duration; /* measurement duration in extended beacon + * format */ + u8 channel; /* channel to measure */ + u8 type; /* see enum iwl_measure_type */ + __le16 reserved; +} __packed; + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) + */ +struct iwl_spectrum_cmd { + __le16 len; /* number of bytes starting from token */ + u8 token; /* token id */ + u8 id; /* measurement id -- 0 or 1 */ + u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ + u8 periodic; /* 1 = periodic */ + __le16 path_loss_timeout; + __le32 start_time; /* start time in extended beacon format */ + __le32 reserved2; + __le32 flags; /* rxon flags */ + __le32 filter_flags; /* rxon filter flags */ + __le16 channel_count; /* minimum 1, maximum 10 */ + __le16 reserved3; + struct iwl_measure_channel channels[10]; +} __packed; + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) + */ +struct iwl_spectrum_resp { + u8 token; + u8 id; /* id of the prior command replaced, or 0xff */ + __le16 status; /* 0 - command will be handled + * 1 - cannot handle (conflicts with another + * measurement) */ +} __packed; + +enum iwl_measurement_state { + IWL_MEASUREMENT_START = 0, + IWL_MEASUREMENT_STOP = 1, +}; + +enum iwl_measurement_status { + IWL_MEASUREMENT_OK = 0, + IWL_MEASUREMENT_CONCURRENT = 1, + IWL_MEASUREMENT_CSA_CONFLICT = 2, + IWL_MEASUREMENT_TGH_CONFLICT = 3, + /* 4-5 reserved */ + IWL_MEASUREMENT_STOPPED = 6, + IWL_MEASUREMENT_TIMEOUT = 7, + IWL_MEASUREMENT_PERIODIC_FAILED = 8, +}; + +#define NUM_ELEMENTS_IN_HISTOGRAM 8 + +struct iwl_measurement_histogram { + __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ + __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ +} __packed; + +/* clear channel availability counters */ +struct iwl_measurement_cca_counters { + __le32 ofdm; + __le32 cck; +} __packed; + +enum iwl_measure_type { + IWL_MEASURE_BASIC = (1 << 0), + IWL_MEASURE_CHANNEL_LOAD = (1 << 1), + IWL_MEASURE_HISTOGRAM_RPI = (1 << 2), + IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3), + IWL_MEASURE_FRAME = (1 << 4), + /* bits 5:6 are reserved */ + IWL_MEASURE_IDLE = (1 << 7), +}; + +/* + * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command) + */ +struct iwl_spectrum_notification { + u8 id; /* measurement id -- 0 or 1 */ + u8 token; + u8 channel_index; /* index in measurement channel list */ + u8 state; /* 0 - start, 1 - stop */ + __le32 start_time; /* lower 32-bits of TSF */ + u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ + u8 channel; + u8 type; /* see enum iwl_measurement_type */ + u8 reserved1; + /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only + * valid if applicable for measurement type requested. */ + __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ + __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ + __le32 cca_time; /* channel load time in usecs */ + u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - + * unidentified */ + u8 reserved2[3]; + struct iwl_measurement_histogram histogram; + __le32 stop_time; /* lower 32-bits of TSF */ + __le32 status; /* see iwl_measurement_status */ +} __packed; + +/****************************************************************************** + * (7) + * Power Management Commands, Responses, Notifications: + * + *****************************************************************************/ + +/** + * struct iwl_powertable_cmd - Power Table Command + * @flags: See below: + * + * POWER_TABLE_CMD = 0x77 (command, has simple generic response) + * + * PM allow: + * bit 0 - '0' Driver not allow power management + * '1' Driver allow PM (use rest of parameters) + * + * uCode send sleep notifications: + * bit 1 - '0' Don't send sleep notification + * '1' send sleep notification (SEND_PM_NOTIFICATION) + * + * Sleep over DTIM + * bit 2 - '0' PM have to walk up every DTIM + * '1' PM could sleep over DTIM till listen Interval. + * + * PCI power managed + * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) + * '1' !(PCI_CFG_LINK_CTRL & 0x1) + * + * Fast PD + * bit 4 - '1' Put radio to sleep when receiving frame for others + * + * Force sleep Modes + * bit 31/30- '00' use both mac/xtal sleeps + * '01' force Mac sleep + * '10' force xtal sleep + * '11' Illegal set + * + * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then + * ucode assume sleep over DTIM is allowed and we don't need to wake up + * for every DTIM. + */ +#define IWL_POWER_VEC_SIZE 5 + +#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) +#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0)) +#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1)) +#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) +#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) +#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) +#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5)) +#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6)) +#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7)) +#define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8)) +#define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9)) + +struct iwl_powertable_cmd { + __le16 flags; + u8 keep_alive_seconds; + u8 debug_flags; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; + __le32 keep_alive_beacons; +} __packed; + +/* + * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) + * all devices identical. + */ +struct iwl_sleep_notification { + u8 pm_sleep_mode; + u8 pm_wakeup_src; + __le16 reserved; + __le32 sleep_time; + __le32 tsf_low; + __le32 bcon_timer; +} __packed; + +/* Sleep states. all devices identical. */ +enum { + IWL_PM_NO_SLEEP = 0, + IWL_PM_SLP_MAC = 1, + IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, + IWL_PM_SLP_FULL_MAC_CARD_STATE = 3, + IWL_PM_SLP_PHY = 4, + IWL_PM_SLP_REPENT = 5, + IWL_PM_WAKEUP_BY_TIMER = 6, + IWL_PM_WAKEUP_BY_DRIVER = 7, + IWL_PM_WAKEUP_BY_RFKILL = 8, + /* 3 reserved */ + IWL_PM_NUM_OF_MODES = 12, +}; + +/* + * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response) + */ +#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */ +#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */ +#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */ +struct iwl_card_state_cmd { + __le32 status; /* CARD_STATE_CMD_* request new power state */ +} __packed; + +/* + * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) + */ +struct iwl_card_state_notif { + __le32 flags; +} __packed; + +#define HW_CARD_DISABLED 0x01 +#define SW_CARD_DISABLED 0x02 +#define CT_CARD_DISABLED 0x04 +#define RXON_CARD_DISABLED 0x10 + +struct iwl_ct_kill_config { + __le32 reserved; + __le32 critical_temperature_M; + __le32 critical_temperature_R; +} __packed; + +/* 1000, and 6x00 */ +struct iwl_ct_kill_throttling_config { + __le32 critical_temperature_exit; + __le32 reserved; + __le32 critical_temperature_enter; +} __packed; + +/****************************************************************************** + * (8) + * Scan Commands, Responses, Notifications: + * + *****************************************************************************/ + +#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) +#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) + +/** + * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table + * + * One for each channel in the scan list. + * Each channel can independently select: + * 1) SSID for directed active scans + * 2) Txpower setting (for rate specified within Tx command) + * 3) How long to stay on-channel (behavior may be modified by quiet_time, + * quiet_plcp_th, good_CRC_th) + * + * To avoid uCode errors, make sure the following are true (see comments + * under struct iwl_scan_cmd about max_out_time and quiet_time): + * 1) If using passive_dwell (i.e. passive_dwell != 0): + * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) + * 2) quiet_time <= active_dwell + * 3) If restricting off-channel time (i.e. max_out_time !=0): + * passive_dwell < max_out_time + * active_dwell < max_out_time + */ + +struct iwl_scan_channel { + /* + * type is defined as: + * 0:0 1 = active, 0 = passive + * 1:20 SSID direct bit map; if a bit is set, then corresponding + * SSID IE is transmitted in probe request. + * 21:31 reserved + */ + __le32 type; + __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */ + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ + __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ + __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ +} __packed; + +/* set number of direct probes __le32 type */ +#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) + +/** + * struct iwl_ssid_ie - directed scan network information element + * + * Up to 20 of these may appear in REPLY_SCAN_CMD, + * selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 entries. + * SSID IEs get transmitted in reverse order of entry. + */ +struct iwl_ssid_ie { + u8 id; + u8 len; + u8 ssid[32]; +} __packed; + +#define PROBE_OPTION_MAX 20 +#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) +#define IWL_GOOD_CRC_TH_DISABLED 0 +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) +#define IWL_MAX_CMD_SIZE 4096 + +/* + * REPLY_SCAN_CMD = 0x80 (command) + * + * The hardware scan command is very powerful; the driver can set it up to + * maintain (relatively) normal network traffic while doing a scan in the + * background. The max_out_time and suspend_time control the ratio of how + * long the device stays on an associated network channel ("service channel") + * vs. how long it's away from the service channel, i.e. tuned to other channels + * for scanning. + * + * max_out_time is the max time off-channel (in usec), and suspend_time + * is how long (in "extended beacon" format) that the scan is "suspended" + * after returning to the service channel. That is, suspend_time is the + * time that we stay on the service channel, doing normal work, between + * scan segments. The driver may set these parameters differently to support + * scanning when associated vs. not associated, and light vs. heavy traffic + * loads when associated. + * + * After receiving this command, the device's scan engine does the following; + * + * 1) Sends SCAN_START notification to driver + * 2) Checks to see if it has time to do scan for one channel + * 3) Sends NULL packet, with power-save (PS) bit set to 1, + * to tell AP that we're going off-channel + * 4) Tunes to first channel in scan list, does active or passive scan + * 5) Sends SCAN_RESULT notification to driver + * 6) Checks to see if it has time to do scan on *next* channel in list + * 7) Repeats 4-6 until it no longer has time to scan the next channel + * before max_out_time expires + * 8) Returns to service channel + * 9) Sends NULL packet with PS=0 to tell AP that we're back + * 10) Stays on service channel until suspend_time expires + * 11) Repeats entire process 2-10 until list is complete + * 12) Sends SCAN_COMPLETE notification + * + * For fast, efficient scans, the scan command also has support for staying on + * a channel for just a short time, if doing active scanning and getting no + * responses to the transmitted probe request. This time is controlled by + * quiet_time, and the number of received packets below which a channel is + * considered "quiet" is controlled by quiet_plcp_threshold. + * + * For active scanning on channels that have regulatory restrictions against + * blindly transmitting, the scan can listen before transmitting, to make sure + * that there is already legitimate activity on the channel. If enough + * packets are cleanly received on the channel (controlled by good_CRC_th, + * typical value 1), the scan engine starts transmitting probe requests. + * + * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. + * + * To avoid uCode errors, see timing restrictions described under + * struct iwl_scan_channel. + */ + +enum iwl_scan_flags { + /* BIT(0) currently unused */ + IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1), + /* bits 2-7 reserved */ +}; + +struct iwl_scan_cmd { + __le16 len; + u8 scan_flags; /* scan flags: see enum iwl_scan_flags */ + u8 channel_count; /* # channels in channel list */ + __le16 quiet_time; /* dwell only this # millisecs on quiet channel + * (only for active scan) */ + __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ + __le16 good_CRC_th; /* passive -> active promotion threshold */ + __le16 rx_chain; /* RXON_RX_CHAIN_* */ + __le32 max_out_time; /* max usec to be away from associated (service) + * channel */ + __le32 suspend_time; /* pause scan this long (in "extended beacon + * format") when returning to service chnl: + */ + __le32 flags; /* RXON_FLG_* */ + __le32 filter_flags; /* RXON_FILTER_* */ + + /* For active scans (set to all-0s for passive scans). + * Does not include payload. Must specify Tx rate; no rate scaling. */ + struct iwl_tx_cmd tx_cmd; + + /* For directed active scans (set to all-0s otherwise) */ + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; + + /* + * Probe request frame, followed by channel list. + * + * Size of probe request frame is specified by byte count in tx_cmd. + * Channel list follows immediately after probe request frame. + * Number of channels in list is specified by channel_count. + * Each channel in list is of type: + * + * struct iwl_scan_channel channels[0]; + * + * NOTE: Only one band of channels can be scanned per pass. You + * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait + * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) + * before requesting another scan. + */ + u8 data[0]; +} __packed; + +/* Can abort will notify by complete notification with abort status. */ +#define CAN_ABORT_STATUS cpu_to_le32(0x1) +/* complete notification statuses */ +#define ABORT_STATUS 0x2 + +/* + * REPLY_SCAN_CMD = 0x80 (response) + */ +struct iwl_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ +} __packed; + +/* + * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) + */ +struct iwl_scanstart_notification { + __le32 tsf_low; + __le32 tsf_high; + __le32 beacon_timer; + u8 channel; + u8 band; + u8 reserved[2]; + __le32 status; +} __packed; + +#define SCAN_OWNER_STATUS 0x1 +#define MEASURE_OWNER_STATUS 0x2 + +#define IWL_PROBE_STATUS_OK 0 +#define IWL_PROBE_STATUS_TX_FAILED BIT(0) +/* error statuses combined with TX_FAILED */ +#define IWL_PROBE_STATUS_FAIL_TTL BIT(1) +#define IWL_PROBE_STATUS_FAIL_BT BIT(2) + +#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */ +/* + * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command) + */ +struct iwl_scanresults_notification { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; /* not enough time to send */ + __le32 tsf_low; + __le32 tsf_high; + __le32 statistics[NUMBER_OF_STATISTICS]; +} __packed; + +/* + * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command) + */ +struct iwl_scancomplete_notification { + u8 scanned_channels; + u8 status; + u8 bt_status; /* BT On/Off status */ + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; +} __packed; + + +/****************************************************************************** + * (9) + * IBSS/AP Commands and Notifications: + * + *****************************************************************************/ + +enum iwl_ibss_manager { + IWL_NOT_IBSS_MANAGER = 0, + IWL_IBSS_MANAGER = 1, +}; + +/* + * BEACON_NOTIFICATION = 0x90 (notification only, not a command) + */ + +struct iwlagn_beacon_notif { + struct iwlagn_tx_resp beacon_notify_hdr; + __le32 low_tsf; + __le32 high_tsf; + __le32 ibss_mgr_status; +} __packed; + +/* + * REPLY_TX_BEACON = 0x91 (command, has simple generic response) + */ + +struct iwl_tx_beacon_cmd { + struct iwl_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; + u8 reserved1; + struct ieee80211_hdr frame[0]; /* beacon frame */ +} __packed; + +/****************************************************************************** + * (10) + * Statistics Commands and Notifications: + * + *****************************************************************************/ + +#define IWL_TEMP_CONVERT 260 + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +/* Used for passing to driver number of successes and failures per rate */ +struct rate_histogram { + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __packed; + +/* statistics command response */ + +struct statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; + +struct statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved3; +} __packed; + +struct statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; + +#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) + +struct statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; +} __packed; + +struct statistics_rx_non_phy_bt { + struct statistics_rx_non_phy common; + /* additional stats for bt */ + __le32 num_bt_kills; + __le32 reserved[2]; +} __packed; + +struct statistics_rx { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy general; + struct statistics_rx_ht_phy ofdm_ht; +} __packed; + +struct statistics_rx_bt { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy_bt general; + struct statistics_rx_ht_phy ofdm_ht; +} __packed; + +/** + * struct statistics_tx_power - current tx power + * + * @ant_a: current tx power on chain a in 1/2 dB step + * @ant_b: current tx power on chain b in 1/2 dB step + * @ant_c: current tx power on chain c in 1/2 dB step + */ +struct statistics_tx_power { + u8 ant_a; + u8 ant_b; + u8 ant_c; + u8 reserved; +} __packed; + +struct statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; +} __packed; + +struct statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct statistics_tx_non_phy_agg agg; + /* + * "tx_power" are optional parameters provided by uCode, + * 6000 series is the only device provide the information, + * Those are reserved fields for all the other devices + */ + struct statistics_tx_power tx_power; + __le32 reserved1; +} __packed; + + +struct statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 reserved1; + __le32 reserved2; +} __packed; + +struct statistics_general_common { + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* radio voltage */ + struct statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct statistics_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; + +struct statistics_bt_activity { + /* Tx statistics */ + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + /* Rx statistics */ + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; + +struct statistics_general { + struct statistics_general_common common; + __le32 reserved2; + __le32 reserved3; +} __packed; + +struct statistics_general_bt { + struct statistics_general_common common; + struct statistics_bt_activity activity; + __le32 reserved2; + __le32 reserved3; +} __packed; + +#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) + +/* + * REPLY_STATISTICS_CMD = 0x9c, + * all devices identical. + * + * This command triggers an immediate response containing uCode statistics. + * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below. + * + * If the CLEAR_STATS configuration flag is set, uCode will clear its + * internal copy of the statistics (counters) after issuing the response. + * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below). + * + * If the DISABLE_NOTIF configuration flag is set, uCode will not issue + * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag + * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself. + */ +#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ +#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */ +struct iwl_statistics_cmd { + __le32 configuration_flags; /* IWL_STATS_CONF_* */ +} __packed; + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ +#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) +#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) + +struct iwl_notif_statistics { + __le32 flag; + struct statistics_rx rx; + struct statistics_tx tx; + struct statistics_general general; +} __packed; + +struct iwl_bt_notif_statistics { + __le32 flag; + struct statistics_rx_bt rx; + struct statistics_tx tx; + struct statistics_general_bt general; +} __packed; + +/* + * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) + * + * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed + * in regardless of how many missed beacons, which mean when driver receive the + * notification, inside the command, it can find all the beacons information + * which include number of total missed beacons, number of consecutive missed + * beacons, number of beacons received and number of beacons expected to + * receive. + * + * If uCode detected consecutive_missed_beacons > 5, it will reset the radio + * in order to bring the radio/PHY back to working state; which has no relation + * to when driver will perform sensitivity calibration. + * + * Driver should set it own missed_beacon_threshold to decide when to perform + * sensitivity calibration based on number of consecutive missed beacons in + * order to improve overall performance, especially in noisy environment. + * + */ + +#define IWL_MISSED_BEACON_THRESHOLD_MIN (1) +#define IWL_MISSED_BEACON_THRESHOLD_DEF (5) +#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF + +struct iwl_missed_beacon_notif { + __le32 consecutive_missed_beacons; + __le32 total_missed_becons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; + + +/****************************************************************************** + * (11) + * Rx Calibration Commands: + * + * With the uCode used for open source drivers, most Tx calibration (except + * for Tx Power) and most Rx calibration is done by uCode during the + * "initialize" phase of uCode boot. Driver must calibrate only: + * + * 1) Tx power (depends on temperature), described elsewhere + * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) + * 3) Receiver sensitivity (to optimize signal detection) + * + *****************************************************************************/ + +/** + * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) + * + * This command sets up the Rx signal detector for a sensitivity level that + * is high enough to lock onto all signals within the associated network, + * but low enough to ignore signals that are below a certain threshold, so as + * not to have too many "false alarms". False alarms are signals that the + * Rx DSP tries to lock onto, but then discards after determining that they + * are noise. + * + * The optimum number of false alarms is between 5 and 50 per 200 TUs + * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. + * time listening, not transmitting). Driver must adjust sensitivity so that + * the ratio of actual false alarms to actual Rx time falls within this range. + * + * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each + * received beacon. These provide information to the driver to analyze the + * sensitivity. Don't analyze statistics that come in from scanning, or any + * other non-associated-network source. Pertinent statistics include: + * + * From "general" statistics (struct statistics_rx_non_phy): + * + * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) + * Measure of energy of desired signal. Used for establishing a level + * below which the device does not detect signals. + * + * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) + * Measure of background noise in silent period after beacon. + * + * channel_load + * uSecs of actual Rx time during beacon period (varies according to + * how much time was spent transmitting). + * + * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: + * + * false_alarm_cnt + * Signal locks abandoned early (before phy-level header). + * + * plcp_err + * Signal locks abandoned late (during phy-level header). + * + * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from + * beacon to beacon, i.e. each value is an accumulation of all errors + * before and including the latest beacon. Values will wrap around to 0 + * after counting up to 2^32 - 1. Driver must differentiate vs. + * previous beacon's values to determine # false alarms in the current + * beacon period. + * + * Total number of false alarms = false_alarms + plcp_errs + * + * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd + * (notice that the start points for OFDM are at or close to settings for + * maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 + * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 + * + * If actual rate of OFDM false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), reduce sensitivity + * by *adding* 1 to all 4 of the table entries above, up to the max for + * each entry. Conversely, if false alarm rate is too low (less than 5 + * for each 204.8 msecs listening), *subtract* 1 from each entry to + * increase sensitivity. + * + * For CCK sensitivity, keep track of the following: + * + * 1). 20-beacon history of maximum background noise, indicated by + * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the + * 3 receivers. For any given beacon, the "silence reference" is + * the maximum of last 60 samples (20 beacons * 3 receivers). + * + * 2). 10-beacon history of strongest signal level, as indicated + * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, + * i.e. the strength of the signal through the best receiver at the + * moment. These measurements are "upside down", with lower values + * for stronger signals, so max energy will be *minimum* value. + * + * Then for any given beacon, the driver must determine the *weakest* + * of the strongest signals; this is the minimum level that needs to be + * successfully detected, when using the best receiver at the moment. + * "Max cck energy" is the maximum (higher value means lower energy!) + * of the last 10 minima. Once this is determined, driver must add + * a little margin by adding "6" to it. + * + * 3). Number of consecutive beacon periods with too few false alarms. + * Reset this to 0 at the first beacon period that falls within the + * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). + * + * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd + * (notice that the start points for CCK are at maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 + * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 + * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 + * + * If actual rate of CCK false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), method for reducing + * sensitivity is: + * + * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * up to max 400. + * + * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, + * sensitivity has been reduced a significant amount; bring it up to + * a moderate 161. Otherwise, *add* 3, up to max 200. + * + * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, + * sensitivity has been reduced only a moderate or small amount; + * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, + * down to min 0. Otherwise (if gain has been significantly reduced), + * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. + * + * b) Save a snapshot of the "silence reference". + * + * If actual rate of CCK false alarms (+ plcp_errors) is too low + * (less than 5 for each 204.8 msecs listening), method for increasing + * sensitivity is used only if: + * + * 1a) Previous beacon did not have too many false alarms + * 1b) AND difference between previous "silence reference" and current + * "silence reference" (prev - current) is 2 or more, + * OR 2) 100 or more consecutive beacon periods have had rate of + * less than 5 false alarms per 204.8 milliseconds rx time. + * + * Method for increasing sensitivity: + * + * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, + * down to min 125. + * + * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * down to min 200. + * + * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. + * + * If actual rate of CCK false alarms (+ plcp_errors) is within good range + * (between 5 and 50 for each 204.8 msecs listening): + * + * 1) Save a snapshot of the silence reference. + * + * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), + * give some extra margin to energy threshold by *subtracting* 8 + * from value in HD_MIN_ENERGY_CCK_DET_INDEX. + * + * For all cases (too few, too many, good range), make sure that the CCK + * detection threshold (energy) is below the energy level for robust + * detection over the past 10 beacon periods, the "Max cck energy". + * Lower values mean higher energy; this means making sure that the value + * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". + * + */ + +/* + * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd) + */ +#define HD_TABLE_SIZE (11) /* number of entries */ +#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ +#define HD_MIN_ENERGY_OFDM_DET_INDEX (1) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6) +#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7) +#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) +#define HD_OFDM_ENERGY_TH_IN_INDEX (10) + +/* + * Additional table entries in enhance SENSITIVITY_CMD + */ +#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11) +#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12) +#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17) +#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19) +#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21) +#define HD_RESERVED (22) + +/* number of entries for enhanced tbl */ +#define ENHANCE_HD_TABLE_SIZE (23) + +/* number of additional entries for enhanced tbl */ +#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE) + +#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0) +#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0) +#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37) +#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) +#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99) + +#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1) +#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1) +#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40) +#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486) +#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45) +#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60) +#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476) +#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99) + + +/* Control field in struct iwl_sensitivity_cmd */ +#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) +#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) + +/** + * struct iwl_sensitivity_cmd + * @control: (1) updates working table, (0) updates default table + * @table: energy threshold values, use HD_* as index into table + * + * Always use "1" in "control" to update uCode's working table and DSP. + */ +struct iwl_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ +} __packed; + +/* + * + */ +struct iwl_enhance_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */ +} __packed; + + +/** + * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) + * + * This command sets the relative gains of agn device's 3 radio receiver chains. + * + * After the first association, driver should accumulate signal and noise + * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 + * beacons from the associated network (don't collect statistics that come + * in from scanning, or any other non-network source). + * + * DISCONNECTED ANTENNA: + * + * Driver should determine which antennas are actually connected, by comparing + * average beacon signal levels for the 3 Rx chains. Accumulate (add) the + * following values over 20 beacons, one accumulator for each of the chains + * a/b/c, from struct statistics_rx_non_phy: + * + * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the strongest signal from among a/b/c. Compare the other two to the + * strongest. If any signal is more than 15 dB (times 20, unless you + * divide the accumulated values by 20) below the strongest, the driver + * considers that antenna to be disconnected, and should not try to use that + * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, + * driver should declare the stronger one as connected, and attempt to use it + * (A and B are the only 2 Tx chains!). + * + * + * RX BALANCE: + * + * Driver should balance the 3 receivers (but just the ones that are connected + * to antennas, see above) for gain, by comparing the average signal levels + * detected during the silence after each beacon (background noise). + * Accumulate (add) the following values over 20 beacons, one accumulator for + * each of the chains a/b/c, from struct statistics_rx_non_phy: + * + * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the weakest background noise level from among a/b/c. This Rx chain + * will be the reference, with 0 gain adjustment. Attenuate other channels by + * finding noise difference: + * + * (accum_noise[i] - accum_noise[reference]) / 30 + * + * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. + * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the + * driver should limit the difference results to a range of 0-3 (0-4.5 dB), + * and set bit 2 to indicate "reduce gain". The value for the reference + * (weakest) chain should be "0". + * + * diff_gain_[abc] bit fields: + * 2: (1) reduce gain, (0) increase gain + * 1-0: amount of gain, units of 1.5 dB + */ + +/* Phy calibration command for series */ +enum { + IWL_PHY_CALIBRATE_DC_CMD = 8, + IWL_PHY_CALIBRATE_LO_CMD = 9, + IWL_PHY_CALIBRATE_TX_IQ_CMD = 11, + IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, + IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16, + IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, + IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18, +}; + +/* This enum defines the bitmap of various calibrations to enable in both + * init ucode and runtime ucode through CALIBRATION_CFG_CMD. + */ +enum iwl_ucode_calib_cfg { + IWL_CALIB_CFG_RX_BB_IDX = BIT(0), + IWL_CALIB_CFG_DC_IDX = BIT(1), + IWL_CALIB_CFG_LO_IDX = BIT(2), + IWL_CALIB_CFG_TX_IQ_IDX = BIT(3), + IWL_CALIB_CFG_RX_IQ_IDX = BIT(4), + IWL_CALIB_CFG_NOISE_IDX = BIT(5), + IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6), + IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7), + IWL_CALIB_CFG_PAPD_IDX = BIT(8), + IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9), + IWL_CALIB_CFG_TX_PWR_IDX = BIT(10), +}; + +#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ + IWL_CALIB_CFG_DC_IDX | \ + IWL_CALIB_CFG_LO_IDX | \ + IWL_CALIB_CFG_TX_IQ_IDX | \ + IWL_CALIB_CFG_RX_IQ_IDX | \ + IWL_CALIB_CFG_CRYSTAL_IDX) + +#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ + IWL_CALIB_CFG_DC_IDX | \ + IWL_CALIB_CFG_LO_IDX | \ + IWL_CALIB_CFG_TX_IQ_IDX | \ + IWL_CALIB_CFG_RX_IQ_IDX | \ + IWL_CALIB_CFG_TEMPERATURE_IDX | \ + IWL_CALIB_CFG_PAPD_IDX | \ + IWL_CALIB_CFG_TX_PWR_IDX | \ + IWL_CALIB_CFG_CRYSTAL_IDX) + +#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0)) + +struct iwl_calib_cfg_elmnt_s { + __le32 is_enable; + __le32 start; + __le32 send_res; + __le32 apply_res; + __le32 reserved; +} __packed; + +struct iwl_calib_cfg_status_s { + struct iwl_calib_cfg_elmnt_s once; + struct iwl_calib_cfg_elmnt_s perd; + __le32 flags; +} __packed; + +struct iwl_calib_cfg_cmd { + struct iwl_calib_cfg_status_s ucd_calib_cfg; + struct iwl_calib_cfg_status_s drv_calib_cfg; + __le32 reserved1; +} __packed; + +struct iwl_calib_hdr { + u8 op_code; + u8 first_group; + u8 groups_num; + u8 data_valid; +} __packed; + +struct iwl_calib_cmd { + struct iwl_calib_hdr hdr; + u8 data[0]; +} __packed; + +struct iwl_calib_xtal_freq_cmd { + struct iwl_calib_hdr hdr; + u8 cap_pin1; + u8 cap_pin2; + u8 pad[2]; +} __packed; + +#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700) +struct iwl_calib_temperature_offset_cmd { + struct iwl_calib_hdr hdr; + __le16 radio_sensor_offset; + __le16 reserved; +} __packed; + +struct iwl_calib_temperature_offset_v2_cmd { + struct iwl_calib_hdr hdr; + __le16 radio_sensor_offset_high; + __le16 radio_sensor_offset_low; + __le16 burntVoltageRef; + __le16 reserved; +} __packed; + +/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ +struct iwl_calib_chain_noise_reset_cmd { + struct iwl_calib_hdr hdr; + u8 data[0]; +}; + +/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */ +struct iwl_calib_chain_noise_gain_cmd { + struct iwl_calib_hdr hdr; + u8 delta_gain_1; + u8 delta_gain_2; + u8 pad[2]; +} __packed; + +/****************************************************************************** + * (12) + * Miscellaneous Commands: + * + *****************************************************************************/ + +/* + * LEDs Command & Response + * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) + * + * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), + * this command turns it on or off, or sets up a periodic blinking cycle. + */ +struct iwl_led_cmd { + __le32 interval; /* "interval" in uSec */ + u8 id; /* 1: Activity, 2: Link, 3: Tech */ + u8 off; /* # intervals off while blinking; + * "0", with >0 "on" value, turns LED on */ + u8 on; /* # intervals on while blinking; + * "0", regardless of "off", turns LED off */ + u8 reserved; +} __packed; + +/* + * station priority table entries + * also used as potential "events" value for both + * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD + */ + +/* + * COEX events entry flag masks + * RP - Requested Priority + * WP - Win Medium Priority: priority assigned when the contention has been won + */ +#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1) +#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2) +#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4) + +#define COEX_CU_UNASSOC_IDLE_RP 4 +#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4 +#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4 +#define COEX_CU_CALIBRATION_RP 4 +#define COEX_CU_PERIODIC_CALIBRATION_RP 4 +#define COEX_CU_CONNECTION_ESTAB_RP 4 +#define COEX_CU_ASSOCIATED_IDLE_RP 4 +#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4 +#define COEX_CU_ASSOC_AUTO_SCAN_RP 4 +#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4 +#define COEX_CU_RF_ON_RP 6 +#define COEX_CU_RF_OFF_RP 4 +#define COEX_CU_STAND_ALONE_DEBUG_RP 6 +#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4 +#define COEX_CU_RSRVD1_RP 4 +#define COEX_CU_RSRVD2_RP 4 + +#define COEX_CU_UNASSOC_IDLE_WP 3 +#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3 +#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3 +#define COEX_CU_CALIBRATION_WP 3 +#define COEX_CU_PERIODIC_CALIBRATION_WP 3 +#define COEX_CU_CONNECTION_ESTAB_WP 3 +#define COEX_CU_ASSOCIATED_IDLE_WP 3 +#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3 +#define COEX_CU_ASSOC_AUTO_SCAN_WP 3 +#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3 +#define COEX_CU_RF_ON_WP 3 +#define COEX_CU_RF_OFF_WP 3 +#define COEX_CU_STAND_ALONE_DEBUG_WP 6 +#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3 +#define COEX_CU_RSRVD1_WP 3 +#define COEX_CU_RSRVD2_WP 3 + +#define COEX_UNASSOC_IDLE_FLAGS 0 +#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_UNASSOC_AUTO_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_CALIBRATION_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_PERIODIC_CALIBRATION_FLAGS 0 +/* + * COEX_CONNECTION_ESTAB: + * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. + */ +#define COEX_CONNECTION_ESTAB_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) +#define COEX_ASSOCIATED_IDLE_FLAGS 0 +#define COEX_ASSOC_MANUAL_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_ASSOC_AUTO_SCAN_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 +#define COEX_RF_ON_FLAGS 0 +#define COEX_RF_OFF_FLAGS 0 +#define COEX_STAND_ALONE_DEBUG_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) +#define COEX_IPAN_ASSOC_LEVEL_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) +#define COEX_RSRVD1_FLAGS 0 +#define COEX_RSRVD2_FLAGS 0 +/* + * COEX_CU_RF_ON is the event wrapping all radio ownership. + * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. + */ +#define COEX_CU_RF_ON_FLAGS \ + (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ + COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ + COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) + + +enum { + /* un-association part */ + COEX_UNASSOC_IDLE = 0, + COEX_UNASSOC_MANUAL_SCAN = 1, + COEX_UNASSOC_AUTO_SCAN = 2, + /* calibration */ + COEX_CALIBRATION = 3, + COEX_PERIODIC_CALIBRATION = 4, + /* connection */ + COEX_CONNECTION_ESTAB = 5, + /* association part */ + COEX_ASSOCIATED_IDLE = 6, + COEX_ASSOC_MANUAL_SCAN = 7, + COEX_ASSOC_AUTO_SCAN = 8, + COEX_ASSOC_ACTIVE_LEVEL = 9, + /* RF ON/OFF */ + COEX_RF_ON = 10, + COEX_RF_OFF = 11, + COEX_STAND_ALONE_DEBUG = 12, + /* IPAN */ + COEX_IPAN_ASSOC_LEVEL = 13, + /* reserved */ + COEX_RSRVD1 = 14, + COEX_RSRVD2 = 15, + COEX_NUM_OF_EVENTS = 16 +}; + +/* + * Coexistence WIFI/WIMAX Command + * COEX_PRIORITY_TABLE_CMD = 0x5a + * + */ +struct iwl_wimax_coex_event_entry { + u8 request_prio; + u8 win_medium_prio; + u8 reserved; + u8 flags; +} __packed; + +/* COEX flag masks */ + +/* Station table is valid */ +#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1) +/* UnMask wake up src at unassociated sleep */ +#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4) +/* UnMask wake up src at associated sleep */ +#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8) +/* Enable CoEx feature. */ +#define COEX_FLAGS_COEX_ENABLE_MSK (0x80) + +struct iwl_wimax_coex_cmd { + u8 flags; + u8 reserved[3]; + struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS]; +} __packed; + +/* + * Coexistence MEDIUM NOTIFICATION + * COEX_MEDIUM_NOTIFICATION = 0x5b + * + * notification from uCode to host to indicate medium changes + * + */ +/* + * status field + * bit 0 - 2: medium status + * bit 3: medium change indication + * bit 4 - 31: reserved + */ +/* status option values, (0 - 2 bits) */ +#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */ +#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */ +#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */ +#define COEX_MEDIUM_MSK (0x7) + +/* send notification status (1 bit) */ +#define COEX_MEDIUM_CHANGED (0x8) +#define COEX_MEDIUM_CHANGED_MSK (0x8) +#define COEX_MEDIUM_SHIFT (3) + +struct iwl_coex_medium_notification { + __le32 status; + __le32 events; +} __packed; + +/* + * Coexistence EVENT Command + * COEX_EVENT_CMD = 0x5c + * + * send from host to uCode for coex event request. + */ +/* flags options */ +#define COEX_EVENT_REQUEST_MSK (0x1) + +struct iwl_coex_event_cmd { + u8 flags; + u8 event; + __le16 reserved; +} __packed; + +struct iwl_coex_event_resp { + __le32 status; +} __packed; + + +/****************************************************************************** + * Bluetooth Coexistence commands + * + *****************************************************************************/ + +/* + * BT Status notification + * REPLY_BT_COEX_PROFILE_NOTIF = 0xce + */ +enum iwl_bt_coex_profile_traffic_load { + IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, + IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, + IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, + IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, +/* + * There are no more even though below is a u8, the + * indication from the BT device only has two bits. + */ +}; + +#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1 +#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2 + +/* BT UART message - Share Part (BT -> WiFi) */ +#define BT_UART_MSG_FRAME1MSGTYPE_POS (0) +#define BT_UART_MSG_FRAME1MSGTYPE_MSK \ + (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS) +#define BT_UART_MSG_FRAME1SSN_POS (3) +#define BT_UART_MSG_FRAME1SSN_MSK \ + (0x3 << BT_UART_MSG_FRAME1SSN_POS) +#define BT_UART_MSG_FRAME1UPDATEREQ_POS (5) +#define BT_UART_MSG_FRAME1UPDATEREQ_MSK \ + (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS) +#define BT_UART_MSG_FRAME1RESERVED_POS (6) +#define BT_UART_MSG_FRAME1RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME1RESERVED_POS) + +#define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0) +#define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \ + (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS) +#define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2) +#define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \ + (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS) +#define BT_UART_MSG_FRAME2CHLSEQN_POS (4) +#define BT_UART_MSG_FRAME2CHLSEQN_MSK \ + (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS) +#define BT_UART_MSG_FRAME2INBAND_POS (5) +#define BT_UART_MSG_FRAME2INBAND_MSK \ + (0x1 << BT_UART_MSG_FRAME2INBAND_POS) +#define BT_UART_MSG_FRAME2RESERVED_POS (6) +#define BT_UART_MSG_FRAME2RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME2RESERVED_POS) + +#define BT_UART_MSG_FRAME3SCOESCO_POS (0) +#define BT_UART_MSG_FRAME3SCOESCO_MSK \ + (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS) +#define BT_UART_MSG_FRAME3SNIFF_POS (1) +#define BT_UART_MSG_FRAME3SNIFF_MSK \ + (0x1 << BT_UART_MSG_FRAME3SNIFF_POS) +#define BT_UART_MSG_FRAME3A2DP_POS (2) +#define BT_UART_MSG_FRAME3A2DP_MSK \ + (0x1 << BT_UART_MSG_FRAME3A2DP_POS) +#define BT_UART_MSG_FRAME3ACL_POS (3) +#define BT_UART_MSG_FRAME3ACL_MSK \ + (0x1 << BT_UART_MSG_FRAME3ACL_POS) +#define BT_UART_MSG_FRAME3MASTER_POS (4) +#define BT_UART_MSG_FRAME3MASTER_MSK \ + (0x1 << BT_UART_MSG_FRAME3MASTER_POS) +#define BT_UART_MSG_FRAME3OBEX_POS (5) +#define BT_UART_MSG_FRAME3OBEX_MSK \ + (0x1 << BT_UART_MSG_FRAME3OBEX_POS) +#define BT_UART_MSG_FRAME3RESERVED_POS (6) +#define BT_UART_MSG_FRAME3RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME3RESERVED_POS) + +#define BT_UART_MSG_FRAME4IDLEDURATION_POS (0) +#define BT_UART_MSG_FRAME4IDLEDURATION_MSK \ + (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS) +#define BT_UART_MSG_FRAME4RESERVED_POS (6) +#define BT_UART_MSG_FRAME4RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME4RESERVED_POS) + +#define BT_UART_MSG_FRAME5TXACTIVITY_POS (0) +#define BT_UART_MSG_FRAME5TXACTIVITY_MSK \ + (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS) +#define BT_UART_MSG_FRAME5RXACTIVITY_POS (2) +#define BT_UART_MSG_FRAME5RXACTIVITY_MSK \ + (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS) +#define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4) +#define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \ + (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS) +#define BT_UART_MSG_FRAME5RESERVED_POS (6) +#define BT_UART_MSG_FRAME5RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME5RESERVED_POS) + +#define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0) +#define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \ + (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS) +#define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5) +#define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \ + (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS) +#define BT_UART_MSG_FRAME6RESERVED_POS (6) +#define BT_UART_MSG_FRAME6RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME6RESERVED_POS) + +#define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0) +#define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \ + (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS) +#define BT_UART_MSG_FRAME7PAGE_POS (3) +#define BT_UART_MSG_FRAME7PAGE_MSK \ + (0x1 << BT_UART_MSG_FRAME7PAGE_POS) +#define BT_UART_MSG_FRAME7INQUIRY_POS (4) +#define BT_UART_MSG_FRAME7INQUIRY_MSK \ + (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS) +#define BT_UART_MSG_FRAME7CONNECTABLE_POS (5) +#define BT_UART_MSG_FRAME7CONNECTABLE_MSK \ + (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS) +#define BT_UART_MSG_FRAME7RESERVED_POS (6) +#define BT_UART_MSG_FRAME7RESERVED_MSK \ + (0x3 << BT_UART_MSG_FRAME7RESERVED_POS) + +/* BT Session Activity 2 UART message (BT -> WiFi) */ +#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5) +#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \ + (0x1< + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "iwl-debug.h" +#include "iwl-io.h" +#include "dev.h" +#include "agn.h" + +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, priv, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_u32(#name, mode, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FUNC(name) \ +static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_WRITE_FUNC(name) \ +static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos); + + +#define DEBUGFS_READ_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +static ssize_t iwl_dbgfs_sram_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + u32 val = 0; + char *buf; + ssize_t ret; + int i = 0; + bool device_format = false; + int offset = 0; + int len = 0; + int pos = 0; + int sram; + struct iwl_priv *priv = file->private_data; + const struct fw_img *img; + size_t bufsz; + + /* default is to dump the entire data segment */ + if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { + priv->dbgfs_sram_offset = 0x800000; + if (!priv->ucode_loaded) + return -EINVAL; + img = &priv->fw->img[priv->cur_ucode]; + priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } + len = priv->dbgfs_sram_len; + + if (len == -4) { + device_format = true; + len = 4; + } + + bufsz = 50 + len * 4; + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", + len); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", + priv->dbgfs_sram_offset); + + /* adjust sram address since reads are only on even u32 boundaries */ + offset = priv->dbgfs_sram_offset & 0x3; + sram = priv->dbgfs_sram_offset & ~0x3; + + /* read the first u32 from sram */ + val = iwl_read_targ_mem(priv->trans, sram); + + for (; len; len--) { + /* put the address at the start of every line */ + if (i == 0) + pos += scnprintf(buf + pos, bufsz - pos, + "%08X: ", sram + offset); + + if (device_format) + pos += scnprintf(buf + pos, bufsz - pos, + "%02x", (val >> (8 * (3 - offset))) & 0xff); + else + pos += scnprintf(buf + pos, bufsz - pos, + "%02x ", (val >> (8 * offset)) & 0xff); + + /* if all bytes processed, read the next u32 from sram */ + if (++offset == 4) { + sram += 4; + offset = 0; + val = iwl_read_targ_mem(priv->trans, sram); + } + + /* put in extra spaces and split lines for human readability */ + if (++i == 16) { + i = 0; + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } else if (!(i & 7)) { + pos += scnprintf(buf + pos, bufsz - pos, " "); + } else if (!(i & 3)) { + pos += scnprintf(buf + pos, bufsz - pos, " "); + } + } + if (i) + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_sram_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[64]; + int buf_size; + u32 offset, len; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x,%x", &offset, &len) == 2) { + priv->dbgfs_sram_offset = offset; + priv->dbgfs_sram_len = len; + } else if (sscanf(buf, "%x", &offset) == 1) { + priv->dbgfs_sram_offset = offset; + priv->dbgfs_sram_len = -4; + } else { + priv->dbgfs_sram_offset = 0; + priv->dbgfs_sram_len = 0; + } + + return count; +} + +static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN]; + + if (!priv->wowlan_sram) + return -ENODATA; + + return simple_read_from_buffer(user_buf, count, ppos, + priv->wowlan_sram, + img->sec[IWL_UCODE_SECTION_DATA].len); +} +static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_station_entry *station; + struct iwl_tid_data *tid_data; + char *buf; + int i, j, pos = 0; + ssize_t ret; + /* Add 30 for initial string */ + const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations); + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", + priv->num_stations); + + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + station = &priv->stations[i]; + if (!station->used) + continue; + pos += scnprintf(buf + pos, bufsz - pos, + "station %d - addr: %pM, flags: %#x\n", + i, station->sta.sta.addr, + station->sta.station_flags_msk); + pos += scnprintf(buf + pos, bufsz - pos, + "TID seqno next_rclmd " + "rate_n_flags state txq\n"); + + for (j = 0; j < IWL_MAX_TID_COUNT; j++) { + tid_data = &priv->tid_data[i][j]; + pos += scnprintf(buf + pos, bufsz - pos, + "%d: 0x%.4x 0x%.4x 0x%.8x " + "%d %.2d", + j, tid_data->seq_number, + tid_data->next_reclaimed, + tid_data->agg.rate_n_flags, + tid_data->agg.state, + tid_data->agg.txq_id); + + if (tid_data->agg.wait_for_ba) + pos += scnprintf(buf + pos, bufsz - pos, + " - waitforba"); + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_nvm_read(struct file *file, + char __user *user_buf, + size_t count, + loff_t *ppos) +{ + ssize_t ret; + struct iwl_priv *priv = file->private_data; + int pos = 0, ofs = 0, buf_size = 0; + const u8 *ptr; + char *buf; + u16 eeprom_ver; + size_t eeprom_len = priv->cfg->base_params->eeprom_size; + buf_size = 4 * eeprom_len + 256; + + if (eeprom_len % 16) + return -ENODATA; + + ptr = priv->eeprom; + if (!ptr) + return -ENOMEM; + + /* 4 characters for byte 0xYY */ + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s, " + "version: 0x%x\n", + (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + ? "OTP" : "EEPROM", eeprom_ver); + for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { + pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, + buf_size - pos, 0); + pos += strlen(buf + pos); + if (buf_size - pos > 0) + buf[pos++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct ieee80211_channel *channels = NULL; + const struct ieee80211_supported_band *supp_band = NULL; + int pos = 0, i, bufsz = PAGE_SIZE; + char *buf; + ssize_t ret; + + if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 2.4GHz band 802.11bg):\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_PASSIVE_SCAN ? + "passive only" : "active/passive"); + } + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 5.2GHz band (802.11a)\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_PASSIVE_SCAN ? + "passive only" : "active/passive"); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[512]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", + test_bit(STATUS_RF_KILL_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", + test_bit(STATUS_CT_KILL, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", + test_bit(STATUS_ALIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", + test_bit(STATUS_READY, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n", + test_bit(STATUS_GEO_CONFIGURED, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", + test_bit(STATUS_EXIT_PENDING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", + test_bit(STATUS_STATISTICS, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", + test_bit(STATUS_SCANNING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", + test_bit(STATUS_SCAN_ABORTING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", + test_bit(STATUS_SCAN_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", + test_bit(STATUS_POWER_PMI, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", + test_bit(STATUS_FW_ERROR, &priv->status)); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < REPLY_MAX; cnt++) { + if (priv->rx_handlers_stats[cnt] > 0) + pos += scnprintf(buf + pos, bufsz - pos, + "\tRx handler[%36s]:\t\t %u\n", + iwl_dvm_get_cmd_string(cnt), + priv->rx_handlers_stats[cnt]); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + + char buf[8]; + int buf_size; + u32 reset_flag; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + memset(&priv->rx_handlers_stats[0], 0, + sizeof(priv->rx_handlers_stats)); + + return count; +} + +static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_rxon_context *ctx; + int pos = 0, i; + char buf[256 * NUM_IWL_RXON_CTX]; + const size_t bufsz = sizeof(buf); + + for_each_context(priv, ctx) { + pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n", + ctx->ctxid); + for (i = 0; i < AC_NUM; i++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tcw_min\tcw_max\taifsn\ttxop\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "AC[%d]\t%u\t%u\t%u\t%u\n", i, + ctx->qos_data.def_qos_parm.ac[i].cw_min, + ctx->qos_data.def_qos_parm.ac[i].cw_max, + ctx->qos_data.def_qos_parm.ac[i].aifsn, + ctx->qos_data.def_qos_parm.ac[i].edca_txop); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling Mode: %s\n", + tt->advanced_tt ? "Advance" : "Legacy"); + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling State: %d\n", + tt->state); + if (tt->advanced_tt) { + restriction = tt->restriction + tt->state; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx mode: %d\n", + restriction->tx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "Rx mode: %d\n", + restriction->rx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "HT mode: %d\n", + restriction->is_ht); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int ht40; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &ht40) != 1) + return -EFAULT; + if (!iwl_is_any_associated(priv)) + priv->disable_ht40 = ht40 ? true : false; + else + return -EINVAL; + + return count; +} + +static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "11n 40MHz Mode: %s\n", + priv->disable_ht40 ? "Disabled" : "Enabled"); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_temperature_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + + +static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int value; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + /* + * Our users expect 0 to be "CAM", but 0 isn't actually + * valid here. However, let's not confuse them and present + * IWL_POWER_INDEX_1 as "1", not "0". + */ + if (value == 0) + return -EINVAL; + else if (value > 0) + value -= 1; + + if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) + return -EINVAL; + + if (!iwl_is_ready_rf(priv)) + return -EAGAIN; + + priv->power_data.debug_sleep_level_override = value; + + mutex_lock(&priv->mutex); + iwl_power_update_mode(priv, true); + mutex_unlock(&priv->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[10]; + int pos, value; + const size_t bufsz = sizeof(buf); + + /* see the write function */ + value = priv->power_data.debug_sleep_level_override; + if (value >= 0) + value += 1; + + pos = scnprintf(buf, bufsz, "%d\n", value); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[200]; + int pos = 0, i; + const size_t bufsz = sizeof(buf); + struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; + + pos += scnprintf(buf + pos, bufsz - pos, + "flags: %#.2x\n", le16_to_cpu(cmd->flags)); + pos += scnprintf(buf + pos, bufsz - pos, + "RX/TX timeout: %d/%d usec\n", + le32_to_cpu(cmd->rx_data_timeout), + le32_to_cpu(cmd->tx_data_timeout)); + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "sleep_interval[%d]: %d\n", i, + le32_to_cpu(cmd->sleep_interval[i])); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +DEBUGFS_READ_WRITE_FILE_OPS(sram); +DEBUGFS_READ_FILE_OPS(wowlan_sram); +DEBUGFS_READ_FILE_OPS(nvm); +DEBUGFS_READ_FILE_OPS(stations); +DEBUGFS_READ_FILE_OPS(channels); +DEBUGFS_READ_FILE_OPS(status); +DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers); +DEBUGFS_READ_FILE_OPS(qos); +DEBUGFS_READ_FILE_OPS(thermal_throttling); +DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); +DEBUGFS_READ_FILE_OPS(temperature); +DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); +DEBUGFS_READ_FILE_OPS(current_sleep_command); + +static const char *fmt_value = " %-30s %10u\n"; +static const char *fmt_hex = " %-30s 0x%02X\n"; +static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; +static const char *fmt_header = + "%-32s current cumulative delta max\n"; + +static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) +{ + int p = 0; + u32 flag; + + lockdep_assert_held(&priv->statistics.lock); + + flag = le32_to_cpu(priv->statistics.flag); + + p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); + if (flag & UCODE_STATISTICS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", + (flag & UCODE_STATISTICS_FREQUENCY_MSK) + ? "2.4 GHz" : "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", + (flag & UCODE_STATISTICS_NARROW_BAND_MSK) + ? "enabled" : "disabled"); + + return p; +} + +static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_rx_phy) * 40 + + sizeof(struct statistics_rx_non_phy) * 40 + + sizeof(struct statistics_rx_ht_phy) * 40 + 400; + ssize_t ret; + struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; + struct statistics_rx_non_phy *general, *accum_general; + struct statistics_rx_non_phy *delta_general, *max_general; + struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + spin_lock_bh(&priv->statistics.lock); + ofdm = &priv->statistics.rx_ofdm; + cck = &priv->statistics.rx_cck; + general = &priv->statistics.rx_non_phy; + ht = &priv->statistics.rx_ofdm_ht; + accum_ofdm = &priv->accum_stats.rx_ofdm; + accum_cck = &priv->accum_stats.rx_cck; + accum_general = &priv->accum_stats.rx_non_phy; + accum_ht = &priv->accum_stats.rx_ofdm_ht; + delta_ofdm = &priv->delta_stats.rx_ofdm; + delta_cck = &priv->delta_stats.rx_cck; + delta_general = &priv->delta_stats.rx_non_phy; + delta_ht = &priv->delta_stats.rx_ofdm_ht; + max_ofdm = &priv->max_delta_stats.rx_ofdm; + max_cck = &priv->max_delta_stats.rx_cck; + max_general = &priv->max_delta_stats.rx_non_phy; + max_ht = &priv->max_delta_stats.rx_ofdm_ht; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - OFDM:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_cnt:", + le32_to_cpu(ofdm->ina_cnt), + accum_ofdm->ina_cnt, + delta_ofdm->ina_cnt, max_ofdm->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_cnt:", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, + delta_ofdm->fina_cnt, max_ofdm->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, + delta_ofdm->plcp_err, max_ofdm->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, + delta_ofdm->crc32_err, max_ofdm->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(ofdm->overrun_err), + accum_ofdm->overrun_err, delta_ofdm->overrun_err, + max_ofdm->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(ofdm->early_overrun_err), + accum_ofdm->early_overrun_err, + delta_ofdm->early_overrun_err, + max_ofdm->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(ofdm->crc32_good), + accum_ofdm->crc32_good, delta_ofdm->crc32_good, + max_ofdm->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "false_alarm_cnt:", + le32_to_cpu(ofdm->false_alarm_cnt), + accum_ofdm->false_alarm_cnt, + delta_ofdm->false_alarm_cnt, + max_ofdm->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(ofdm->fina_sync_err_cnt), + accum_ofdm->fina_sync_err_cnt, + delta_ofdm->fina_sync_err_cnt, + max_ofdm->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sfd_timeout:", + le32_to_cpu(ofdm->sfd_timeout), + accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, + max_ofdm->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_timeout:", + le32_to_cpu(ofdm->fina_timeout), + accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, + max_ofdm->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unresponded_rts:", + le32_to_cpu(ofdm->unresponded_rts), + accum_ofdm->unresponded_rts, + delta_ofdm->unresponded_rts, + max_ofdm->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(ofdm->rxe_frame_limit_overrun), + accum_ofdm->rxe_frame_limit_overrun, + delta_ofdm->rxe_frame_limit_overrun, + max_ofdm->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ack_cnt:", + le32_to_cpu(ofdm->sent_ack_cnt), + accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, + max_ofdm->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_cts_cnt:", + le32_to_cpu(ofdm->sent_cts_cnt), + accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, + max_ofdm->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(ofdm->sent_ba_rsp_cnt), + accum_ofdm->sent_ba_rsp_cnt, + delta_ofdm->sent_ba_rsp_cnt, + max_ofdm->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_self_kill:", + le32_to_cpu(ofdm->dsp_self_kill), + accum_ofdm->dsp_self_kill, + delta_ofdm->dsp_self_kill, + max_ofdm->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(ofdm->mh_format_err), + accum_ofdm->mh_format_err, + delta_ofdm->mh_format_err, + max_ofdm->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "re_acq_main_rssi_sum:", + le32_to_cpu(ofdm->re_acq_main_rssi_sum), + accum_ofdm->re_acq_main_rssi_sum, + delta_ofdm->re_acq_main_rssi_sum, + max_ofdm->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - CCK:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_cnt:", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, + delta_cck->ina_cnt, max_cck->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_cnt:", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, + delta_cck->fina_cnt, max_cck->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, + delta_cck->plcp_err, max_cck->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, + delta_cck->crc32_err, max_cck->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(cck->overrun_err), + accum_cck->overrun_err, delta_cck->overrun_err, + max_cck->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(cck->early_overrun_err), + accum_cck->early_overrun_err, + delta_cck->early_overrun_err, + max_cck->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, + delta_cck->crc32_good, max_cck->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "false_alarm_cnt:", + le32_to_cpu(cck->false_alarm_cnt), + accum_cck->false_alarm_cnt, + delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(cck->fina_sync_err_cnt), + accum_cck->fina_sync_err_cnt, + delta_cck->fina_sync_err_cnt, + max_cck->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sfd_timeout:", + le32_to_cpu(cck->sfd_timeout), + accum_cck->sfd_timeout, delta_cck->sfd_timeout, + max_cck->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_timeout:", + le32_to_cpu(cck->fina_timeout), + accum_cck->fina_timeout, delta_cck->fina_timeout, + max_cck->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unresponded_rts:", + le32_to_cpu(cck->unresponded_rts), + accum_cck->unresponded_rts, delta_cck->unresponded_rts, + max_cck->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(cck->rxe_frame_limit_overrun), + accum_cck->rxe_frame_limit_overrun, + delta_cck->rxe_frame_limit_overrun, + max_cck->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ack_cnt:", + le32_to_cpu(cck->sent_ack_cnt), + accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, + max_cck->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_cts_cnt:", + le32_to_cpu(cck->sent_cts_cnt), + accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, + max_cck->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(cck->sent_ba_rsp_cnt), + accum_cck->sent_ba_rsp_cnt, + delta_cck->sent_ba_rsp_cnt, + max_cck->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_self_kill:", + le32_to_cpu(cck->dsp_self_kill), + accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, + max_cck->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(cck->mh_format_err), + accum_cck->mh_format_err, delta_cck->mh_format_err, + max_cck->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "re_acq_main_rssi_sum:", + le32_to_cpu(cck->re_acq_main_rssi_sum), + accum_cck->re_acq_main_rssi_sum, + delta_cck->re_acq_main_rssi_sum, + max_cck->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - GENERAL:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bogus_cts:", + le32_to_cpu(general->bogus_cts), + accum_general->bogus_cts, delta_general->bogus_cts, + max_general->bogus_cts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bogus_ack:", + le32_to_cpu(general->bogus_ack), + accum_general->bogus_ack, delta_general->bogus_ack, + max_general->bogus_ack); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "non_bssid_frames:", + le32_to_cpu(general->non_bssid_frames), + accum_general->non_bssid_frames, + delta_general->non_bssid_frames, + max_general->non_bssid_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "filtered_frames:", + le32_to_cpu(general->filtered_frames), + accum_general->filtered_frames, + delta_general->filtered_frames, + max_general->filtered_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "non_channel_beacons:", + le32_to_cpu(general->non_channel_beacons), + accum_general->non_channel_beacons, + delta_general->non_channel_beacons, + max_general->non_channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "channel_beacons:", + le32_to_cpu(general->channel_beacons), + accum_general->channel_beacons, + delta_general->channel_beacons, + max_general->channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "num_missed_bcon:", + le32_to_cpu(general->num_missed_bcon), + accum_general->num_missed_bcon, + delta_general->num_missed_bcon, + max_general->num_missed_bcon); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "adc_rx_saturation_time:", + le32_to_cpu(general->adc_rx_saturation_time), + accum_general->adc_rx_saturation_time, + delta_general->adc_rx_saturation_time, + max_general->adc_rx_saturation_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_detect_search_tm:", + le32_to_cpu(general->ina_detection_search_time), + accum_general->ina_detection_search_time, + delta_general->ina_detection_search_time, + max_general->ina_detection_search_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_a:", + le32_to_cpu(general->beacon_silence_rssi_a), + accum_general->beacon_silence_rssi_a, + delta_general->beacon_silence_rssi_a, + max_general->beacon_silence_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_b:", + le32_to_cpu(general->beacon_silence_rssi_b), + accum_general->beacon_silence_rssi_b, + delta_general->beacon_silence_rssi_b, + max_general->beacon_silence_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_c:", + le32_to_cpu(general->beacon_silence_rssi_c), + accum_general->beacon_silence_rssi_c, + delta_general->beacon_silence_rssi_c, + max_general->beacon_silence_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "interference_data_flag:", + le32_to_cpu(general->interference_data_flag), + accum_general->interference_data_flag, + delta_general->interference_data_flag, + max_general->interference_data_flag); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "channel_load:", + le32_to_cpu(general->channel_load), + accum_general->channel_load, + delta_general->channel_load, + max_general->channel_load); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_false_alarms:", + le32_to_cpu(general->dsp_false_alarms), + accum_general->dsp_false_alarms, + delta_general->dsp_false_alarms, + max_general->dsp_false_alarms); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_a:", + le32_to_cpu(general->beacon_rssi_a), + accum_general->beacon_rssi_a, + delta_general->beacon_rssi_a, + max_general->beacon_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_b:", + le32_to_cpu(general->beacon_rssi_b), + accum_general->beacon_rssi_b, + delta_general->beacon_rssi_b, + max_general->beacon_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_c:", + le32_to_cpu(general->beacon_rssi_c), + accum_general->beacon_rssi_c, + delta_general->beacon_rssi_c, + max_general->beacon_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_a:", + le32_to_cpu(general->beacon_energy_a), + accum_general->beacon_energy_a, + delta_general->beacon_energy_a, + max_general->beacon_energy_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_b:", + le32_to_cpu(general->beacon_energy_b), + accum_general->beacon_energy_b, + delta_general->beacon_energy_b, + max_general->beacon_energy_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_c:", + le32_to_cpu(general->beacon_energy_c), + accum_general->beacon_energy_c, + delta_general->beacon_energy_c, + max_general->beacon_energy_c); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - OFDM_HT:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, + delta_ht->plcp_err, max_ht->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, + delta_ht->overrun_err, max_ht->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(ht->early_overrun_err), + accum_ht->early_overrun_err, + delta_ht->early_overrun_err, + max_ht->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, + delta_ht->crc32_good, max_ht->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, + delta_ht->crc32_err, max_ht->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(ht->mh_format_err), + accum_ht->mh_format_err, + delta_ht->mh_format_err, max_ht->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_crc32_good:", + le32_to_cpu(ht->agg_crc32_good), + accum_ht->agg_crc32_good, + delta_ht->agg_crc32_good, max_ht->agg_crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_mpdu_cnt:", + le32_to_cpu(ht->agg_mpdu_cnt), + accum_ht->agg_mpdu_cnt, + delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_cnt:", + le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, + delta_ht->agg_cnt, max_ht->agg_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unsupport_mcs:", + le32_to_cpu(ht->unsupport_mcs), + accum_ht->unsupport_mcs, + delta_ht->unsupport_mcs, max_ht->unsupport_mcs); + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_tx) * 48) + 250; + ssize_t ret; + struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + spin_lock_bh(&priv->statistics.lock); + + tx = &priv->statistics.tx; + accum_tx = &priv->accum_stats.tx; + delta_tx = &priv->delta_stats.tx; + max_tx = &priv->max_delta_stats.tx; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Tx:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "preamble:", + le32_to_cpu(tx->preamble_cnt), + accum_tx->preamble_cnt, + delta_tx->preamble_cnt, max_tx->preamble_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rx_detected_cnt:", + le32_to_cpu(tx->rx_detected_cnt), + accum_tx->rx_detected_cnt, + delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bt_prio_defer_cnt:", + le32_to_cpu(tx->bt_prio_defer_cnt), + accum_tx->bt_prio_defer_cnt, + delta_tx->bt_prio_defer_cnt, + max_tx->bt_prio_defer_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bt_prio_kill_cnt:", + le32_to_cpu(tx->bt_prio_kill_cnt), + accum_tx->bt_prio_kill_cnt, + delta_tx->bt_prio_kill_cnt, + max_tx->bt_prio_kill_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "few_bytes_cnt:", + le32_to_cpu(tx->few_bytes_cnt), + accum_tx->few_bytes_cnt, + delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "cts_timeout:", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, + delta_tx->cts_timeout, max_tx->cts_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ack_timeout:", + le32_to_cpu(tx->ack_timeout), + accum_tx->ack_timeout, + delta_tx->ack_timeout, max_tx->ack_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "expected_ack_cnt:", + le32_to_cpu(tx->expected_ack_cnt), + accum_tx->expected_ack_cnt, + delta_tx->expected_ack_cnt, + max_tx->expected_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "actual_ack_cnt:", + le32_to_cpu(tx->actual_ack_cnt), + accum_tx->actual_ack_cnt, + delta_tx->actual_ack_cnt, + max_tx->actual_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dump_msdu_cnt:", + le32_to_cpu(tx->dump_msdu_cnt), + accum_tx->dump_msdu_cnt, + delta_tx->dump_msdu_cnt, + max_tx->dump_msdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "abort_nxt_frame_mismatch:", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), + accum_tx->burst_abort_next_frame_mismatch_cnt, + delta_tx->burst_abort_next_frame_mismatch_cnt, + max_tx->burst_abort_next_frame_mismatch_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "abort_missing_nxt_frame:", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), + accum_tx->burst_abort_missing_next_frame_cnt, + delta_tx->burst_abort_missing_next_frame_cnt, + max_tx->burst_abort_missing_next_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "cts_timeout_collision:", + le32_to_cpu(tx->cts_timeout_collision), + accum_tx->cts_timeout_collision, + delta_tx->cts_timeout_collision, + max_tx->cts_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ack_ba_timeout_collision:", + le32_to_cpu(tx->ack_or_ba_timeout_collision), + accum_tx->ack_or_ba_timeout_collision, + delta_tx->ack_or_ba_timeout_collision, + max_tx->ack_or_ba_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg ba_timeout:", + le32_to_cpu(tx->agg.ba_timeout), + accum_tx->agg.ba_timeout, + delta_tx->agg.ba_timeout, + max_tx->agg.ba_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg ba_resched_frames:", + le32_to_cpu(tx->agg.ba_reschedule_frames), + accum_tx->agg.ba_reschedule_frames, + delta_tx->agg.ba_reschedule_frames, + max_tx->agg.ba_reschedule_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_agg_frame:", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), + accum_tx->agg.scd_query_agg_frame_cnt, + delta_tx->agg.scd_query_agg_frame_cnt, + max_tx->agg.scd_query_agg_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_no_agg:", + le32_to_cpu(tx->agg.scd_query_no_agg), + accum_tx->agg.scd_query_no_agg, + delta_tx->agg.scd_query_no_agg, + max_tx->agg.scd_query_no_agg); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_agg:", + le32_to_cpu(tx->agg.scd_query_agg), + accum_tx->agg.scd_query_agg, + delta_tx->agg.scd_query_agg, + max_tx->agg.scd_query_agg); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_mismatch:", + le32_to_cpu(tx->agg.scd_query_mismatch), + accum_tx->agg.scd_query_mismatch, + delta_tx->agg.scd_query_mismatch, + max_tx->agg.scd_query_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg frame_not_ready:", + le32_to_cpu(tx->agg.frame_not_ready), + accum_tx->agg.frame_not_ready, + delta_tx->agg.frame_not_ready, + max_tx->agg.frame_not_ready); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg underrun:", + le32_to_cpu(tx->agg.underrun), + accum_tx->agg.underrun, + delta_tx->agg.underrun, max_tx->agg.underrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg bt_prio_kill:", + le32_to_cpu(tx->agg.bt_prio_kill), + accum_tx->agg.bt_prio_kill, + delta_tx->agg.bt_prio_kill, + max_tx->agg.bt_prio_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg rx_ba_rsp_cnt:", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt), + accum_tx->agg.rx_ba_rsp_cnt, + delta_tx->agg.rx_ba_rsp_cnt, + max_tx->agg.rx_ba_rsp_cnt); + + if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { + pos += scnprintf(buf + pos, bufsz - pos, + "tx power: (1/2 dB step)\n"); + if ((priv->hw_params.valid_tx_ant & ANT_A) && + tx->tx_power.ant_a) + pos += scnprintf(buf + pos, bufsz - pos, + fmt_hex, "antenna A:", + tx->tx_power.ant_a); + if ((priv->hw_params.valid_tx_ant & ANT_B) && + tx->tx_power.ant_b) + pos += scnprintf(buf + pos, bufsz - pos, + fmt_hex, "antenna B:", + tx->tx_power.ant_b); + if ((priv->hw_params.valid_tx_ant & ANT_C) && + tx->tx_power.ant_c) + pos += scnprintf(buf + pos, bufsz - pos, + fmt_hex, "antenna C:", + tx->tx_power.ant_c); + } + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_general) * 10 + 300; + ssize_t ret; + struct statistics_general_common *general, *accum_general; + struct statistics_general_common *delta_general, *max_general; + struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; + struct statistics_div *div, *accum_div, *delta_div, *max_div; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + + spin_lock_bh(&priv->statistics.lock); + + general = &priv->statistics.common; + dbg = &priv->statistics.common.dbg; + div = &priv->statistics.common.div; + accum_general = &priv->accum_stats.common; + accum_dbg = &priv->accum_stats.common.dbg; + accum_div = &priv->accum_stats.common.div; + delta_general = &priv->delta_stats.common; + max_general = &priv->max_delta_stats.common; + delta_dbg = &priv->delta_stats.common.dbg; + max_dbg = &priv->max_delta_stats.common.dbg; + delta_div = &priv->delta_stats.common.div; + max_div = &priv->max_delta_stats.common.div; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_General:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "temperature:", + le32_to_cpu(general->temperature)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "temperature_m:", + le32_to_cpu(general->temperature_m)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "ttl_timestamp:", + le32_to_cpu(general->ttl_timestamp)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "burst_check:", + le32_to_cpu(dbg->burst_check), + accum_dbg->burst_check, + delta_dbg->burst_check, max_dbg->burst_check); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "burst_count:", + le32_to_cpu(dbg->burst_count), + accum_dbg->burst_count, + delta_dbg->burst_count, max_dbg->burst_count); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "wait_for_silence_timeout_count:", + le32_to_cpu(dbg->wait_for_silence_timeout_cnt), + accum_dbg->wait_for_silence_timeout_cnt, + delta_dbg->wait_for_silence_timeout_cnt, + max_dbg->wait_for_silence_timeout_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sleep_time:", + le32_to_cpu(general->sleep_time), + accum_general->sleep_time, + delta_general->sleep_time, max_general->sleep_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "slots_out:", + le32_to_cpu(general->slots_out), + accum_general->slots_out, + delta_general->slots_out, max_general->slots_out); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "slots_idle:", + le32_to_cpu(general->slots_idle), + accum_general->slots_idle, + delta_general->slots_idle, max_general->slots_idle); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "tx_on_a:", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, + delta_div->tx_on_a, max_div->tx_on_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "tx_on_b:", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, + delta_div->tx_on_b, max_div->tx_on_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "exec_time:", + le32_to_cpu(div->exec_time), accum_div->exec_time, + delta_div->exec_time, max_div->exec_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "probe_time:", + le32_to_cpu(div->probe_time), accum_div->probe_time, + delta_div->probe_time, max_div->probe_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rx_enable_counter:", + le32_to_cpu(general->rx_enable_counter), + accum_general->rx_enable_counter, + delta_general->rx_enable_counter, + max_general->rx_enable_counter); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "num_of_sos_states:", + le32_to_cpu(general->num_of_sos_states), + accum_general->num_of_sos_states, + delta_general->num_of_sos_states, + max_general->num_of_sos_states); + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200; + ssize_t ret; + struct statistics_bt_activity *bt, *accum_bt; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + if (!priv->bt_enable_flag) + return -EINVAL; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, CMD_SYNC, false); + mutex_unlock(&priv->mutex); + + if (ret) + return -EAGAIN; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + + spin_lock_bh(&priv->statistics.lock); + + bt = &priv->statistics.bt_activity; + accum_bt = &priv->accum_stats.bt_activity; + + pos += iwl_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "\t\t\tcurrent\t\t\taccumulative\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_tx_req_cnt), + accum_bt->hi_priority_tx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_tx_denied_cnt), + accum_bt->hi_priority_tx_denied_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_tx_req_cnt), + accum_bt->lo_priority_tx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_tx_denied_cnt), + accum_bt->lo_priority_tx_denied_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_rx_req_cnt), + accum_bt->hi_priority_rx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->hi_priority_rx_denied_cnt), + accum_bt->hi_priority_rx_denied_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_rx_req_cnt), + accum_bt->lo_priority_rx_req_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n", + le32_to_cpu(bt->lo_priority_rx_denied_cnt), + accum_bt->lo_priority_rx_denied_cnt); + + pos += scnprintf(buf + pos, bufsz - pos, + "(rx)num_bt_kills:\t\t%u\t\t\t%u\n", + le32_to_cpu(priv->statistics.num_bt_kills), + priv->statistics.accum_num_bt_kills); + + spin_unlock_bh(&priv->statistics.lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) + + (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200; + ssize_t ret; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY), + priv->reply_tx_stats.pp_delay); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES), + priv->reply_tx_stats.pp_few_bytes); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO), + priv->reply_tx_stats.pp_bt_prio); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD), + priv->reply_tx_stats.pp_quiet_period); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK), + priv->reply_tx_stats.pp_calc_ttak); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_tx_fail_reason( + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY), + priv->reply_tx_stats.int_crossed_retry); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT), + priv->reply_tx_stats.short_limit); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT), + priv->reply_tx_stats.long_limit); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN), + priv->reply_tx_stats.fifo_underrun); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW), + priv->reply_tx_stats.drain_flow); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH), + priv->reply_tx_stats.rfkill_flush); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE), + priv->reply_tx_stats.life_expire); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS), + priv->reply_tx_stats.dest_ps); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED), + priv->reply_tx_stats.host_abort); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY), + priv->reply_tx_stats.pp_delay); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID), + priv->reply_tx_stats.sta_invalid); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED), + priv->reply_tx_stats.frag_drop); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE), + priv->reply_tx_stats.tid_disable); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED), + priv->reply_tx_stats.fifo_flush); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_tx_fail_reason( + TX_STATUS_FAIL_INSUFFICIENT_CF_POLL), + priv->reply_tx_stats.insuff_cf_poll); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX), + priv->reply_tx_stats.fail_hw_drop); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_tx_fail_reason( + TX_STATUS_FAIL_NO_BEACON_ON_RADAR), + priv->reply_tx_stats.sta_color_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", + priv->reply_tx_stats.unknown); + + pos += scnprintf(buf + pos, bufsz - pos, + "\nStatistics_Agg_TX_Error:\n"); + + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK), + priv->reply_agg_tx_stats.underrun); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK), + priv->reply_agg_tx_stats.bt_prio); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK), + priv->reply_agg_tx_stats.few_bytes); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK), + priv->reply_agg_tx_stats.abort); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_LAST_SENT_TTL_MSK), + priv->reply_agg_tx_stats.last_sent_ttl); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK), + priv->reply_agg_tx_stats.last_sent_try); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_LAST_SENT_BT_KILL_MSK), + priv->reply_agg_tx_stats.last_sent_bt_kill); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK), + priv->reply_agg_tx_stats.scd_query); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", + iwl_get_agg_tx_fail_reason( + AGG_TX_STATE_TEST_BAD_CRC32_MSK), + priv->reply_agg_tx_stats.bad_crc32); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK), + priv->reply_agg_tx_stats.response); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK), + priv->reply_agg_tx_stats.dump_tx); + pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", + iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK), + priv->reply_agg_tx_stats.delay_tx); + pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", + priv->reply_agg_tx_stats.unknown); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; + ssize_t ret; + struct iwl_sensitivity_data *data; + + data = &priv->sensitivity_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", + data->auto_corr_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc:\t\t %u\n", + data->auto_corr_ofdm_mrc); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", + data->auto_corr_ofdm_x1); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc_x1:\t\t %u\n", + data->auto_corr_ofdm_mrc_x1); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", + data->auto_corr_cck); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", + data->auto_corr_cck_mrc); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_ofdm:\t\t %u\n", + data->last_bad_plcp_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", + data->last_fa_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_cck:\t\t %u\n", + data->last_bad_plcp_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", + data->last_fa_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", + data->nrg_curr_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", + data->nrg_prev_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); + for (cnt = 0; cnt < 10; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_value[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); + for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_silence_rssi[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", + data->nrg_silence_ref); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", + data->nrg_energy_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", + data->nrg_silence_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", + data->nrg_th_cck); + pos += scnprintf(buf + pos, bufsz - pos, + "nrg_auto_corr_silence_diff:\t %u\n", + data->nrg_auto_corr_silence_diff); + pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", + data->num_in_cck_no_fa); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", + data->nrg_th_ofdm); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + + +static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; + ssize_t ret; + struct iwl_chain_noise_data *data; + + data = &priv->chain_noise_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", + data->active_chains); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", + data->chain_noise_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", + data->chain_noise_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", + data->chain_noise_c); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", + data->chain_signal_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", + data->chain_signal_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", + data->chain_signal_c); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", + data->beacon_count); + + pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->disconn_array[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->delta_gain_code[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", + data->radio_write); + pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", + data->state); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[60]; + int pos = 0; + const size_t bufsz = sizeof(buf); + u32 pwrsave_status; + + pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) & + CSR_GP_REG_POWER_SAVE_STATUS_MSK; + + pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : + (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : + (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : + "error"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int clear; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &clear) != 1) + return -EFAULT; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + iwl_send_statistics_request(priv, CMD_SYNC, true); + mutex_unlock(&priv->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[128]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", + priv->event_log.ucode_trace ? "On" : "Off"); + pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", + priv->event_log.non_wraps_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", + priv->event_log.wraps_once_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", + priv->event_log.wraps_more_count); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int trace; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &trace) != 1) + return -EFAULT; + + if (trace) { + priv->event_log.ucode_trace = true; + if (iwl_is_alive(priv)) { + /* start collecting data now */ + mod_timer(&priv->ucode_trace, jiffies); + } + } else { + priv->event_log.ucode_trace = false; + del_timer_sync(&priv->ucode_trace); + } + + return count; +} + +static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", + priv->missed_beacon_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int missed; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &missed) != 1) + return -EINVAL; + + if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || + missed > IWL_MISSED_BEACON_THRESHOLD_MAX) + priv->missed_beacon_threshold = + IWL_MISSED_BEACON_THRESHOLD_DEF; + else + priv->missed_beacon_threshold = missed; + + return count; +} + +static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%u\n", + priv->plcp_delta_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int plcp; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &plcp) != 1) + return -EINVAL; + if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || + (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) + priv->plcp_delta_threshold = + IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; + else + priv->plcp_delta_threshold = plcp; + return count; +} + +static ssize_t iwl_dbgfs_rf_reset_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[300]; + const size_t bufsz = sizeof(buf); + struct iwl_rf_reset *rf_reset = &priv->rf_reset; + + pos += scnprintf(buf + pos, bufsz - pos, + "RF reset statistics\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request: %d\n", + rf_reset->reset_request_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request success: %d\n", + rf_reset->reset_success_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request reject: %d\n", + rf_reset->reset_reject_count); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_rf_reset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int ret; + + ret = iwl_force_rf_reset(priv, true); + return ret ? ret : count; +} + +static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int flush; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &flush) != 1) + return -EINVAL; + + if (iwl_is_rfkill(priv)) + return -EFAULT; + + iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); + + return count; +} + +static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char buf[200]; + const size_t bufsz = sizeof(buf); + + if (!priv->bt_enable_flag) { + pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n"); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); + } + pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n", + priv->bt_enable_flag); + pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n", + priv->bt_full_concurrent ? "full concurrency" : "3-wire"); + pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, " + "last traffic notif: %d\n", + priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load); + pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, " + "kill_ack_mask: %x, kill_cts_mask: %x\n", + priv->bt_ch_announce, priv->kill_ack_mask, + priv->kill_cts_mask); + + pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: "); + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n"); + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + pos += scnprintf(buf + pos, bufsz - pos, "High\n"); + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + pos += scnprintf(buf + pos, bufsz - pos, "Low\n"); + break; + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + default: + pos += scnprintf(buf + pos, bufsz - pos, "None\n"); + break; + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + + int pos = 0; + char buf[40]; + const size_t bufsz = sizeof(buf); + + if (priv->cfg->ht_params) + pos += scnprintf(buf + pos, bufsz - pos, + "use %s for aggregation\n", + (priv->hw_params.use_rts_for_aggregation) ? + "rts/cts" : "cts-to-self"); + else + pos += scnprintf(buf + pos, bufsz - pos, "N/A"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int rts; + + if (!priv->cfg->ht_params) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &rts) != 1) + return -EINVAL; + if (rts) + priv->hw_params.use_rts_for_aggregation = true; + else + priv->hw_params.use_rts_for_aggregation = false; + return count; +} + +static int iwl_cmd_echo_test(struct iwl_priv *priv) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = REPLY_ECHO, + .len = { 0 }, + .flags = CMD_SYNC, + }; + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) + IWL_ERR(priv, "echo testing fail: 0X%x\n", ret); + else + IWL_DEBUG_INFO(priv, "echo testing pass\n"); + return ret; +} + +static ssize_t iwl_dbgfs_echo_test_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + iwl_cmd_echo_test(priv); + return count; +} + +static ssize_t iwl_dbgfs_log_event_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -ENOMEM; + + ret = pos = iwl_dump_nic_event_log(priv, true, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + } + return ret; +} + +static ssize_t iwl_dbgfs_log_event_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 event_log_flag; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &event_log_flag) != 1) + return -EFAULT; + if (event_log_flag == 1) + iwl_dump_nic_event_log(priv, true, NULL, false); + + return count; +} + +static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[120]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "Sensitivity calibrations %s\n", + (priv->calib_disabled & + IWL_SENSITIVITY_CALIB_DISABLED) ? + "DISABLED" : "ENABLED"); + pos += scnprintf(buf + pos, bufsz - pos, + "Chain noise calibrations %s\n", + (priv->calib_disabled & + IWL_CHAIN_NOISE_CALIB_DISABLED) ? + "DISABLED" : "ENABLED"); + pos += scnprintf(buf + pos, bufsz - pos, + "Tx power calibrations %s\n", + (priv->calib_disabled & + IWL_TX_POWER_CALIB_DISABLED) ? + "DISABLED" : "ENABLED"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + u32 calib_disabled; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &calib_disabled) != 1) + return -EFAULT; + + priv->calib_disabled = calib_disabled; + + return count; +} + +DEBUGFS_READ_FILE_OPS(ucode_rx_stats); +DEBUGFS_READ_FILE_OPS(ucode_tx_stats); +DEBUGFS_READ_FILE_OPS(ucode_general_stats); +DEBUGFS_READ_FILE_OPS(sensitivity); +DEBUGFS_READ_FILE_OPS(chain_noise); +DEBUGFS_READ_FILE_OPS(power_save_status); +DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); +DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); +DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); +DEBUGFS_READ_WRITE_FILE_OPS(rf_reset); +DEBUGFS_READ_FILE_OPS(rxon_flags); +DEBUGFS_READ_FILE_OPS(rxon_filter_flags); +DEBUGFS_WRITE_FILE_OPS(txfifo_flush); +DEBUGFS_READ_FILE_OPS(ucode_bt_stats); +DEBUGFS_READ_FILE_OPS(bt_traffic); +DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); +DEBUGFS_READ_FILE_OPS(reply_tx_error); +DEBUGFS_WRITE_FILE_OPS(echo_test); +DEBUGFS_READ_WRITE_FILE_OPS(log_event); +DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled); + +/* + * Create the debugfs files and directories + * + */ +int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) +{ + struct dentry *phyd = priv->hw->wiphy->debugfsdir; + struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; + + dir_drv = debugfs_create_dir(name, phyd); + if (!dir_drv) + return -ENOMEM; + + priv->debugfs_dir = dir_drv; + + dir_data = debugfs_create_dir("data", dir_drv); + if (!dir_data) + goto err; + dir_rf = debugfs_create_dir("rf", dir_drv); + if (!dir_rf) + goto err; + dir_debug = debugfs_create_dir("debug", dir_drv); + if (!dir_debug) + goto err; + + DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR); + + DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR); + + if (iwl_advanced_bt_coexist(priv)) + DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR); + + /* Calibrations disabled/enabled status*/ + DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR); + + if (iwl_trans_dbgfs_register(priv->trans, dir_debug)) + goto err; + return 0; + +err: + IWL_ERR(priv, "Can't create the debugfs directory\n"); + iwl_dbgfs_unregister(priv); + return -ENOMEM; +} + +/** + * Remove the debugfs files and directories + * + */ +void iwl_dbgfs_unregister(struct iwl_priv *priv) +{ + if (!priv->debugfs_dir) + return; + + debugfs_remove_recursive(priv->debugfs_dir); + priv->debugfs_dir = NULL; +} diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h new file mode 100644 index 000000000000..2880a0a3da68 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -0,0 +1,1073 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +/* + * Please use this file (dev.h) for driver implementation definitions. + * Please use commands.h for uCode API definitions. + */ + +#ifndef __iwl_dev_h__ +#define __iwl_dev_h__ + +#include +#include +#include +#include +#include +#include + +#include "iwl-fw.h" +#include "iwl-csr.h" +#include "iwl-debug.h" +#include "iwl-agn-hw.h" +#include "iwl-op-mode.h" +#include "iwl-notif-wait.h" +#include "iwl-trans.h" + +#include "eeprom.h" +#include "led.h" +#include "power.h" +#include "rs.h" +#include "tt.h" + +/* CT-KILL constants */ +#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ +#define CT_KILL_THRESHOLD 114 /* in Celsius */ +#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ + +/* Default noise level to report when noise measurement is not available. + * This may be because we're: + * 1) Not associated no beacon statistics being sent to driver) + * 2) Scanning (noise measurement does not apply to associated channel) + * Use default noise value of -127 ... this is below the range of measurable + * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user. + * Also, -127 works better than 0 when averaging frames with/without + * noise info (e.g. averaging might be done in app); measured dBm values are + * always negative ... using a negative value as the default keeps all + * averages within an s8's (used in some apps) range of negative values. */ +#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127) + +/* + * RTS threshold here is total size [2347] minus 4 FCS bytes + * Per spec: + * a value of 0 means RTS on all data/management packets + * a value > max MSDU size means no RTS + * else RTS for data/management frames where MPDU is larger + * than RTS value. + */ +#define DEFAULT_RTS_THRESHOLD 2347U +#define MIN_RTS_THRESHOLD 0U +#define MAX_RTS_THRESHOLD 2347U +#define MAX_MSDU_SIZE 2304U +#define MAX_MPDU_SIZE 2346U +#define DEFAULT_BEACON_INTERVAL 200U +#define DEFAULT_SHORT_RETRY_LIMIT 7U +#define DEFAULT_LONG_RETRY_LIMIT 4U + +#define IWL_NUM_SCAN_RATES (2) + +/* + * One for each channel, holds all channel setup data + * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant + * with one another! + */ +struct iwl_channel_info { + struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */ + struct iwl_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for + * HT40 channel */ + + u8 channel; /* channel number */ + u8 flags; /* flags copied from EEPROM */ + s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ + s8 min_power; /* always 0 */ + s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ + + u8 group_index; /* 0-4, maps channel to group1/2/3/4/5 */ + u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */ + enum ieee80211_band band; + + /* HT40 channel info */ + s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + u8 ht40_flags; /* flags copied from EEPROM */ + u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ +}; + +/* + * Minimum number of queues. MAX_NUM is defined in hw specific files. + * Set the minimum to accommodate + * - 4 standard TX queues + * - the command queue + * - 4 PAN TX queues + * - the PAN multicast queue, and + * - the AUX (TX during scan dwell) queue. + */ +#define IWL_MIN_NUM_QUEUES 11 + +/* + * Command queue depends on iPAN support. + */ +#define IWL_DEFAULT_CMD_QUEUE_NUM 4 +#define IWL_IPAN_CMD_QUEUE_NUM 9 + +#define IEEE80211_DATA_LEN 2304 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +#define IWL_SUPPORTED_RATES_IE_LEN 8 + +#define IWL_INVALID_RATE 0xFF +#define IWL_INVALID_VALUE -1 + +union iwl_ht_rate_supp { + u16 rates; + struct { + u8 siso_rate; + u8 mimo_rate; + }; +}; + +#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) +#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) +#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) +#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) +#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K +#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K +#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K + +/* + * Maximal MPDU density for TX aggregation + * 4 - 2us density + * 5 - 4us density + * 6 - 8us density + * 7 - 16us density + */ +#define CFG_HT_MPDU_DENSITY_2USEC (0x4) +#define CFG_HT_MPDU_DENSITY_4USEC (0x5) +#define CFG_HT_MPDU_DENSITY_8USEC (0x6) +#define CFG_HT_MPDU_DENSITY_16USEC (0x7) +#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC +#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC +#define CFG_HT_MPDU_DENSITY_MIN (0x1) + +struct iwl_ht_config { + bool single_chain_sufficient; + enum ieee80211_smps_mode smps; /* current smps mode */ +}; + +/* QoS structures */ +struct iwl_qos_info { + int qos_active; + struct iwl_qosparam_cmd def_qos_parm; +}; + +/** + * enum iwl_agg_state + * + * The state machine of the BA agreement establishment / tear down. + * These states relate to a specific RA / TID. + * + * @IWL_AGG_OFF: aggregation is not used + * @IWL_AGG_STARTING: aggregation are starting (between start and oper) + * @IWL_AGG_ON: aggregation session is up + * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + */ +enum iwl_agg_state { + IWL_AGG_OFF = 0, + IWL_AGG_STARTING, + IWL_AGG_ON, + IWL_EMPTYING_HW_QUEUE_ADDBA, + IWL_EMPTYING_HW_QUEUE_DELBA, +}; + +/** + * struct iwl_ht_agg - aggregation state machine + + * This structs holds the states for the BA agreement establishment and tear + * down. It also holds the state during the BA session itself. This struct is + * duplicated for each RA / TID. + + * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the + * Tx response (REPLY_TX), and the block ack notification + * (REPLY_COMPRESSED_BA). + * @state: state of the BA agreement establishment / tear down. + * @txq_id: Tx queue used by the BA session + * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or + * the first packet to be sent in legacy HW queue in Tx AGG stop flow. + * Basically when next_reclaimed reaches ssn, we can tell mac80211 that + * we are ready to finish the Tx AGG stop / start flow. + * @wait_for_ba: Expect block-ack before next Tx reply + */ +struct iwl_ht_agg { + u32 rate_n_flags; + enum iwl_agg_state state; + u16 txq_id; + u16 ssn; + bool wait_for_ba; +}; + +/** + * struct iwl_tid_data - one for each RA / TID + + * This structs holds the states for each RA / TID. + + * @seq_number: the next WiFi sequence number to use + * @next_reclaimed: the WiFi sequence number of the next packet to be acked. + * This is basically (last acked packet++). + * @agg: aggregation state machine + */ +struct iwl_tid_data { + u16 seq_number; + u16 next_reclaimed; + struct iwl_ht_agg agg; +}; + +/* + * Structure should be accessed with sta_lock held. When station addition + * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only + * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock + * held. + */ +struct iwl_station_entry { + struct iwl_addsta_cmd sta; + u8 used, ctxid; + struct iwl_link_quality_cmd *lq; +}; + +/* + * iwl_station_priv: Driver's private station information + * + * When mac80211 creates a station it reserves some space (hw->sta_data_size) + * in the structure for use by driver. This structure is places in that + * space. + */ +struct iwl_station_priv { + struct iwl_rxon_context *ctx; + struct iwl_lq_sta lq_sta; + atomic_t pending_frames; + bool client; + bool asleep; + u8 max_agg_bufsize; + u8 sta_id; +}; + +/** + * struct iwl_vif_priv - driver's private per-interface information + * + * When mac80211 allocates a virtual interface, it can allocate + * space for us to put data into. + */ +struct iwl_vif_priv { + struct iwl_rxon_context *ctx; + u8 ibss_bssid_sta_id; +}; + +struct iwl_sensitivity_ranges { + u16 min_nrg_cck; + + u16 nrg_th_cck; + u16 nrg_th_ofdm; + + u16 auto_corr_min_ofdm; + u16 auto_corr_min_ofdm_mrc; + u16 auto_corr_min_ofdm_x1; + u16 auto_corr_min_ofdm_mrc_x1; + + u16 auto_corr_max_ofdm; + u16 auto_corr_max_ofdm_mrc; + u16 auto_corr_max_ofdm_x1; + u16 auto_corr_max_ofdm_mrc_x1; + + u16 auto_corr_max_cck; + u16 auto_corr_max_cck_mrc; + u16 auto_corr_min_cck; + u16 auto_corr_min_cck_mrc; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + + +#define KELVIN_TO_CELSIUS(x) ((x)-273) +#define CELSIUS_TO_KELVIN(x) ((x)+273) + + +/****************************************************************************** + * + * Functions implemented in core module which are forward declared here + * for use by iwl-[4-5].c + * + * NOTE: The implementation of these functions are not hardware specific + * which is why they are in the core module files. + * + * Naming convention -- + * iwl_ <-- Is part of iwlwifi + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * + ****************************************************************************/ +extern void iwl_update_chain_flags(struct iwl_priv *priv); +extern const u8 iwl_bcast_addr[ETH_ALEN]; + +#define IWL_OPERATION_MODE_AUTO 0 +#define IWL_OPERATION_MODE_HT_ONLY 1 +#define IWL_OPERATION_MODE_MIXED 2 +#define IWL_OPERATION_MODE_20MHZ 3 + +#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 + +/* Sensitivity and chain noise calibration */ +#define INITIALIZATION_VALUE 0xFFFF +#define IWL_CAL_NUM_BEACONS 16 +#define MAXIMUM_ALLOWED_PATHLOSS 15 + +#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 + +#define MAX_FA_OFDM 50 +#define MIN_FA_OFDM 5 +#define MAX_FA_CCK 50 +#define MIN_FA_CCK 5 + +#define AUTO_CORR_STEP_OFDM 1 + +#define AUTO_CORR_STEP_CCK 3 +#define AUTO_CORR_MAX_TH_CCK 160 + +#define NRG_DIFF 2 +#define NRG_STEP_CCK 2 +#define NRG_MARGIN 8 +#define MAX_NUMBER_CCK_NO_FA 100 + +#define AUTO_CORR_CCK_MIN_VAL_DEF (125) + +#define CHAIN_A 0 +#define CHAIN_B 1 +#define CHAIN_C 2 +#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 +#define ALL_BAND_FILTER 0xFF00 +#define IN_BAND_FILTER 0xFF +#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF + +#define NRG_NUM_PREV_STAT_L 20 +#define NUM_RX_CHAINS 3 + +enum iwlagn_false_alarm_state { + IWL_FA_TOO_MANY = 0, + IWL_FA_TOO_FEW = 1, + IWL_FA_GOOD_RANGE = 2, +}; + +enum iwlagn_chain_noise_state { + IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ + IWL_CHAIN_NOISE_ACCUMULATE, + IWL_CHAIN_NOISE_CALIBRATED, + IWL_CHAIN_NOISE_DONE, +}; + +/* Sensitivity calib data */ +struct iwl_sensitivity_data { + u32 auto_corr_ofdm; + u32 auto_corr_ofdm_mrc; + u32 auto_corr_ofdm_x1; + u32 auto_corr_ofdm_mrc_x1; + u32 auto_corr_cck; + u32 auto_corr_cck_mrc; + + u32 last_bad_plcp_cnt_ofdm; + u32 last_fa_cnt_ofdm; + u32 last_bad_plcp_cnt_cck; + u32 last_fa_cnt_cck; + + u32 nrg_curr_state; + u32 nrg_prev_state; + u32 nrg_value[10]; + u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; + u32 nrg_silence_ref; + u32 nrg_energy_idx; + u32 nrg_silence_idx; + u32 nrg_th_cck; + s32 nrg_auto_corr_silence_diff; + u32 num_in_cck_no_fa; + u32 nrg_th_ofdm; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + +/* Chain noise (differential Rx gain) calib data */ +struct iwl_chain_noise_data { + u32 active_chains; + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_signal_a; + u32 chain_signal_b; + u32 chain_signal_c; + u16 beacon_count; + u8 disconn_array[NUM_RX_CHAINS]; + u8 delta_gain_code[NUM_RX_CHAINS]; + u8 radio_write; + u8 state; +}; + +enum { + MEASUREMENT_READY = (1 << 0), + MEASUREMENT_ACTIVE = (1 << 1), +}; + +enum iwl_nvm_type { + NVM_DEVICE_TYPE_EEPROM = 0, + NVM_DEVICE_TYPE_OTP, +}; + +/* + * Two types of OTP memory access modes + * IWL_OTP_ACCESS_ABSOLUTE - absolute address mode, + * based on physical memory addressing + * IWL_OTP_ACCESS_RELATIVE - relative address mode, + * based on logical memory addressing + */ +enum iwl_access_mode { + IWL_OTP_ACCESS_ABSOLUTE, + IWL_OTP_ACCESS_RELATIVE, +}; + +/* reply_tx_statistics (for _agn devices) */ +struct reply_tx_error_statistics { + u32 pp_delay; + u32 pp_few_bytes; + u32 pp_bt_prio; + u32 pp_quiet_period; + u32 pp_calc_ttak; + u32 int_crossed_retry; + u32 short_limit; + u32 long_limit; + u32 fifo_underrun; + u32 drain_flow; + u32 rfkill_flush; + u32 life_expire; + u32 dest_ps; + u32 host_abort; + u32 bt_retry; + u32 sta_invalid; + u32 frag_drop; + u32 tid_disable; + u32 fifo_flush; + u32 insuff_cf_poll; + u32 fail_hw_drop; + u32 sta_color_mismatch; + u32 unknown; +}; + +/* reply_agg_tx_statistics (for _agn devices) */ +struct reply_agg_tx_error_statistics { + u32 underrun; + u32 bt_prio; + u32 few_bytes; + u32 abort; + u32 last_sent_ttl; + u32 last_sent_try; + u32 last_sent_bt_kill; + u32 scd_query; + u32 bad_crc32; + u32 response; + u32 dump_tx; + u32 delay_tx; + u32 unknown; +}; + +/* + * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds + * to perform continuous uCode event logging operation if enabled + */ +#define UCODE_TRACE_PERIOD (10) + +/* + * iwl_event_log: current uCode event log position + * + * @ucode_trace: enable/disable ucode continuous trace timer + * @num_wraps: how many times the event buffer wraps + * @next_entry: the entry just before the next one that uCode would fill + * @non_wraps_count: counter for no wrap detected when dump ucode events + * @wraps_once_count: counter for wrap once detected when dump ucode events + * @wraps_more_count: counter for wrap more than once detected + * when dump ucode events + */ +struct iwl_event_log { + bool ucode_trace; + u32 num_wraps; + u32 next_entry; + int non_wraps_count; + int wraps_once_count; + int wraps_more_count; +}; + +#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) + +/* BT Antenna Coupling Threshold (dB) */ +#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) + +/* Firmware reload counter and Timestamp */ +#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */ +#define IWL_MAX_CONTINUE_RELOAD_CNT 4 + + +struct iwl_rf_reset { + int reset_request_count; + int reset_success_count; + int reset_reject_count; + unsigned long last_reset_jiffies; +}; + +enum iwl_rxon_context_id { + IWL_RXON_CTX_BSS, + IWL_RXON_CTX_PAN, + + NUM_IWL_RXON_CTX +}; + +/* extend beacon time format bit shifting */ +/* + * for _agn devices + * bits 31:22 - extended + * bits 21:0 - interval + */ +#define IWLAGN_EXT_BEACON_TIME_POS 22 + +struct iwl_rxon_context { + struct ieee80211_vif *vif; + + u8 mcast_queue; + u8 ac_to_queue[IEEE80211_NUM_ACS]; + u8 ac_to_fifo[IEEE80211_NUM_ACS]; + + /* + * We could use the vif to indicate active, but we + * also need it to be active during disabling when + * we already removed the vif for type setting. + */ + bool always_active, is_active; + + bool ht_need_multiple_chains; + + enum iwl_rxon_context_id ctxid; + + u32 interface_modes, exclusive_interface_modes; + u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype; + + /* + * We declare this const so it can only be + * changed via explicit cast within the + * routines that actually update the physical + * hardware. + */ + const struct iwl_rxon_cmd active; + struct iwl_rxon_cmd staging; + + struct iwl_rxon_time_cmd timing; + + struct iwl_qos_info qos_data; + + u8 bcast_sta_id, ap_sta_id; + + u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd; + u8 qos_cmd; + u8 wep_key_cmd; + + struct iwl_wep_key wep_keys[WEP_KEYS_MAX]; + u8 key_mapping_keys; + + __le32 station_flags; + + int beacon_int; + + struct { + bool non_gf_sta_present; + u8 protection; + bool enabled, is_40mhz; + u8 extension_chan_offset; + } ht; +}; + +enum iwl_scan_type { + IWL_SCAN_NORMAL, + IWL_SCAN_RADIO_RESET, + IWL_SCAN_ROC, +}; + +/** + * struct iwl_hw_params + * + * Holds the module parameters + * + * @tx_chains_num: Number of TX chains + * @rx_chains_num: Number of RX chains + * @valid_tx_ant: usable antennas for TX + * @valid_rx_ant: usable antennas for RX + * @ht40_channel: is 40MHz width possible: BIT(IEEE80211_BAND_XXX) + * @sku: sku read from EEPROM + * @ct_kill_threshold: temperature threshold - in hw dependent unit + * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit + * relevant for 1000, 6000 and up + * @struct iwl_sensitivity_ranges: range of sensitivity values + * @use_rts_for_aggregation: use rts/cts protection for HT traffic + */ +struct iwl_hw_params { + u8 tx_chains_num; + u8 rx_chains_num; + u8 valid_tx_ant; + u8 valid_rx_ant; + u8 ht40_channel; + bool use_rts_for_aggregation; + u16 sku; + u32 ct_kill_threshold; + u32 ct_kill_exit_threshold; + + const struct iwl_sensitivity_ranges *sens; +}; + +struct iwl_lib_ops { + /* set hw dependent parameters */ + void (*set_hw_params)(struct iwl_priv *priv); + int (*set_channel_switch)(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch); + /* device specific configuration */ + void (*nic_config)(struct iwl_priv *priv); + + /* eeprom operations (as defined in eeprom.h) */ + struct iwl_eeprom_ops eeprom_ops; + + /* temperature */ + void (*temperature)(struct iwl_priv *priv); +}; + +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE +struct iwl_testmode_trace { + u32 buff_size; + u32 total_size; + u32 num_chunks; + u8 *cpu_addr; + u8 *trace_addr; + dma_addr_t dma_addr; + bool trace_enabled; +}; +struct iwl_testmode_mem { + u32 buff_size; + u32 num_chunks; + u8 *buff_addr; + bool read_in_progress; +}; +#endif + +struct iwl_wipan_noa_data { + struct rcu_head rcu_head; + u32 length; + u8 data[]; +}; + +/* Calibration disabling bit mask */ +enum { + IWL_CALIB_ENABLE_ALL = 0, + + IWL_SENSITIVITY_CALIB_DISABLED = BIT(0), + IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1), + IWL_TX_POWER_CALIB_DISABLED = BIT(2), + + IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF, +}; + +#define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \ + ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific)) + +#define IWL_MAC80211_GET_DVM(_hw) \ + ((struct iwl_priv *) ((struct iwl_op_mode *) \ + (_hw)->priv)->op_mode_specific) + +struct iwl_priv { + + struct iwl_trans *trans; + struct device *dev; /* for debug prints only */ + const struct iwl_cfg *cfg; + const struct iwl_fw *fw; + const struct iwl_lib_ops *lib; + unsigned long status; + + spinlock_t sta_lock; + struct mutex mutex; + + unsigned long transport_queue_stop; + bool passive_no_rx; +#define IWL_INVALID_MAC80211_QUEUE 0xff + u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; + atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; + + unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + + /* ieee device used by generic ieee processing code */ + struct ieee80211_hw *hw; + struct ieee80211_channel *ieee_channels; + struct ieee80211_rate *ieee_rates; + + struct list_head calib_results; + + struct workqueue_struct *workqueue; + + struct iwl_hw_params hw_params; + + enum ieee80211_band band; + u8 valid_contexts; + + void (*pre_rx_handler)(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb); + int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + + struct iwl_notif_wait_data notif_wait; + + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + + /* spectrum measurement report caching */ + struct iwl_spectrum_notification measure_report; + u8 measurement_status; + +#define IWL_OWNERSHIP_DRIVER 0 +#define IWL_OWNERSHIP_TM 1 + u8 ucode_owner; + + /* ucode beacon time */ + u32 ucode_beacon_time; + int missed_beacon_threshold; + + /* track IBSS manager (last beacon) status */ + u32 ibss_manager; + + /* jiffies when last recovery from statistics was performed */ + unsigned long rx_statistics_jiffies; + + /*counters */ + u32 rx_handlers_stats[REPLY_MAX]; + + /* rf reset */ + struct iwl_rf_reset rf_reset; + + /* firmware reload counter and timestamp */ + unsigned long reload_jiffies; + int reload_count; + bool ucode_loaded; + bool init_ucode_run; /* Don't run init uCode again */ + + /* we allocate array of iwl_channel_info for NIC's valid channels. + * Access via channel # using indirect index array */ + struct iwl_channel_info *channel_info; /* channel info array */ + u8 channel_count; /* # of channels */ + + u8 plcp_delta_threshold; + + /* thermal calibration */ + s32 temperature; /* Celsius */ + s32 last_temperature; + + struct iwl_wipan_noa_data __rcu *noa_data; + + /* Scan related variables */ + unsigned long scan_start; + unsigned long scan_start_tsf; + void *scan_cmd; + enum ieee80211_band scan_band; + struct cfg80211_scan_request *scan_request; + struct ieee80211_vif *scan_vif; + enum iwl_scan_type scan_type; + u8 scan_tx_ant[IEEE80211_NUM_BANDS]; + u8 mgmt_tx_ant; + + /* max number of station keys */ + u8 sta_key_max_num; + + bool new_scan_threshold_behaviour; + + bool wowlan; + + /* EEPROM MAC addresses */ + struct mac_address addresses[2]; + + struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; + + __le16 switch_channel; + + u8 start_calib; + struct iwl_sensitivity_data sensitivity_data; + struct iwl_chain_noise_data chain_noise_data; + __le16 sensitivity_tbl[HD_TABLE_SIZE]; + __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES]; + + struct iwl_ht_config current_ht_config; + + /* Rate scaling data */ + u8 retry_rate; + + int activity_timer_active; + + struct iwl_power_mgr power_data; + struct iwl_tt_mgmt thermal_throttle; + + /* station table variables */ + int num_stations; + struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; + unsigned long ucode_key_table; + struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; + atomic_t num_aux_in_flight; + + u8 mac80211_registered; + + /* Indication if ieee80211_ops->open has been called */ + u8 is_open; + + enum nl80211_iftype iw_mode; + + /* Last Rx'd beacon timestamp */ + u64 timestamp; + + struct { + __le32 flag; + struct statistics_general_common common; + struct statistics_rx_non_phy rx_non_phy; + struct statistics_rx_phy rx_ofdm; + struct statistics_rx_ht_phy rx_ofdm_ht; + struct statistics_rx_phy rx_cck; + struct statistics_tx tx; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct statistics_bt_activity bt_activity; + __le32 num_bt_kills, accum_num_bt_kills; +#endif + spinlock_t lock; + } statistics; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct { + struct statistics_general_common common; + struct statistics_rx_non_phy rx_non_phy; + struct statistics_rx_phy rx_ofdm; + struct statistics_rx_ht_phy rx_ofdm_ht; + struct statistics_rx_phy rx_cck; + struct statistics_tx tx; + struct statistics_bt_activity bt_activity; + } accum_stats, delta_stats, max_delta_stats; +#endif + + /* + * reporting the number of tids has AGG on. 0 means + * no AGGREGATION + */ + u8 agg_tids_count; + + struct iwl_rx_phy_res last_phy_res; + bool last_phy_res_valid; + + /* + * chain noise reset and gain commands are the + * two extra calibration commands follows the standard + * phy calibration commands + */ + u8 phy_calib_chain_noise_reset_cmd; + u8 phy_calib_chain_noise_gain_cmd; + + /* counts reply_tx error */ + struct reply_tx_error_statistics reply_tx_stats; + struct reply_agg_tx_error_statistics reply_agg_tx_stats; + + /* remain-on-channel offload support */ + struct ieee80211_channel *hw_roc_channel; + struct delayed_work hw_roc_disable_work; + enum nl80211_channel_type hw_roc_chantype; + int hw_roc_duration; + bool hw_roc_setup, hw_roc_start_notified; + + /* bt coex */ + u8 bt_enable_flag; + u8 bt_status; + u8 bt_traffic_load, last_bt_traffic_load; + bool bt_ch_announce; + bool bt_full_concurrent; + bool bt_ant_couple_ok; + __le32 kill_ack_mask; + __le32 kill_cts_mask; + __le16 bt_valid; + bool reduced_txpower; + u16 bt_on_thresh; + u16 bt_duration; + u16 dynamic_frag_thresh; + u8 bt_ci_compliance; + struct work_struct bt_traffic_change_work; + bool bt_enable_pspoll; + struct iwl_rxon_context *cur_rssi_ctx; + bool bt_is_sco; + + struct work_struct restart; + struct work_struct scan_completed; + struct work_struct abort_scan; + + struct work_struct beacon_update; + struct iwl_rxon_context *beacon_ctx; + struct sk_buff *beacon_skb; + void *beacon_cmd; + + struct work_struct tt_work; + struct work_struct ct_enter; + struct work_struct ct_exit; + struct work_struct start_internal_scan; + struct work_struct tx_flush; + struct work_struct bt_full_concurrency; + struct work_struct bt_runtime_config; + + struct delayed_work scan_check; + + /* TX Power */ + s8 tx_power_user_lmt; + s8 tx_power_device_lmt; + s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */ + s8 tx_power_next; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* debugfs */ + struct dentry *debugfs_dir; + u32 dbgfs_sram_offset, dbgfs_sram_len; + bool disable_ht40; + void *wowlan_sram; +#endif /* CONFIG_IWLWIFI_DEBUGFS */ + + /* eeprom -- this is in the card's little endian byte order */ + u8 *eeprom; + enum iwl_nvm_type nvm_device_type; + + struct work_struct txpower_work; + u32 calib_disabled; + struct work_struct run_time_calib_work; + struct timer_list statistics_periodic; + struct timer_list ucode_trace; + + struct iwl_event_log event_log; + + struct led_classdev led; + unsigned long blink_on, blink_off; + bool led_registered; +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + struct iwl_testmode_trace testmode_trace; + struct iwl_testmode_mem testmode_mem; + u32 tm_fixed_rate; +#endif + + /* WoWLAN GTK rekey data */ + u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; + __le64 replay_ctr; + __le16 last_seq_ctl; + bool have_rekey_data; + + /* device_pointers: pointers to ucode event tables */ + struct { + u32 error_event_table; + u32 log_event_table; + } device_pointers; + + /* indicator of loaded ucode image */ + enum iwl_ucode_type cur_ucode; +}; /*iwl_priv */ + +extern struct kmem_cache *iwl_tx_cmd_pool; + +static inline struct iwl_rxon_context * +iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + return vif_priv->ctx; +} + +#define for_each_context(priv, ctx) \ + for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \ + ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ + if (priv->valid_contexts & BIT(ctx->ctxid)) + +static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) +{ + return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int iwl_is_associated(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ + return iwl_is_associated_ctx(&priv->contexts[ctxid]); +} + +static inline int iwl_is_any_associated(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + for_each_context(priv, ctx) + if (iwl_is_associated_ctx(ctx)) + return true; + return false; +} + +static inline int is_channel_valid(const struct iwl_channel_info *ch_info) +{ + if (ch_info == NULL) + return 0; + return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; +} + +static inline int is_channel_radar(const struct iwl_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; +} + +static inline u8 is_channel_a_band(const struct iwl_channel_info *ch_info) +{ + return ch_info->band == IEEE80211_BAND_5GHZ; +} + +static inline u8 is_channel_bg_band(const struct iwl_channel_info *ch_info) +{ + return ch_info->band == IEEE80211_BAND_2GHZ; +} + +static inline int is_channel_passive(const struct iwl_channel_info *ch) +{ + return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; +} + +static inline int is_channel_ibss(const struct iwl_channel_info *ch) +{ + return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0; +} + +#endif /* __iwl_dev_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c new file mode 100644 index 000000000000..ac5675d86ec3 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -0,0 +1,740 @@ +/****************************************************************************** + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +/* + * DVM device-specific data & functions + */ +#include "iwl-io.h" +#include "iwl-prph.h" + +#include "agn.h" +#include "dev.h" +#include "commands.h" + +/* + * 1000 series + * =========== + */ + +/* + * For 1000, use advance thermal throttling critical temperature threshold, + * but legacy thermal management implementation for now. + * This is for the reason of 1000 uCode using advance thermal throttling API + * but not implement ct_kill_exit based on ct_kill exit temperature + * so the thermal throttling will still based on legacy thermal throttling + * management. + * The code here need to be modified once 1000 uCode has the advanced thermal + * throttling algorithm in place + */ +static void iwl1000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 1000 series */ +static void iwl1000_nic_config(struct iwl_priv *priv) +{ + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); + + /* Setting digital SVR for 1000 card to 1.32V */ + /* locking is acquired in iwl_set_bits_mask_prph() function */ + iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG, + APMG_SVR_DIGITAL_VOLTAGE_1_32, + ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); +} + +/** + * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time + * @priv -- pointer to iwl_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv, + u16 tsf_bits) +{ + return (1 << tsf_bits) - 1; +} + +/** + * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time + * @priv -- pointer to iwl_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv, + u16 tsf_bits) +{ + return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; +} + +/* + * extended beacon time format + * time in usec will be changed into a 32-bit value in extended:internal format + * the extended part is the beacon counts + * the internal part is the time in usec within one beacon interval + */ +static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, + u32 beacon_interval) +{ + u32 quot; + u32 rem; + u32 interval = beacon_interval * TIME_UNIT; + + if (!interval || !usec) + return 0; + + quot = (usec / interval) & + (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >> + IWLAGN_EXT_BEACON_TIME_POS); + rem = (usec % interval) & iwl_beacon_time_mask_low(priv, + IWLAGN_EXT_BEACON_TIME_POS); + + return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem; +} + +/* base is usually what we get from ucode with each received frame, + * the same as HW timer counter counting down + */ +static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, + u32 addon, u32 beacon_interval) +{ + u32 base_low = base & iwl_beacon_time_mask_low(priv, + IWLAGN_EXT_BEACON_TIME_POS); + u32 addon_low = addon & iwl_beacon_time_mask_low(priv, + IWLAGN_EXT_BEACON_TIME_POS); + u32 interval = beacon_interval * TIME_UNIT; + u32 res = (base & iwl_beacon_time_mask_high(priv, + IWLAGN_EXT_BEACON_TIME_POS)) + + (addon & iwl_beacon_time_mask_high(priv, + IWLAGN_EXT_BEACON_TIME_POS)); + + if (base_low > addon_low) + res += base_low - addon_low; + else if (base_low < addon_low) { + res += interval + base_low - addon_low; + res += (1 << IWLAGN_EXT_BEACON_TIME_POS); + } else + res += (1 << IWLAGN_EXT_BEACON_TIME_POS); + + return cpu_to_le32(res); +} + +static const struct iwl_sensitivity_ranges iwl1000_sensitivity = { + .min_nrg_cck = 95, + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 120, + .auto_corr_min_ofdm_mrc_x1 = 240, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + .auto_corr_max_ofdm_x1 = 155, + .auto_corr_max_ofdm_mrc_x1 = 290, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) +{ + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); + + priv->hw_params.tx_chains_num = + num_of_ant(priv->hw_params.valid_tx_ant); + if (priv->cfg->rx_with_siso_diversity) + priv->hw_params.rx_chains_num = 1; + else + priv->hw_params.rx_chains_num = + num_of_ant(priv->hw_params.valid_rx_ant); + + iwl1000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl1000_sensitivity; +} + +struct iwl_lib_ops iwl1000_lib = { + .set_hw_params = iwl1000_hw_set_hw_params, + .nic_config = iwl1000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + }, + }, + .temperature = iwlagn_temperature, +}; + + +/* + * 2000 series + * =========== + */ + +static void iwl2000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 2000 series */ +static void iwl2000_nic_config(struct iwl_priv *priv) +{ + iwl_rf_config(priv); + + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); +} + +static const struct iwl_sensitivity_ranges iwl2000_sensitivity = { + .min_nrg_cck = 97, + .auto_corr_min_ofdm = 80, + .auto_corr_min_ofdm_mrc = 128, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 192, + + .auto_corr_max_ofdm = 145, + .auto_corr_max_ofdm_mrc = 232, + .auto_corr_max_ofdm_x1 = 110, + .auto_corr_max_ofdm_mrc_x1 = 232, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 175, + .auto_corr_min_cck_mrc = 160, + .auto_corr_max_cck_mrc = 310, + .nrg_th_cck = 97, + .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) +{ + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); + + priv->hw_params.tx_chains_num = + num_of_ant(priv->hw_params.valid_tx_ant); + if (priv->cfg->rx_with_siso_diversity) + priv->hw_params.rx_chains_num = 1; + else + priv->hw_params.rx_chains_num = + num_of_ant(priv->hw_params.valid_rx_ant); + + iwl2000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl2000_sensitivity; +} + +struct iwl_lib_ops iwl2000_lib = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + }, + .enhanced_txpower = true, + }, + .temperature = iwlagn_temperature, +}; + +struct iwl_lib_ops iwl2030_lib = { + .set_hw_params = iwl2000_hw_set_hw_params, + .nic_config = iwl2000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + }, + .enhanced_txpower = true, + }, + .temperature = iwlagn_temperature, +}; + +/* + * 5000 series + * =========== + */ + +/* NIC configuration for 5000 series */ +static void iwl5000_nic_config(struct iwl_priv *priv) +{ + iwl_rf_config(priv); + + /* W/A : NIC is stuck in a reset state after Early PCIe power off + * (PCIe power is lost before PERST# is asserted), + * causing ME FW to lose ownership and not being able to obtain it back. + */ + iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); +} + +static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { + .min_nrg_cck = 100, + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + .auto_corr_max_ofdm_x1 = 120, + .auto_corr_max_ofdm_mrc_x1 = 240, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 200, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 100, + .nrg_th_ofdm = 100, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +static struct iwl_sensitivity_ranges iwl5150_sensitivity = { + .min_nrg_cck = 95, + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + /* max = min for performance bug in 5150 DSP */ + .auto_corr_max_ofdm_x1 = 105, + .auto_corr_max_ofdm_mrc_x1 = 220, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 390, + .nrg_th_cca = 62, +}; + +#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5) + +static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) +{ + u16 temperature, voltage; + __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(priv, + EEPROM_KELVIN_TEMPERATURE); + + temperature = le16_to_cpu(temp_calib[0]); + voltage = le16_to_cpu(temp_calib[1]); + + /* offset = temp - volt / coeff */ + return (s32)(temperature - + voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF); +} + +static void iwl5150_set_ct_threshold(struct iwl_priv *priv) +{ + const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; + s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - + iwl_temp_calib_to_offset(priv); + + priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; +} + +static void iwl5000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; +} + +static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) +{ + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | + BIT(IEEE80211_BAND_5GHZ); + + priv->hw_params.tx_chains_num = + num_of_ant(priv->hw_params.valid_tx_ant); + priv->hw_params.rx_chains_num = + num_of_ant(priv->hw_params.valid_rx_ant); + + iwl5000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl5000_sensitivity; +} + +static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) +{ + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | + BIT(IEEE80211_BAND_5GHZ); + + priv->hw_params.tx_chains_num = + num_of_ant(priv->hw_params.valid_tx_ant); + priv->hw_params.rx_chains_num = + num_of_ant(priv->hw_params.valid_rx_ant); + + iwl5150_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl5150_sensitivity; +} + +static void iwl5150_temperature(struct iwl_priv *priv) +{ + u32 vt = 0; + s32 offset = iwl_temp_calib_to_offset(priv); + + vt = le32_to_cpu(priv->statistics.common.temperature); + vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; + /* now vt hold the temperature in Kelvin */ + priv->temperature = KELVIN_TO_CELSIUS(vt); + iwl_tt_handler(priv); +} + +static int iwl5000_hw_channel_switch(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch) +{ + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl5000_channel_switch_cmd cmd; + u32 switch_time_in_usec, ucode_switch_time; + u16 ch; + u32 tsf_low; + u8 switch_count; + u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); + struct ieee80211_vif *vif = ctx->vif; + struct iwl_host_cmd hcmd = { + .id = REPLY_CHANNEL_SWITCH, + .len = { sizeof(cmd), }, + .flags = CMD_SYNC, + .data = { &cmd, }, + }; + + cmd.band = priv->band == IEEE80211_BAND_2GHZ; + ch = ch_switch->channel->hw_value; + IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", + ctx->active.channel, ch); + cmd.channel = cpu_to_le16(ch); + cmd.rxon_flags = ctx->staging.flags; + cmd.rxon_filter_flags = ctx->staging.filter_flags; + switch_count = ch_switch->count; + tsf_low = ch_switch->timestamp & 0x0ffffffff; + /* + * calculate the ucode channel switch time + * adding TSF as one of the factor for when to switch + */ + if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { + if (switch_count > ((priv->ucode_beacon_time - tsf_low) / + beacon_interval)) { + switch_count -= (priv->ucode_beacon_time - + tsf_low) / beacon_interval; + } else + switch_count = 0; + } + if (switch_count <= 1) + cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); + else { + switch_time_in_usec = + vif->bss_conf.beacon_int * switch_count * TIME_UNIT; + ucode_switch_time = iwl_usecs_to_beacons(priv, + switch_time_in_usec, + beacon_interval); + cmd.switch_time = iwl_add_beacon_time(priv, + priv->ucode_beacon_time, + ucode_switch_time, + beacon_interval); + } + IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", + cmd.switch_time); + cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; + + return iwl_dvm_send_cmd(priv, &hcmd); +} + +struct iwl_lib_ops iwl5000_lib = { + .set_hw_params = iwl5000_hw_set_hw_params, + .set_channel_switch = iwl5000_hw_channel_switch, + .nic_config = iwl5000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, + }, + .temperature = iwlagn_temperature, +}; + +struct iwl_lib_ops iwl5150_lib = { + .set_hw_params = iwl5150_hw_set_hw_params, + .set_channel_switch = iwl5000_hw_channel_switch, + .nic_config = iwl5000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, + }, + .temperature = iwl5150_temperature, +}; + + + +/* + * 6000 series + * =========== + */ + +static void iwl6000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 6000 series */ +static void iwl6000_nic_config(struct iwl_priv *priv) +{ + iwl_rf_config(priv); + + switch (priv->cfg->device_family) { + case IWL_DEVICE_FAMILY_6005: + case IWL_DEVICE_FAMILY_6030: + case IWL_DEVICE_FAMILY_6000: + break; + case IWL_DEVICE_FAMILY_6000i: + /* 2x2 IPA phy type */ + iwl_write32(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); + break; + case IWL_DEVICE_FAMILY_6050: + /* Indicate calibration version to uCode. */ + if (iwl_eeprom_calib_version(priv) >= 6) + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); + break; + case IWL_DEVICE_FAMILY_6150: + /* Indicate calibration version to uCode. */ + if (iwl_eeprom_calib_version(priv) >= 6) + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); + iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_6050_1x2); + break; + default: + WARN_ON(1); + } +} + +static const struct iwl_sensitivity_ranges iwl6000_sensitivity = { + .min_nrg_cck = 110, + .auto_corr_min_ofdm = 80, + .auto_corr_min_ofdm_mrc = 128, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 192, + + .auto_corr_max_ofdm = 145, + .auto_corr_max_ofdm_mrc = 232, + .auto_corr_max_ofdm_x1 = 110, + .auto_corr_max_ofdm_mrc_x1 = 232, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 175, + .auto_corr_min_cck_mrc = 160, + .auto_corr_max_cck_mrc = 310, + .nrg_th_cck = 110, + .nrg_th_ofdm = 110, + + .barker_corr_th_min = 190, + .barker_corr_th_min_mrc = 336, + .nrg_th_cca = 62, +}; + +static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) +{ + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | + BIT(IEEE80211_BAND_5GHZ); + + priv->hw_params.tx_chains_num = + num_of_ant(priv->hw_params.valid_tx_ant); + if (priv->cfg->rx_with_siso_diversity) + priv->hw_params.rx_chains_num = 1; + else + priv->hw_params.rx_chains_num = + num_of_ant(priv->hw_params.valid_rx_ant); + + iwl6000_set_ct_threshold(priv); + + /* Set initial sensitivity parameters */ + priv->hw_params.sens = &iwl6000_sensitivity; + +} + +static int iwl6000_hw_channel_switch(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch) +{ + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl6000_channel_switch_cmd cmd; + u32 switch_time_in_usec, ucode_switch_time; + u16 ch; + u32 tsf_low; + u8 switch_count; + u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); + struct ieee80211_vif *vif = ctx->vif; + struct iwl_host_cmd hcmd = { + .id = REPLY_CHANNEL_SWITCH, + .len = { sizeof(cmd), }, + .flags = CMD_SYNC, + .data = { &cmd, }, + }; + + cmd.band = priv->band == IEEE80211_BAND_2GHZ; + ch = ch_switch->channel->hw_value; + IWL_DEBUG_11H(priv, "channel switch from %u to %u\n", + ctx->active.channel, ch); + cmd.channel = cpu_to_le16(ch); + cmd.rxon_flags = ctx->staging.flags; + cmd.rxon_filter_flags = ctx->staging.filter_flags; + switch_count = ch_switch->count; + tsf_low = ch_switch->timestamp & 0x0ffffffff; + /* + * calculate the ucode channel switch time + * adding TSF as one of the factor for when to switch + */ + if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { + if (switch_count > ((priv->ucode_beacon_time - tsf_low) / + beacon_interval)) { + switch_count -= (priv->ucode_beacon_time - + tsf_low) / beacon_interval; + } else + switch_count = 0; + } + if (switch_count <= 1) + cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); + else { + switch_time_in_usec = + vif->bss_conf.beacon_int * switch_count * TIME_UNIT; + ucode_switch_time = iwl_usecs_to_beacons(priv, + switch_time_in_usec, + beacon_interval); + cmd.switch_time = iwl_add_beacon_time(priv, + priv->ucode_beacon_time, + ucode_switch_time, + beacon_interval); + } + IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", + cmd.switch_time); + cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; + + return iwl_dvm_send_cmd(priv, &hcmd); +} + +struct iwl_lib_ops iwl6000_lib = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, + .enhanced_txpower = true, + }, + .temperature = iwlagn_temperature, +}; + +struct iwl_lib_ops iwl6030_lib = { + .set_hw_params = iwl6000_hw_set_hw_params, + .set_channel_switch = iwl6000_hw_channel_switch, + .nic_config = iwl6000_nic_config, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, + .enhanced_txpower = true, + }, + .temperature = iwlagn_temperature, +}; diff --git a/drivers/net/wireless/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/iwlwifi/dvm/eeprom.c new file mode 100644 index 000000000000..69d280f8db33 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/eeprom.c @@ -0,0 +1,1146 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + + +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-prph.h" +#include "iwl-debug.h" +#include "dev.h" +#include "agn.h" +#include "eeprom.h" + +/************************** EEPROM BANDS **************************** + * + * The iwl_eeprom_band definitions below provide the mapping from the + * EEPROM contents to the specific channel number supported for each + * band. + * + * For example, iwl_priv->eeprom.band_3_channels[4] from the band_3 + * definition below maps to physical channel 42 in the 5.2GHz spectrum. + * The specific geography and calibration information for that channel + * is contained in the eeprom map itself. + * + * During init, we copy the eeprom information and channel map + * information into priv->channel_info_24/52 and priv->channel_map_24/52 + * + * channel_map_24/52 provides the index in the channel_info array for a + * given channel. We have to have two separate maps as there is channel + * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and + * band_2 + * + * A value of 0xff stored in the channel_map indicates that the channel + * is not supported by the hardware at all. + * + * A value of 0xfe in the channel_map indicates that the channel is not + * valid for Tx with the current hardware. This means that + * while the system can tune and receive on a given channel, it may not + * be able to associate or transmit any frames on that + * channel. There is no corresponding channel information for that + * entry. + * + *********************************************************************/ + +/* 2.4 GHz */ +const u8 iwl_eeprom_band_1[14] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +/* 5.2 GHz bands */ +static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ + 1, 2, 3, 4, 5, 6, 7 +}; + +static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +/****************************************************************************** + * + * generic NVM functions + * +******************************************************************************/ + +/* + * The device's EEPROM semaphore prevents conflicts between driver and uCode + * when accessing the EEPROM; each access is a series of pulses to/from the + * EEPROM chip, not a single event, so even reads could conflict if they + * weren't arbitrated by the semaphore. + */ + +#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ +#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + +static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) +{ + u16 count; + int ret; + + for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + /* Request semaphore */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + + /* See if we got it */ + ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + EEPROM_SEM_TIMEOUT); + if (ret >= 0) { + IWL_DEBUG_EEPROM(trans, + "Acquired semaphore after %d tries.\n", + count+1); + return ret; + } + } + + return ret; +} + +static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) +{ + iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + +} + +static int iwl_eeprom_verify_signature(struct iwl_priv *priv) +{ + u32 gp = iwl_read32(priv->trans, CSR_EEPROM_GP) & + CSR_EEPROM_GP_VALID_MSK; + int ret = 0; + + IWL_DEBUG_EEPROM(priv, "EEPROM signature=0x%08x\n", gp); + switch (gp) { + case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: + if (priv->nvm_device_type != NVM_DEVICE_TYPE_OTP) { + IWL_ERR(priv, "EEPROM with bad signature: 0x%08x\n", + gp); + ret = -ENOENT; + } + break; + case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: + case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: + if (priv->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) { + IWL_ERR(priv, "OTP with bad signature: 0x%08x\n", gp); + ret = -ENOENT; + } + break; + case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: + default: + IWL_ERR(priv, "bad EEPROM/OTP signature, type=%s, " + "EEPROM_GP=0x%08x\n", + (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + ? "OTP" : "EEPROM", gp); + ret = -ENOENT; + break; + } + return ret; +} + +u16 iwl_eeprom_query16(struct iwl_priv *priv, size_t offset) +{ + if (!priv->eeprom) + return 0; + return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); +} + +int iwl_eeprom_check_version(struct iwl_priv *priv) +{ + u16 eeprom_ver; + u16 calib_ver; + + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + calib_ver = iwl_eeprom_calib_version(priv); + + if (eeprom_ver < priv->cfg->eeprom_ver || + calib_ver < priv->cfg->eeprom_calib_ver) + goto err; + + IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", + eeprom_ver, calib_ver); + + return 0; +err: + IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " + "CALIB=0x%x < 0x%x\n", + eeprom_ver, priv->cfg->eeprom_ver, + calib_ver, priv->cfg->eeprom_calib_ver); + return -EINVAL; + +} + +int iwl_eeprom_init_hw_params(struct iwl_priv *priv) +{ + u16 radio_cfg; + + priv->hw_params.sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP); + if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE && + !priv->cfg->ht_params) { + IWL_ERR(priv, "Invalid 11n configuration\n"); + return -EINVAL; + } + + if (!priv->hw_params.sku) { + IWL_ERR(priv, "Invalid device sku\n"); + return -EINVAL; + } + + IWL_INFO(priv, "Device SKU: 0x%X\n", priv->hw_params.sku); + + radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); + + priv->hw_params.valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); + priv->hw_params.valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); + + /* check overrides (some devices have wrong EEPROM) */ + if (priv->cfg->valid_tx_ant) + priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; + if (priv->cfg->valid_rx_ant) + priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; + + if (!priv->hw_params.valid_tx_ant || !priv->hw_params.valid_rx_ant) { + IWL_ERR(priv, "Invalid chain (0x%X, 0x%X)\n", + priv->hw_params.valid_tx_ant, + priv->hw_params.valid_rx_ant); + return -EINVAL; + } + + IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", + priv->hw_params.valid_tx_ant, priv->hw_params.valid_rx_ant); + + return 0; +} + +u16 iwl_eeprom_calib_version(struct iwl_priv *priv) +{ + struct iwl_eeprom_calib_hdr *hdr; + + hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, + EEPROM_CALIB_ALL); + return hdr->version; +} + +static u32 eeprom_indirect_address(struct iwl_priv *priv, u32 address) +{ + u16 offset = 0; + + if ((address & INDIRECT_ADDRESS) == 0) + return address; + + switch (address & INDIRECT_TYPE_MSK) { + case INDIRECT_HOST: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_HOST); + break; + case INDIRECT_GENERAL: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_GENERAL); + break; + case INDIRECT_REGULATORY: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_REGULATORY); + break; + case INDIRECT_TXP_LIMIT: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT); + break; + case INDIRECT_TXP_LIMIT_SIZE: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT_SIZE); + break; + case INDIRECT_CALIBRATION: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_CALIBRATION); + break; + case INDIRECT_PROCESS_ADJST: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_PROCESS_ADJST); + break; + case INDIRECT_OTHERS: + offset = iwl_eeprom_query16(priv, EEPROM_LINK_OTHERS); + break; + default: + IWL_ERR(priv, "illegal indirect type: 0x%X\n", + address & INDIRECT_TYPE_MSK); + break; + } + + /* translate the offset from words to byte */ + return (address & ADDRESS_MSK) + (offset << 1); +} + +const u8 *iwl_eeprom_query_addr(struct iwl_priv *priv, size_t offset) +{ + u32 address = eeprom_indirect_address(priv, offset); + BUG_ON(address >= priv->cfg->base_params->eeprom_size); + return &priv->eeprom[address]; +} + +void iwl_eeprom_get_mac(struct iwl_priv *priv, u8 *mac) +{ + const u8 *addr = iwl_eeprom_query_addr(priv, + EEPROM_MAC_ADDRESS); + memcpy(mac, addr, ETH_ALEN); +} + +/****************************************************************************** + * + * OTP related functions + * +******************************************************************************/ + +static void iwl_set_otp_access(struct iwl_trans *trans, + enum iwl_access_mode mode) +{ + iwl_read32(trans, CSR_OTP_GP_REG); + + if (mode == IWL_OTP_ACCESS_ABSOLUTE) + iwl_clear_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); + else + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); +} + +static int iwl_get_nvm_type(struct iwl_trans *trans, u32 hw_rev) +{ + u32 otpgp; + int nvm_type; + + /* OTP only valid for CP/PP and after */ + switch (hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_NONE: + IWL_ERR(trans, "Unknown hardware type\n"); + return -ENOENT; + case CSR_HW_REV_TYPE_5300: + case CSR_HW_REV_TYPE_5350: + case CSR_HW_REV_TYPE_5100: + case CSR_HW_REV_TYPE_5150: + nvm_type = NVM_DEVICE_TYPE_EEPROM; + break; + default: + otpgp = iwl_read32(trans, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) + nvm_type = NVM_DEVICE_TYPE_OTP; + else + nvm_type = NVM_DEVICE_TYPE_EEPROM; + break; + } + return nvm_type; +} + +static int iwl_init_otp_access(struct iwl_trans *trans) +{ + int ret; + + /* Enable 40MHz radio clock */ + iwl_write32(trans, CSR_GP_CNTRL, + iwl_read32(trans, CSR_GP_CNTRL) | + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* wait for clock to be ready */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) + IWL_ERR(trans, "Time out access OTP\n"); + else { + iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + udelay(5); + iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + + /* + * CSR auto clock gate disable bit - + * this is only applicable for HW with OTP shadow RAM + */ + if (trans->cfg->base_params->shadow_ram_support) + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + } + return ret; +} + +static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, + __le16 *eeprom_data) +{ + int ret = 0; + u32 r; + u32 otpgp; + + iwl_write32(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + ret = iwl_poll_bit(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); + return ret; + } + r = iwl_read32(trans, CSR_EEPROM_REG); + /* check for ECC errors: */ + otpgp = iwl_read32(trans, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { + /* stop in this case */ + /* set the uncorrectable OTP ECC bit for acknowledgement */ + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); + return -EINVAL; + } + if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { + /* continue in this case */ + /* set the correctable OTP ECC bit for acknowledgement */ + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); + IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); + } + *eeprom_data = cpu_to_le16(r >> 16); + return 0; +} + +/* + * iwl_is_otp_empty: check for empty OTP + */ +static bool iwl_is_otp_empty(struct iwl_trans *trans) +{ + u16 next_link_addr = 0; + __le16 link_value; + bool is_empty = false; + + /* locate the beginning of OTP link list */ + if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { + if (!link_value) { + IWL_ERR(trans, "OTP is empty\n"); + is_empty = true; + } + } else { + IWL_ERR(trans, "Unable to read first block of OTP list.\n"); + is_empty = true; + } + + return is_empty; +} + + +/* + * iwl_find_otp_image: find EEPROM image in OTP + * finding the OTP block that contains the EEPROM image. + * the last valid block on the link list (the block _before_ the last block) + * is the block we should read and used to configure the device. + * If all the available OTP blocks are full, the last block will be the block + * we should read and used to configure the device. + * only perform this operation if shadow RAM is disabled + */ +static int iwl_find_otp_image(struct iwl_trans *trans, + u16 *validblockaddr) +{ + u16 next_link_addr = 0, valid_addr; + __le16 link_value = 0; + int usedblocks = 0; + + /* set addressing mode to absolute to traverse the link list */ + iwl_set_otp_access(trans, IWL_OTP_ACCESS_ABSOLUTE); + + /* checking for empty OTP or error */ + if (iwl_is_otp_empty(trans)) + return -EINVAL; + + /* + * start traverse link list + * until reach the max number of OTP blocks + * different devices have different number of OTP blocks + */ + do { + /* save current valid block address + * check for more block on the link list + */ + valid_addr = next_link_addr; + next_link_addr = le16_to_cpu(link_value) * sizeof(u16); + IWL_DEBUG_EEPROM(trans, "OTP blocks %d addr 0x%x\n", + usedblocks, next_link_addr); + if (iwl_read_otp_word(trans, next_link_addr, &link_value)) + return -EINVAL; + if (!link_value) { + /* + * reach the end of link list, return success and + * set address point to the starting address + * of the image + */ + *validblockaddr = valid_addr; + /* skip first 2 bytes (link list pointer) */ + *validblockaddr += 2; + return 0; + } + /* more in the link list, continue */ + usedblocks++; + } while (usedblocks <= trans->cfg->base_params->max_ll_items); + + /* OTP has no valid blocks */ + IWL_DEBUG_EEPROM(trans, "OTP has no valid blocks\n"); + return -EINVAL; +} + +/****************************************************************************** + * + * Tx Power related functions + * +******************************************************************************/ +/** + * iwl_get_max_txpower_avg - get the highest tx power from all chains. + * find the highest tx power from all chains for the channel + */ +static s8 iwl_get_max_txpower_avg(const struct iwl_cfg *cfg, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int element, s8 *max_txpower_in_half_dbm) +{ + s8 max_txpower_avg = 0; /* (dBm) */ + + /* Take the highest tx power from any valid chains */ + if ((cfg->valid_tx_ant & ANT_A) && + (enhanced_txpower[element].chain_a_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_a_max; + if ((cfg->valid_tx_ant & ANT_B) && + (enhanced_txpower[element].chain_b_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_b_max; + if ((cfg->valid_tx_ant & ANT_C) && + (enhanced_txpower[element].chain_c_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_c_max; + if (((cfg->valid_tx_ant == ANT_AB) | + (cfg->valid_tx_ant == ANT_BC) | + (cfg->valid_tx_ant == ANT_AC)) && + (enhanced_txpower[element].mimo2_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo2_max; + if ((cfg->valid_tx_ant == ANT_ABC) && + (enhanced_txpower[element].mimo3_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo3_max; + + /* + * max. tx power in EEPROM is in 1/2 dBm format + * convert from 1/2 dBm to dBm (round-up convert) + * but we also do not want to loss 1/2 dBm resolution which + * will impact performance + */ + *max_txpower_in_half_dbm = max_txpower_avg; + return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); +} + +static void +iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *txp, + s8 max_txpower_avg) +{ + int ch_idx; + bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ; + enum ieee80211_band band; + + band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + + for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) { + struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx]; + + /* update matching channel or from common data only */ + if (txp->channel != 0 && ch_info->channel != txp->channel) + continue; + + /* update matching band only */ + if (band != ch_info->band) + continue; + + if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) { + ch_info->max_power_avg = max_txpower_avg; + ch_info->curr_txpow = max_txpower_avg; + ch_info->scan_power = max_txpower_avg; + } + + if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg) + ch_info->ht40_max_power_avg = max_txpower_avg; + } +} + +#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) +#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) +#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) + +#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \ + ? # x " " : "") + +static void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) +{ + struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; + int idx, entries; + __le16 *txp_len; + s8 max_txp_avg, max_txp_avg_halfdbm; + + BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); + + /* the length is in 16-bit words, but we want entries */ + txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS); + entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; + + txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS); + + for (idx = 0; idx < entries; idx++) { + txp = &txp_array[idx]; + /* skip invalid entries */ + if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) + continue; + + IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", + (txp->channel && (txp->flags & + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? + "Common " : (txp->channel) ? + "Channel" : "Common", + (txp->channel), + TXP_CHECK_AND_PRINT(VALID), + TXP_CHECK_AND_PRINT(BAND_52G), + TXP_CHECK_AND_PRINT(OFDM), + TXP_CHECK_AND_PRINT(40MHZ), + TXP_CHECK_AND_PRINT(HT_AP), + TXP_CHECK_AND_PRINT(RES1), + TXP_CHECK_AND_PRINT(RES2), + TXP_CHECK_AND_PRINT(COMMON_TYPE), + txp->flags); + IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x " + "chain_B: 0X%02x chain_C: 0X%02x\n", + txp->chain_a_max, txp->chain_b_max, + txp->chain_c_max); + IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x " + "MIMO3: 0x%02x High 20_on_40: 0x%02x " + "Low 20_on_40: 0x%02x\n", + txp->mimo2_max, txp->mimo3_max, + ((txp->delta_20_in_40 & 0xf0) >> 4), + (txp->delta_20_in_40 & 0x0f)); + + max_txp_avg = iwl_get_max_txpower_avg(priv->cfg, txp_array, idx, + &max_txp_avg_halfdbm); + + /* + * Update the user limit values values to the highest + * power supported by any channel + */ + if (max_txp_avg > priv->tx_power_user_lmt) + priv->tx_power_user_lmt = max_txp_avg; + if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm) + priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm; + + iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg); + } +} + +/** + * iwl_eeprom_init - read EEPROM contents + * + * Load the EEPROM contents from adapter into priv->eeprom + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) +{ + __le16 *e; + u32 gp = iwl_read32(priv->trans, CSR_EEPROM_GP); + int sz; + int ret; + u16 addr; + u16 validblockaddr = 0; + u16 cache_addr = 0; + + priv->nvm_device_type = iwl_get_nvm_type(priv->trans, hw_rev); + if (priv->nvm_device_type == -ENOENT) + return -ENOENT; + /* allocate eeprom */ + sz = priv->cfg->base_params->eeprom_size; + IWL_DEBUG_EEPROM(priv, "NVM size = %d\n", sz); + priv->eeprom = kzalloc(sz, GFP_KERNEL); + if (!priv->eeprom) { + ret = -ENOMEM; + goto alloc_err; + } + e = (__le16 *)priv->eeprom; + + ret = iwl_eeprom_verify_signature(priv); + if (ret < 0) { + IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); + ret = -ENOENT; + goto err; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + ret = iwl_eeprom_acquire_semaphore(priv->trans); + if (ret < 0) { + IWL_ERR(priv, "Failed to acquire EEPROM semaphore.\n"); + ret = -ENOENT; + goto err; + } + + if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { + + ret = iwl_init_otp_access(priv->trans); + if (ret) { + IWL_ERR(priv, "Failed to initialize OTP access.\n"); + ret = -ENOENT; + goto done; + } + iwl_write32(priv->trans, CSR_EEPROM_GP, + iwl_read32(priv->trans, CSR_EEPROM_GP) & + ~CSR_EEPROM_GP_IF_OWNER_MSK); + + iwl_set_bit(priv->trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + /* traversing the linked list if no shadow ram supported */ + if (!priv->cfg->base_params->shadow_ram_support) { + if (iwl_find_otp_image(priv->trans, &validblockaddr)) { + ret = -ENOENT; + goto done; + } + } + for (addr = validblockaddr; addr < validblockaddr + sz; + addr += sizeof(u16)) { + __le16 eeprom_data; + + ret = iwl_read_otp_word(priv->trans, addr, + &eeprom_data); + if (ret) + goto done; + e[cache_addr / 2] = eeprom_data; + cache_addr += sizeof(u16); + } + } else { + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + u32 r; + + iwl_write32(priv->trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + + ret = iwl_poll_bit(priv->trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(priv, + "Time out reading EEPROM[%d]\n", addr); + goto done; + } + r = iwl_read32(priv->trans, CSR_EEPROM_REG); + e[addr / 2] = cpu_to_le16(r >> 16); + } + } + + IWL_DEBUG_EEPROM(priv, "NVM Type: %s, version: 0x%x\n", + (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + ? "OTP" : "EEPROM", + iwl_eeprom_query16(priv, EEPROM_VERSION)); + + ret = 0; +done: + iwl_eeprom_release_semaphore(priv->trans); + +err: + if (ret) + iwl_eeprom_free(priv); +alloc_err: + return ret; +} + +void iwl_eeprom_free(struct iwl_priv *priv) +{ + kfree(priv->eeprom); + priv->eeprom = NULL; +} + +static void iwl_init_band_reference(struct iwl_priv *priv, + int eep_band, int *eeprom_ch_count, + const struct iwl_eeprom_channel **eeprom_ch_info, + const u8 **eeprom_ch_index) +{ + u32 offset = priv->lib-> + eeprom_ops.regulatory_bands[eep_band - 1]; + switch (eep_band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_5; + break; + case 6: /* 2.4GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_6; + break; + case 7: /* 5 GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwl_eeprom_band_7; + break; + default: + BUG(); + return; + } +} + +#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ + ? # x " " : "") +/** + * iwl_mod_ht40_chan_info - Copy ht40 channel info into driver's priv. + * + * Does not set up a command, or touch hardware. + */ +static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, + enum ieee80211_band band, u16 channel, + const struct iwl_eeprom_channel *eeprom_ch, + u8 clear_ht40_extension_channel) +{ + struct iwl_channel_info *ch_info; + + ch_info = (struct iwl_channel_info *) + iwl_get_channel_info(priv, band, channel); + + if (!is_channel_valid(ch_info)) + return -1; + + IWL_DEBUG_EEPROM(priv, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" + " Ad-Hoc %ssupported\n", + ch_info->channel, + is_channel_a_band(ch_info) ? + "5.2" : "2.4", + CHECK_AND_PRINT(IBSS), + CHECK_AND_PRINT(ACTIVE), + CHECK_AND_PRINT(RADAR), + CHECK_AND_PRINT(WIDE), + CHECK_AND_PRINT(DFS), + eeprom_ch->flags, + eeprom_ch->max_power_avg, + ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) + && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? + "" : "not "); + + ch_info->ht40_eeprom = *eeprom_ch; + ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; + ch_info->ht40_flags = eeprom_ch->flags; + if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) + ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; + + return 0; +} + +#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ + ? # x " " : "") + +/** + * iwl_init_channel_map - Set up driver's info for all possible channels + */ +int iwl_init_channel_map(struct iwl_priv *priv) +{ + int eeprom_ch_count = 0; + const u8 *eeprom_ch_index = NULL; + const struct iwl_eeprom_channel *eeprom_ch_info = NULL; + int band, ch; + struct iwl_channel_info *ch_info; + + if (priv->channel_count) { + IWL_DEBUG_EEPROM(priv, "Channel map already initialized.\n"); + return 0; + } + + IWL_DEBUG_EEPROM(priv, "Initializing regulatory info from EEPROM\n"); + + priv->channel_count = + ARRAY_SIZE(iwl_eeprom_band_1) + + ARRAY_SIZE(iwl_eeprom_band_2) + + ARRAY_SIZE(iwl_eeprom_band_3) + + ARRAY_SIZE(iwl_eeprom_band_4) + + ARRAY_SIZE(iwl_eeprom_band_5); + + IWL_DEBUG_EEPROM(priv, "Parsing data for %d channels.\n", + priv->channel_count); + + priv->channel_info = kcalloc(priv->channel_count, + sizeof(struct iwl_channel_info), + GFP_KERNEL); + if (!priv->channel_info) { + IWL_ERR(priv, "Could not allocate channel_info\n"); + priv->channel_count = 0; + return -ENOMEM; + } + + ch_info = priv->channel_info; + + /* Loop through the 5 EEPROM bands adding them in order to the + * channel map we maintain (that contains additional information than + * what just in the EEPROM) */ + for (band = 1; band <= 5; band++) { + + iwl_init_band_reference(priv, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_index); + + /* Loop through each band adding each of the channels */ + for (ch = 0; ch < eeprom_ch_count; ch++) { + ch_info->channel = eeprom_ch_index[ch]; + ch_info->band = (band == 1) ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + + /* permanently store EEPROM's channel regulatory flags + * and max power in channel info database. */ + ch_info->eeprom = eeprom_ch_info[ch]; + + /* Copy the run-time flags so they are there even on + * invalid channels */ + ch_info->flags = eeprom_ch_info[ch].flags; + /* First write that ht40 is not enabled, and then enable + * one by one */ + ch_info->ht40_extension_channel = + IEEE80211_CHAN_NO_HT40; + + if (!(is_channel_valid(ch_info))) { + IWL_DEBUG_EEPROM(priv, + "Ch. %d Flags %x [%sGHz] - " + "No traffic\n", + ch_info->channel, + ch_info->flags, + is_channel_a_band(ch_info) ? + "5.2" : "2.4"); + ch_info++; + continue; + } + + /* Initialize regulatory-based run-time data */ + ch_info->max_power_avg = ch_info->curr_txpow = + eeprom_ch_info[ch].max_power_avg; + ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; + ch_info->min_power = 0; + + IWL_DEBUG_EEPROM(priv, "Ch. %d [%sGHz] " + "%s%s%s%s%s%s(0x%02x %ddBm):" + " Ad-Hoc %ssupported\n", + ch_info->channel, + is_channel_a_band(ch_info) ? + "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(DFS), + eeprom_ch_info[ch].flags, + eeprom_ch_info[ch].max_power_avg, + ((eeprom_ch_info[ch]. + flags & EEPROM_CHANNEL_IBSS) + && !(eeprom_ch_info[ch]. + flags & EEPROM_CHANNEL_RADAR)) + ? "" : "not "); + + ch_info++; + } + } + + /* Check if we do have HT40 channels */ + if (priv->lib->eeprom_ops.regulatory_bands[5] == + EEPROM_REGULATORY_BAND_NO_HT40 && + priv->lib->eeprom_ops.regulatory_bands[6] == + EEPROM_REGULATORY_BAND_NO_HT40) + return 0; + + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ + for (band = 6; band <= 7; band++) { + enum ieee80211_band ieeeband; + + iwl_init_band_reference(priv, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_index); + + /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ + ieeeband = + (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + + /* Loop through each band adding each of the channels */ + for (ch = 0; ch < eeprom_ch_count; ch++) { + /* Set up driver's info for lower half */ + iwl_mod_ht40_chan_info(priv, ieeeband, + eeprom_ch_index[ch], + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40PLUS); + + /* Set up driver's info for upper half */ + iwl_mod_ht40_chan_info(priv, ieeeband, + eeprom_ch_index[ch] + 4, + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40MINUS); + } + } + + /* for newer device (6000 series and up) + * EEPROM contain enhanced tx power information + * driver need to process addition information + * to determine the max channel tx power limits + */ + if (priv->lib->eeprom_ops.enhanced_txpower) + iwl_eeprom_enhanced_txpower(priv); + + return 0; +} + +/* + * iwl_free_channel_map - undo allocations in iwl_init_channel_map + */ +void iwl_free_channel_map(struct iwl_priv *priv) +{ + kfree(priv->channel_info); + priv->channel_count = 0; +} + +/** + * iwl_get_channel_info - Find driver's private channel info + * + * Based on band and channel number. + */ +const struct iwl_channel_info *iwl_get_channel_info(const struct iwl_priv *priv, + enum ieee80211_band band, u16 channel) +{ + int i; + + switch (band) { + case IEEE80211_BAND_5GHZ: + for (i = 14; i < priv->channel_count; i++) { + if (priv->channel_info[i].channel == channel) + return &priv->channel_info[i]; + } + break; + case IEEE80211_BAND_2GHZ: + if (channel >= 1 && channel <= 14) + return &priv->channel_info[channel - 1]; + break; + default: + BUG(); + } + + return NULL; +} + +void iwl_rf_config(struct iwl_priv *priv) +{ + u16 radio_cfg; + + radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); + + /* write radio config values to register */ + if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { + iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, + EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | + EEPROM_RF_CFG_STEP_MSK(radio_cfg) | + EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", + EEPROM_RF_CFG_TYPE_MSK(radio_cfg), + EEPROM_RF_CFG_STEP_MSK(radio_cfg), + EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + } else + WARN_ON(1); + + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/eeprom.h b/drivers/net/wireless/iwlwifi/dvm/eeprom.h new file mode 100644 index 000000000000..64bfd947caeb --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/eeprom.h @@ -0,0 +1,269 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_eeprom_h__ +#define __iwl_eeprom_h__ + +#include + +struct iwl_priv; + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ + +#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ +#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + + +/* + * Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags. + * + * IBSS and/or AP operation is allowed *only* on those channels with + * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because + * RADAR detection is not supported by the 4965 driver, but is a + * requirement for establishing a new network for legal operation on channels + * requiring RADAR detection or restricting ACTIVE scanning. + * + * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. + * It only indicates that 20 MHz channel use is supported; HT40 channel + * usage is indicated by a separate set of regulatory flags for each + * HT40 channel pair. + * + * NOTE: Using a channel inappropriately will result in a uCode error! + */ +#define IWL_NUM_TX_CALIB_GROUPS 5 +enum { + EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ + EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ + /* Bit 2 Reserved */ + EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ + EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ + EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ + /* Bit 6 Reserved (was Narrow Channel) */ + EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ +}; + +/* SKU Capabilities */ +#define EEPROM_SKU_CAP_BAND_24GHZ (1 << 4) +#define EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) +#define EEPROM_SKU_CAP_11N_ENABLE (1 << 6) +#define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7) +#define EEPROM_SKU_CAP_IPAN_ENABLE (1 << 8) + +/* *regulatory* channel data format in eeprom, one for each channel. + * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ +struct iwl_eeprom_channel { + u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ + s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ +} __packed; + +enum iwl_eeprom_enhanced_txpwr_flags { + IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), + IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), + IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), + IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), + IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), + IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), + IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), +}; + +/** + * iwl_eeprom_enhanced_txpwr structure + * This structure presents the enhanced regulatory tx power limit layout + * in eeprom image + * Enhanced regulatory tx power portion of eeprom image can be broken down + * into individual structures; each one is 8 bytes in size and contain the + * following information + * @flags: entry flags + * @channel: channel number + * @chain_a_max_pwr: chain a max power in 1/2 dBm + * @chain_b_max_pwr: chain b max power in 1/2 dBm + * @chain_c_max_pwr: chain c max power in 1/2 dBm + * @delta_20_in_40: 20-in-40 deltas (hi/lo) + * @mimo2_max_pwr: mimo2 max power in 1/2 dBm + * @mimo3_max_pwr: mimo3 max power in 1/2 dBm + * + */ +struct iwl_eeprom_enhanced_txpwr { + u8 flags; + u8 channel; + s8 chain_a_max; + s8 chain_b_max; + s8 chain_c_max; + u8 delta_20_in_40; + s8 mimo2_max; + s8 mimo3_max; +} __packed; + +/* calibration */ +struct iwl_eeprom_calib_hdr { + u8 version; + u8 pa_type; + __le16 voltage; +} __packed; + +#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) +#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) + +/* temperature */ +#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) +#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) + + +/* agn links */ +#define EEPROM_LINK_HOST (2*0x64) +#define EEPROM_LINK_GENERAL (2*0x65) +#define EEPROM_LINK_REGULATORY (2*0x66) +#define EEPROM_LINK_CALIBRATION (2*0x67) +#define EEPROM_LINK_PROCESS_ADJST (2*0x68) +#define EEPROM_LINK_OTHERS (2*0x69) +#define EEPROM_LINK_TXP_LIMIT (2*0x6a) +#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) + +/* agn regulatory - indirect access */ +#define EEPROM_REG_BAND_1_CHANNELS ((0x08)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 28 bytes */ +#define EEPROM_REG_BAND_2_CHANNELS ((0x26)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 26 bytes */ +#define EEPROM_REG_BAND_3_CHANNELS ((0x42)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +#define EEPROM_REG_BAND_4_CHANNELS ((0x5C)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ +#define EEPROM_REG_BAND_5_CHANNELS ((0x74)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 12 bytes */ +#define EEPROM_REG_BAND_24_HT40_CHANNELS ((0x82)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ +#define EEPROM_REG_BAND_52_HT40_CHANNELS ((0x92)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ + +/* 6000 regulatory - indirect access */ +#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS ((0x80)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ +/* 2.4 GHz */ +extern const u8 iwl_eeprom_band_1[14]; + +#define ADDRESS_MSK 0x0000FFFF +#define INDIRECT_TYPE_MSK 0x000F0000 +#define INDIRECT_HOST 0x00010000 +#define INDIRECT_GENERAL 0x00020000 +#define INDIRECT_REGULATORY 0x00030000 +#define INDIRECT_CALIBRATION 0x00040000 +#define INDIRECT_PROCESS_ADJST 0x00050000 +#define INDIRECT_OTHERS 0x00060000 +#define INDIRECT_TXP_LIMIT 0x00070000 +#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 +#define INDIRECT_ADDRESS 0x00100000 + +/* General */ +#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ +#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ +#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ +#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ +#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ +#define EEPROM_VERSION (2*0x44) /* 2 bytes */ +#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ +#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ +#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ +#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ + +/* The following masks are to be applied on EEPROM_RADIO_CONFIG */ +#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ +#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + +#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 + +#define EEPROM_REGULATORY_BAND_NO_HT40 (0) + +struct iwl_eeprom_ops { + const u32 regulatory_bands[7]; + bool enhanced_txpower; +}; + + +int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev); +void iwl_eeprom_free(struct iwl_priv *priv); +int iwl_eeprom_check_version(struct iwl_priv *priv); +int iwl_eeprom_init_hw_params(struct iwl_priv *priv); +u16 iwl_eeprom_calib_version(struct iwl_priv *priv); +const u8 *iwl_eeprom_query_addr(struct iwl_priv *priv, size_t offset); +u16 iwl_eeprom_query16(struct iwl_priv *priv, size_t offset); +void iwl_eeprom_get_mac(struct iwl_priv *priv, u8 *mac); +int iwl_init_channel_map(struct iwl_priv *priv); +void iwl_free_channel_map(struct iwl_priv *priv); +const struct iwl_channel_info *iwl_get_channel_info( + const struct iwl_priv *priv, + enum ieee80211_band band, u16 channel); +void iwl_rf_config(struct iwl_priv *priv); + +#endif /* __iwl_eeprom_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/led.c b/drivers/net/wireless/iwlwifi/dvm/led.c new file mode 100644 index 000000000000..bf479f709091 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/led.c @@ -0,0 +1,224 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-modparams.h" +#include "dev.h" +#include "agn.h" + +/* Throughput OFF time(ms) ON time (ms) + * >300 25 25 + * >200 to 300 40 40 + * >100 to 200 55 55 + * >70 to 100 65 65 + * >50 to 70 75 75 + * >20 to 50 85 85 + * >10 to 20 95 95 + * >5 to 10 110 110 + * >1 to 5 130 130 + * >0 to 1 167 167 + * <=0 SOLID ON + */ +static const struct ieee80211_tpt_blink iwl_blink[] = { + { .throughput = 0, .blink_time = 334 }, + { .throughput = 1 * 1024 - 1, .blink_time = 260 }, + { .throughput = 5 * 1024 - 1, .blink_time = 220 }, + { .throughput = 10 * 1024 - 1, .blink_time = 190 }, + { .throughput = 20 * 1024 - 1, .blink_time = 170 }, + { .throughput = 50 * 1024 - 1, .blink_time = 150 }, + { .throughput = 70 * 1024 - 1, .blink_time = 130 }, + { .throughput = 100 * 1024 - 1, .blink_time = 110 }, + { .throughput = 200 * 1024 - 1, .blink_time = 80 }, + { .throughput = 300 * 1024 - 1, .blink_time = 50 }, +}; + +/* Set led register off */ +void iwlagn_led_enable(struct iwl_priv *priv) +{ + iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TRUN_ON); +} + +/* + * Adjust led blink rate to compensate on a MAC Clock difference on every HW + * Led blink rate analysis showed an average deviation of 20% on 5000 series + * and up. + * Need to compensate on the led on/off time per HW according to the deviation + * to achieve the desired led frequency + * The calculation is: (100-averageDeviation)/100 * blinkTime + * For code efficiency the calculation will be: + * compensation = (100 - averageDeviation) * 64 / 100 + * NewBlinkTime = (compensation * BlinkTime) / 64 + */ +static inline u8 iwl_blink_compensation(struct iwl_priv *priv, + u8 time, u16 compensation) +{ + if (!compensation) { + IWL_ERR(priv, "undefined blink compensation: " + "use pre-defined blinking time\n"); + return time; + } + + return (u8)((time * compensation) >> 6); +} + +static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_LEDS_CMD, + .len = { sizeof(struct iwl_led_cmd), }, + .data = { led_cmd, }, + .flags = CMD_ASYNC, + }; + u32 reg; + + reg = iwl_read32(priv->trans, CSR_LED_REG); + if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) + iwl_write32(priv->trans, CSR_LED_REG, + reg & CSR_LED_BSM_CTRL_MSK); + + return iwl_dvm_send_cmd(priv, &cmd); +} + +/* Set led pattern command */ +static int iwl_led_cmd(struct iwl_priv *priv, + unsigned long on, + unsigned long off) +{ + struct iwl_led_cmd led_cmd = { + .id = IWL_LED_LINK, + .interval = IWL_DEF_LED_INTRVL + }; + int ret; + + if (!test_bit(STATUS_READY, &priv->status)) + return -EBUSY; + + if (priv->blink_on == on && priv->blink_off == off) + return 0; + + if (off == 0) { + /* led is SOLID_ON */ + on = IWL_LED_SOLID; + } + + IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", + priv->cfg->base_params->led_compensation); + led_cmd.on = iwl_blink_compensation(priv, on, + priv->cfg->base_params->led_compensation); + led_cmd.off = iwl_blink_compensation(priv, off, + priv->cfg->base_params->led_compensation); + + ret = iwl_send_led_cmd(priv, &led_cmd); + if (!ret) { + priv->blink_on = on; + priv->blink_off = off; + } + return ret; +} + +static void iwl_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); + unsigned long on = 0; + + if (brightness > 0) + on = IWL_LED_SOLID; + + iwl_led_cmd(priv, on, 0); +} + +static int iwl_led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); + + return iwl_led_cmd(priv, *delay_on, *delay_off); +} + +void iwl_leds_init(struct iwl_priv *priv) +{ + int mode = iwlwifi_mod_params.led_mode; + int ret; + + if (mode == IWL_LED_DISABLE) { + IWL_INFO(priv, "Led disabled\n"); + return; + } + if (mode == IWL_LED_DEFAULT) + mode = priv->cfg->led_mode; + + priv->led.name = kasprintf(GFP_KERNEL, "%s-led", + wiphy_name(priv->hw->wiphy)); + priv->led.brightness_set = iwl_led_brightness_set; + priv->led.blink_set = iwl_led_blink_set; + priv->led.max_brightness = 1; + + switch (mode) { + case IWL_LED_DEFAULT: + WARN_ON(1); + break; + case IWL_LED_BLINK: + priv->led.default_trigger = + ieee80211_create_tpt_led_trigger(priv->hw, + IEEE80211_TPT_LEDTRIG_FL_CONNECTED, + iwl_blink, ARRAY_SIZE(iwl_blink)); + break; + case IWL_LED_RF_STATE: + priv->led.default_trigger = + ieee80211_get_radio_led_name(priv->hw); + break; + } + + ret = led_classdev_register(priv->trans->dev, &priv->led); + if (ret) { + kfree(priv->led.name); + return; + } + + priv->led_registered = true; +} + +void iwl_leds_exit(struct iwl_priv *priv) +{ + if (!priv->led_registered) + return; + + led_classdev_unregister(&priv->led); + kfree(priv->led.name); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/iwlwifi/dvm/led.h new file mode 100644 index 000000000000..b02a853103d3 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/led.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_leds_h__ +#define __iwl_leds_h__ + + +struct iwl_priv; + +#define IWL_LED_SOLID 11 +#define IWL_DEF_LED_INTRVL cpu_to_le32(1000) + +#define IWL_LED_ACTIVITY (0<<1) +#define IWL_LED_LINK (1<<1) + +void iwlagn_led_enable(struct iwl_priv *priv); +void iwl_leds_init(struct iwl_priv *priv); +void iwl_leds_exit(struct iwl_priv *priv); + +#endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c new file mode 100644 index 000000000000..4ac9af7eb853 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -0,0 +1,1291 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "iwl-io.h" +#include "iwl-agn-hw.h" +#include "iwl-trans.h" +#include "iwl-modparams.h" + +#include "dev.h" +#include "agn.h" + +int iwlagn_hw_valid_rtc_data_addr(u32 addr) +{ + return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && + (addr < IWLAGN_RTC_DATA_UPPER_BOUND); +} + +int iwlagn_send_tx_power(struct iwl_priv *priv) +{ + struct iwlagn_tx_power_dbm_cmd tx_power_cmd; + u8 tx_ant_cfg_cmd; + + if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), + "TX Power requested while scanning!\n")) + return -EAGAIN; + + /* half dBm need to multiply */ + tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); + + if (priv->tx_power_lmt_in_half_dbm && + priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) { + /* + * For the newer devices which using enhanced/extend tx power + * table in EEPROM, the format is in half dBm. driver need to + * convert to dBm format before report to mac80211. + * By doing so, there is a possibility of 1/2 dBm resolution + * lost. driver will perform "round-up" operation before + * reporting, but it will cause 1/2 dBm tx power over the + * regulatory limit. Perform the checking here, if the + * "tx_power_user_lmt" is higher than EEPROM value (in + * half-dBm format), lower the tx power based on EEPROM + */ + tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm; + } + tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; + tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; + + if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) + tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; + else + tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; + + return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC, + sizeof(tx_power_cmd), &tx_power_cmd); +} + +void iwlagn_temperature(struct iwl_priv *priv) +{ + lockdep_assert_held(&priv->statistics.lock); + + /* store temperature from correct statistics (in Celsius) */ + priv->temperature = le32_to_cpu(priv->statistics.common.temperature); + iwl_tt_handler(priv); +} + +int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) +{ + int idx = 0; + int band_offset = 0; + + /* HT rate format: mac80211 wants an MCS number, which is just LSB */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = (rate_n_flags & 0xff); + return idx; + /* Legacy rate format, search for match in table */ + } else { + if (band == IEEE80211_BAND_5GHZ) + band_offset = IWL_FIRST_OFDM_RATE; + for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) + if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) + return idx - band_offset; + } + + return -1; +} + +int iwlagn_manage_ibss_station(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + if (add) + return iwlagn_add_bssid_station(priv, vif_priv->ctx, + vif->bss_conf.bssid, + &vif_priv->ibss_bssid_sta_id); + return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, + vif->bss_conf.bssid); +} + +/** + * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode + * + * pre-requirements: + * 1. acquire mutex before calling + * 2. make sure rf is on and not in exit state + */ +int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +{ + struct iwl_txfifo_flush_cmd flush_cmd; + struct iwl_host_cmd cmd = { + .id = REPLY_TXFIFO_FLUSH, + .len = { sizeof(struct iwl_txfifo_flush_cmd), }, + .flags = CMD_SYNC, + .data = { &flush_cmd, }, + }; + + might_sleep(); + + memset(&flush_cmd, 0, sizeof(flush_cmd)); + if (flush_control & BIT(IWL_RXON_CTX_BSS)) + flush_cmd.fifo_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | + IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | + IWL_SCD_MGMT_MSK; + if ((flush_control & BIT(IWL_RXON_CTX_PAN)) && + (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) + flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK | + IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK | + IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | + IWL_PAN_SCD_MULTICAST_MSK; + + if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) + flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; + + IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", + flush_cmd.fifo_control); + flush_cmd.flush_control = cpu_to_le16(flush_control); + + return iwl_dvm_send_cmd(priv, &cmd); +} + +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +{ + mutex_lock(&priv->mutex); + ieee80211_stop_queues(priv->hw); + if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } + IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); + iwl_trans_wait_tx_queue_empty(priv->trans); +done: + ieee80211_wake_queues(priv->hw); + mutex_unlock(&priv->mutex); +} + +/* + * BT coex + */ +/* Notmal TDM */ +static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + + +/* Loose Coex */ +static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), +}; + +/* Full concurrency */ +static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), +}; + +void iwlagn_send_advance_bt_config(struct iwl_priv *priv) +{ + struct iwl_basic_bt_cmd basic = { + .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, + .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, + .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, + .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, + }; + struct iwl_bt_cmd_v1 bt_cmd_v1; + struct iwl_bt_cmd_v2 bt_cmd_v2; + int ret; + + BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != + sizeof(basic.bt3_lookup_table)); + + if (priv->cfg->bt_params) { + /* + * newer generation of devices (2000 series and newer) + * use the version 2 of the bt command + * we need to make sure sending the host command + * with correct data structure to avoid uCode assert + */ + if (priv->cfg->bt_params->bt_session_2) { + bt_cmd_v2.prio_boost = cpu_to_le32( + priv->cfg->bt_params->bt_prio_boost); + bt_cmd_v2.tx_prio_boost = 0; + bt_cmd_v2.rx_prio_boost = 0; + } else { + bt_cmd_v1.prio_boost = + priv->cfg->bt_params->bt_prio_boost; + bt_cmd_v1.tx_prio_boost = 0; + bt_cmd_v1.rx_prio_boost = 0; + } + } else { + IWL_ERR(priv, "failed to construct BT Coex Config\n"); + return; + } + + /* + * Possible situations when BT needs to take over for receive, + * at the same time where STA needs to response to AP's frame(s), + * reduce the tx power of the required response frames, by that, + * allow the concurrent BT receive & WiFi transmit + * (BT - ANT A, WiFi -ANT B), without interference to one another + * + * Reduced tx power apply to control frames only (ACK/Back/CTS) + * when indicated by the BT config command + */ + basic.kill_ack_mask = priv->kill_ack_mask; + basic.kill_cts_mask = priv->kill_cts_mask; + if (priv->reduced_txpower) + basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; + basic.valid = priv->bt_valid; + + /* + * Configure BT coex mode to "no coexistence" when the + * user disabled BT coexistence, we have no interface + * (might be in monitor mode), or the interface is in + * IBSS mode (no proper uCode support for coex then). + */ + if (!iwlwifi_mod_params.bt_coex_active || + priv->iw_mode == NL80211_IFTYPE_ADHOC) { + basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; + } else { + basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << + IWLAGN_BT_FLAG_COEX_MODE_SHIFT; + + if (!priv->bt_enable_pspoll) + basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; + else + basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; + + if (priv->bt_ch_announce) + basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; + IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); + } + priv->bt_enable_flag = basic.flags; + if (priv->bt_full_concurrent) + memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, + sizeof(iwlagn_concurrent_lookup)); + else + memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, + sizeof(iwlagn_def_3w_lookup)); + + IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", + basic.flags ? "active" : "disabled", + priv->bt_full_concurrent ? + "full concurrency" : "3-wire"); + + if (priv->cfg->bt_params->bt_session_2) { + memcpy(&bt_cmd_v2.basic, &basic, + sizeof(basic)); + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, + CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2); + } else { + memcpy(&bt_cmd_v1.basic, &basic, + sizeof(basic)); + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, + CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1); + } + if (ret) + IWL_ERR(priv, "failed to send BT Coex Config\n"); + +} + +void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) +{ + struct iwl_rxon_context *ctx, *found_ctx = NULL; + bool found_ap = false; + + lockdep_assert_held(&priv->mutex); + + /* Check whether AP or GO mode is active. */ + if (rssi_ena) { + for_each_context(priv, ctx) { + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && + iwl_is_associated_ctx(ctx)) { + found_ap = true; + break; + } + } + } + + /* + * If disable was received or If GO/AP mode, disable RSSI + * measurements. + */ + if (!rssi_ena || found_ap) { + if (priv->cur_rssi_ctx) { + ctx = priv->cur_rssi_ctx; + ieee80211_disable_rssi_reports(ctx->vif); + priv->cur_rssi_ctx = NULL; + } + return; + } + + /* + * If rssi measurements need to be enabled, consider all cases now. + * Figure out how many contexts are active. + */ + for_each_context(priv, ctx) { + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && + iwl_is_associated_ctx(ctx)) { + found_ctx = ctx; + break; + } + } + + /* + * rssi monitor already enabled for the correct interface...nothing + * to do. + */ + if (found_ctx == priv->cur_rssi_ctx) + return; + + /* + * Figure out if rssi monitor is currently enabled, and needs + * to be changed. If rssi monitor is already enabled, disable + * it first else just enable rssi measurements on the + * interface found above. + */ + if (priv->cur_rssi_ctx) { + ctx = priv->cur_rssi_ctx; + if (ctx->vif) + ieee80211_disable_rssi_reports(ctx->vif); + } + + priv->cur_rssi_ctx = found_ctx; + + if (!found_ctx) + return; + + ieee80211_enable_rssi_reports(found_ctx->vif, + IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, + IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); +} + +static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) +{ + return BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3 >> + BT_UART_MSG_FRAME3SCOESCO_POS; +} + +static void iwlagn_bt_traffic_change_work(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, bt_traffic_change_work); + struct iwl_rxon_context *ctx; + int smps_request = -1; + + if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { + /* bt coex disabled */ + return; + } + + /* + * Note: bt_traffic_load can be overridden by scan complete and + * coex profile notifications. Ignore that since only bad consequence + * can be not matching debug print with actual state. + */ + IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", + priv->bt_traffic_load); + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + if (priv->bt_status) + smps_request = IEEE80211_SMPS_DYNAMIC; + else + smps_request = IEEE80211_SMPS_AUTOMATIC; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + smps_request = IEEE80211_SMPS_DYNAMIC; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + smps_request = IEEE80211_SMPS_STATIC; + break; + default: + IWL_ERR(priv, "Invalid BT traffic load: %d\n", + priv->bt_traffic_load); + break; + } + + mutex_lock(&priv->mutex); + + /* + * We can not send command to firmware while scanning. When the scan + * complete we will schedule this work again. We do check with mutex + * locked to prevent new scan request to arrive. We do not check + * STATUS_SCANNING to avoid race when queue_work two times from + * different notifications, but quit and not perform any work at all. + */ + if (test_bit(STATUS_SCAN_HW, &priv->status)) + goto out; + + iwl_update_chain_flags(priv); + + if (smps_request != -1) { + priv->current_ht_config.smps = smps_request; + for_each_context(priv, ctx) { + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) + ieee80211_request_smps(ctx->vif, smps_request); + } + } + + /* + * Dynamic PS poll related functionality. Adjust RSSI measurements if + * necessary. + */ + iwlagn_bt_coex_rssi_monitor(priv); +out: + mutex_unlock(&priv->mutex); +} + +/* + * If BT sco traffic, and RSSI monitor is enabled, move measurements to the + * correct interface or disable it if this is the last interface to be + * removed. + */ +void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) +{ + if (priv->bt_is_sco && + priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) + iwlagn_bt_adjust_rssi_monitor(priv, true); + else + iwlagn_bt_adjust_rssi_monitor(priv, false); +} + +static void iwlagn_print_uartmsg(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " + "Update Req = 0x%X\n", + (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> + BT_UART_MSG_FRAME1MSGTYPE_POS, + (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> + BT_UART_MSG_FRAME1SSN_POS, + (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> + BT_UART_MSG_FRAME1UPDATEREQ_POS); + + IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " + "Chl_SeqN = 0x%X, In band = 0x%X\n", + (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, + (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2TRAFFICLOAD_POS, + (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2CHLSEQN_POS, + (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> + BT_UART_MSG_FRAME2INBAND_POS); + + IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " + "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", + (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3SCOESCO_POS, + (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3SNIFF_POS, + (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3A2DP_POS, + (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3ACL_POS, + (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3MASTER_POS, + (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> + BT_UART_MSG_FRAME3OBEX_POS); + + IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", + (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> + BT_UART_MSG_FRAME4IDLEDURATION_POS); + + IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " + "eSCO Retransmissions = 0x%X\n", + (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> + BT_UART_MSG_FRAME5TXACTIVITY_POS, + (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> + BT_UART_MSG_FRAME5RXACTIVITY_POS, + (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> + BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); + + IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", + (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> + BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, + (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> + BT_UART_MSG_FRAME6DISCOVERABLE_POS); + + IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " + "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", + (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, + (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7PAGE_POS, + (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7INQUIRY_POS, + (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7CONNECTABLE_POS); +} + +static bool iwlagn_set_kill_msk(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + bool need_update = false; + u8 kill_msk = IWL_BT_KILL_REDUCE; + static const __le32 bt_kill_ack_msg[3] = { + IWLAGN_BT_KILL_ACK_MASK_DEFAULT, + IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, + IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; + static const __le32 bt_kill_cts_msg[3] = { + IWLAGN_BT_KILL_CTS_MASK_DEFAULT, + IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, + IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; + + if (!priv->reduced_txpower) + kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) + ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; + if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || + priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { + priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; + priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; + priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; + priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; + need_update = true; + } + return need_update; +} + +/* + * Upon RSSI changes, sends a bt config command with following changes + * 1. enable/disable "reduced control frames tx power + * 2. update the "kill)ack_mask" and "kill_cts_mask" + * + * If "reduced tx power" is enabled, uCode shall + * 1. ACK/Back/CTS rate shall reduced to 6Mbps + * 2. not use duplciate 20/40MHz mode + */ +static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, + struct iwl_bt_uart_msg *uart_msg) +{ + bool need_update = false; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + int ave_rssi; + + if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { + IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); + return false; + } + + ave_rssi = ieee80211_ave_rssi(ctx->vif); + if (!ave_rssi) { + /* no rssi data, no changes to reduce tx power */ + IWL_DEBUG_COEX(priv, "no rssi data available\n"); + return need_update; + } + if (!priv->reduced_txpower && + !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && + (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && + (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | + BT_UART_MSG_FRAME3OBEX_MSK)) && + !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | + BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { + /* enabling reduced tx power */ + priv->reduced_txpower = true; + priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; + need_update = true; + } else if (priv->reduced_txpower && + (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || + (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || + (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | + BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || + !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | + BT_UART_MSG_FRAME3OBEX_MSK)))) { + /* disable reduced tx power */ + priv->reduced_txpower = false; + priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; + need_update = true; + } + + return need_update; +} + +int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; + struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; + + if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { + /* bt coex disabled */ + return 0; + } + + IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); + IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); + IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); + IWL_DEBUG_COEX(priv, " CI compliance: %d\n", + coex->bt_ci_compliance); + iwlagn_print_uartmsg(priv, uart_msg); + + priv->last_bt_traffic_load = priv->bt_traffic_load; + priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); + + if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { + if (priv->bt_status != coex->bt_status || + priv->last_bt_traffic_load != coex->bt_traffic_load) { + if (coex->bt_status) { + /* BT on */ + if (!priv->bt_ch_announce) + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + else + priv->bt_traffic_load = + coex->bt_traffic_load; + } else { + /* BT off */ + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_NONE; + } + priv->bt_status = coex->bt_status; + queue_work(priv->workqueue, + &priv->bt_traffic_change_work); + } + } + + /* schedule to send runtime bt_config */ + /* check reduce power before change ack/cts kill mask */ + if (iwlagn_fill_txpower_mode(priv, uart_msg) || + iwlagn_set_kill_msk(priv, uart_msg)) + queue_work(priv->workqueue, &priv->bt_runtime_config); + + + /* FIXME: based on notification, adjust the prio_boost */ + + priv->bt_ci_compliance = coex->bt_ci_compliance; + return 0; +} + +void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) +{ + priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = + iwlagn_bt_coex_profile_notif; +} + +void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) +{ + INIT_WORK(&priv->bt_traffic_change_work, + iwlagn_bt_traffic_change_work); +} + +void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) +{ + cancel_work_sync(&priv->bt_traffic_change_work); +} + +static bool is_single_rx_stream(struct iwl_priv *priv) +{ + return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || + priv->current_ht_config.single_chain_sufficient; +} + +#define IWL_NUM_RX_CHAINS_MULTIPLE 3 +#define IWL_NUM_RX_CHAINS_SINGLE 2 +#define IWL_NUM_IDLE_CHAINS_DUAL 2 +#define IWL_NUM_IDLE_CHAINS_SINGLE 1 + +/* + * Determine how many receiver/antenna chains to use. + * + * More provides better reception via diversity. Fewer saves power + * at the expense of throughput, but only when not in powersave to + * start with. + * + * MIMO (dual stream) requires at least 2, but works better with 3. + * This does not determine *which* chains to use, just how many. + */ +static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) +{ + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist && + (priv->bt_full_concurrent || + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { + /* + * only use chain 'A' in bt high traffic load or + * full concurrency mode + */ + return IWL_NUM_RX_CHAINS_SINGLE; + } + /* # of Rx chains to use when expecting MIMO. */ + if (is_single_rx_stream(priv)) + return IWL_NUM_RX_CHAINS_SINGLE; + else + return IWL_NUM_RX_CHAINS_MULTIPLE; +} + +/* + * When we are in power saving mode, unless device support spatial + * multiplexing power save, use the active count for rx chain count. + */ +static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) +{ + /* # Rx chains when idling, depending on SMPS mode */ + switch (priv->current_ht_config.smps) { + case IEEE80211_SMPS_STATIC: + case IEEE80211_SMPS_DYNAMIC: + return IWL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_OFF: + return active_cnt; + default: + WARN(1, "invalid SMPS mode %d", + priv->current_ht_config.smps); + return active_cnt; + } +} + +/* up to 4 chains */ +static u8 iwl_count_chain_bitmap(u32 chain_bitmap) +{ + u8 res; + res = (chain_bitmap & BIT(0)) >> 0; + res += (chain_bitmap & BIT(1)) >> 1; + res += (chain_bitmap & BIT(2)) >> 2; + res += (chain_bitmap & BIT(3)) >> 3; + return res; +} + +/** + * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image + * + * Selects how many and which Rx receivers/antennas/chains to use. + * This should not be used for scan command ... it puts data in wrong place. + */ +void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + bool is_single = is_single_rx_stream(priv); + bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); + u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; + u32 active_chains; + u16 rx_chain; + + /* Tell uCode which antennas are actually connected. + * Before first association, we assume all antennas are connected. + * Just after first association, iwl_chain_noise_calibration() + * checks which antennas actually *are* connected. */ + if (priv->chain_noise_data.active_chains) + active_chains = priv->chain_noise_data.active_chains; + else + active_chains = priv->hw_params.valid_rx_ant; + + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist && + (priv->bt_full_concurrent || + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { + /* + * only use chain 'A' in bt high traffic load or + * full concurrency mode + */ + active_chains = first_antenna(active_chains); + } + + rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; + + /* How many receivers should we use? */ + active_rx_cnt = iwl_get_active_rx_chain_count(priv); + idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); + + + /* correct rx chain count according hw settings + * and chain noise calibration + */ + valid_rx_cnt = iwl_count_chain_bitmap(active_chains); + if (valid_rx_cnt < active_rx_cnt) + active_rx_cnt = valid_rx_cnt; + + if (valid_rx_cnt < idle_rx_cnt) + idle_rx_cnt = valid_rx_cnt; + + rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; + rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; + + ctx->staging.rx_chain = cpu_to_le16(rx_chain); + + if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) + ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; + else + ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; + + IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", + ctx->staging.rx_chain, + active_rx_cnt, idle_rx_cnt); + + WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || + active_rx_cnt < idle_rx_cnt); +} + +u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) +{ + int i; + u8 ind = ant; + + if (priv->band == IEEE80211_BAND_2GHZ && + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) + return 0; + + for (i = 0; i < RATE_ANT_NUM - 1; i++) { + ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; + if (valid & BIT(ind)) + return ind; + } + return ant; +} + +#ifdef CONFIG_PM_SLEEP +static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) +{ + int i; + + for (i = 0; i < IWLAGN_P1K_SIZE; i++) + out[i] = cpu_to_le16(p1k[i]); +} + +struct wowlan_key_data { + struct iwl_rxon_context *ctx; + struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; + struct iwlagn_wowlan_tkip_params_cmd *tkip; + const u8 *bssid; + bool error, use_rsc_tsc, use_tkip; +}; + + +static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct wowlan_key_data *data = _data; + struct iwl_rxon_context *ctx = data->ctx; + struct aes_sc *aes_sc, *aes_tx_sc = NULL; + struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; + struct iwlagn_p1k_cache *rx_p1ks; + u8 *rx_mic_key; + struct ieee80211_key_seq seq; + u32 cur_rx_iv32 = 0; + u16 p1k[IWLAGN_P1K_SIZE]; + int ret, i; + + mutex_lock(&priv->mutex); + + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + !sta && !ctx->key_mapping_keys) + ret = iwl_set_default_wep_key(priv, ctx, key); + else + ret = iwl_set_dynamic_key(priv, ctx, key, sta); + + if (ret) { + IWL_ERR(priv, "Error setting key during suspend!\n"); + data->error = true; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (sta) { + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; + tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; + + rx_p1ks = data->tkip->rx_uni; + + ieee80211_get_key_tx_seq(key, &seq); + tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + + ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); + iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); + + memcpy(data->tkip->mic_keys.tx, + &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + IWLAGN_MIC_KEY_SIZE); + + rx_mic_key = data->tkip->mic_keys.rx_unicast; + } else { + tkip_sc = + data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; + rx_p1ks = data->tkip->rx_multi; + rx_mic_key = data->tkip->mic_keys.rx_mcast; + } + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 (as they need to to avoid replay attacks) + * for checking the IV in the frames. + */ + for (i = 0; i < IWLAGN_NUM_RSC; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); + /* wrapping isn't allowed, AP must rekey */ + if (seq.tkip.iv32 > cur_rx_iv32) + cur_rx_iv32 = seq.tkip.iv32; + } + + ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); + iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); + ieee80211_get_tkip_rx_p1k(key, data->bssid, + cur_rx_iv32 + 1, p1k); + iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); + + memcpy(rx_mic_key, + &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + IWLAGN_MIC_KEY_SIZE); + + data->use_tkip = true; + data->use_rsc_tsc = true; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (sta) { + u8 *pn = seq.ccmp.pn; + + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; + aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; + + ieee80211_get_key_tx_seq(key, &seq); + aes_tx_sc->pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } else + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 for checking the IV in the frames. + */ + for (i = 0; i < IWLAGN_NUM_RSC; i++) { + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + aes_sc->pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } + data->use_rsc_tsc = true; + break; + } + + mutex_unlock(&priv->mutex); +} + +int iwlagn_send_patterns(struct iwl_priv *priv, + struct cfg80211_wowlan *wowlan) +{ + struct iwlagn_wowlan_patterns_cmd *pattern_cmd; + struct iwl_host_cmd cmd = { + .id = REPLY_WOWLAN_PATTERNS, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .flags = CMD_SYNC, + }; + int i, err; + + if (!wowlan->n_patterns) + return 0; + + cmd.len[0] = sizeof(*pattern_cmd) + + wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); + + pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); + if (!pattern_cmd) + return -ENOMEM; + + pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); + + for (i = 0; i < wowlan->n_patterns; i++) { + int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + + memcpy(&pattern_cmd->patterns[i].mask, + wowlan->patterns[i].mask, mask_len); + memcpy(&pattern_cmd->patterns[i].pattern, + wowlan->patterns[i].pattern, + wowlan->patterns[i].pattern_len); + pattern_cmd->patterns[i].mask_size = mask_len; + pattern_cmd->patterns[i].pattern_size = + wowlan->patterns[i].pattern_len; + } + + cmd.data[0] = pattern_cmd; + err = iwl_dvm_send_cmd(priv, &cmd); + kfree(pattern_cmd); + return err; +} + +int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) +{ + struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; + struct iwl_rxon_cmd rxon; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; + struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; + struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; + struct wowlan_key_data key_data = { + .ctx = ctx, + .bssid = ctx->active.bssid_addr, + .use_rsc_tsc = false, + .tkip = &tkip_cmd, + .use_tkip = false, + }; + int ret, i; + u16 seq; + + key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); + if (!key_data.rsc_tsc) + return -ENOMEM; + + memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); + + /* + * We know the last used seqno, and the uCode expects to know that + * one, it will increment before TX. + */ + seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; + wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); + + /* + * For QoS counters, we store the one to use next, so subtract 0x10 + * since the uCode will add 0x10 before using the value. + */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + seq = priv->tid_data[IWL_AP_ID][i].seq_number; + seq -= 0x10; + wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); + } + + if (wowlan->disconnect) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | + IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); + if (wowlan->magic_pkt) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); + if (wowlan->gtk_rekey_failure) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); + if (wowlan->eap_identity_req) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); + if (wowlan->four_way_handshake) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); + if (wowlan->n_patterns) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); + + if (wowlan->rfkill_release) + d3_cfg_cmd.wakeup_flags |= + cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); + + iwl_scan_cancel_timeout(priv, 200); + + memcpy(&rxon, &ctx->active, sizeof(rxon)); + + priv->ucode_loaded = false; + iwl_trans_stop_device(priv->trans); + + priv->wowlan = true; + + ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); + if (ret) + goto out; + + /* now configure WoWLAN ucode */ + ret = iwl_alive_start(priv); + if (ret) + goto out; + + memcpy(&ctx->staging, &rxon, sizeof(rxon)); + ret = iwlagn_commit_rxon(priv, ctx); + if (ret) + goto out; + + ret = iwl_power_update_mode(priv, true); + if (ret) + goto out; + + if (!iwlwifi_mod_params.sw_crypto) { + /* mark all keys clear */ + priv->ucode_key_table = 0; + ctx->key_mapping_keys = 0; + + /* + * This needs to be unlocked due to lock ordering + * constraints. Since we're in the suspend path + * that isn't really a problem though. + */ + mutex_unlock(&priv->mutex); + ieee80211_iter_keys(priv->hw, ctx->vif, + iwlagn_wowlan_program_keys, + &key_data); + mutex_lock(&priv->mutex); + if (key_data.error) { + ret = -EIO; + goto out; + } + + if (key_data.use_rsc_tsc) { + struct iwl_host_cmd rsc_tsc_cmd = { + .id = REPLY_WOWLAN_TSC_RSC_PARAMS, + .flags = CMD_SYNC, + .data[0] = key_data.rsc_tsc, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .len[0] = sizeof(*key_data.rsc_tsc), + }; + + ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); + if (ret) + goto out; + } + + if (key_data.use_tkip) { + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_WOWLAN_TKIP_PARAMS, + CMD_SYNC, sizeof(tkip_cmd), + &tkip_cmd); + if (ret) + goto out; + } + + if (priv->have_rekey_data) { + memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); + memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); + kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); + memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); + kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); + kek_kck_cmd.replay_ctr = priv->replay_ctr; + + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_WOWLAN_KEK_KCK_MATERIAL, + CMD_SYNC, sizeof(kek_kck_cmd), + &kek_kck_cmd); + if (ret) + goto out; + } + } + + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC, + sizeof(d3_cfg_cmd), &d3_cfg_cmd); + if (ret) + goto out; + + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, + CMD_SYNC, sizeof(wakeup_filter_cmd), + &wakeup_filter_cmd); + if (ret) + goto out; + + ret = iwlagn_send_patterns(priv, wowlan); + out: + kfree(key_data.rsc_tsc); + return ret; +} +#endif + +int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) +{ + if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { + IWL_WARN(priv, "Not sending command - %s KILL\n", + iwl_is_rfkill(priv) ? "RF" : "CT"); + return -EIO; + } + + if (test_bit(STATUS_FW_ERROR, &priv->status)) { + IWL_ERR(priv, "Command %s failed: FW Error\n", + iwl_dvm_get_cmd_string(cmd->id)); + return -EIO; + } + + /* + * Synchronous commands from this op-mode must hold + * the mutex, this ensures we don't try to send two + * (or more) synchronous commands at a time. + */ + if (cmd->flags & CMD_SYNC) + lockdep_assert_held(&priv->mutex); + + if (priv->ucode_owner == IWL_OWNERSHIP_TM && + !(cmd->flags & CMD_ON_DEMAND)) { + IWL_DEBUG_HC(priv, "tm own the uCode, no regular hcmd send\n"); + return -EIO; + } + + return iwl_trans_send_cmd(priv->trans, cmd); +} + +int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, + u32 flags, u16 len, const void *data) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = { len, }, + .data = { data, }, + .flags = flags, + }; + + return iwl_dvm_send_cmd(priv, &cmd); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c new file mode 100644 index 000000000000..33603c5a24a8 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -0,0 +1,1631 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-modparams.h" + +#include "eeprom.h" +#include "dev.h" +#include "calib.h" +#include "agn.h" + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +}; + +static const struct ieee80211_iface_combination +iwlagn_iface_combinations_dualmode[] = { + { .num_different_channels = 1, + .max_interfaces = 2, + .beacon_int_infra_match = true, + .limits = iwlagn_sta_ap_limits, + .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), + }, + { .num_different_channels = 1, + .max_interfaces = 2, + .limits = iwlagn_2sta_limits, + .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), + }, +}; + +static const struct ieee80211_iface_combination +iwlagn_iface_combinations_p2p[] = { + { .num_different_channels = 1, + .max_interfaces = 2, + .beacon_int_infra_match = true, + .limits = iwlagn_p2p_sta_go_limits, + .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits), + }, + { .num_different_channels = 1, + .max_interfaces = 2, + .limits = iwlagn_p2p_2sta_limits, + .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits), + }, +}; + +/* + * Not a mac80211 entry point function, but it fits in with all the + * other mac80211 functions grouped here. + */ +int iwlagn_mac_setup_register(struct iwl_priv *priv, + const struct iwl_ucode_capabilities *capa) +{ + int ret; + struct ieee80211_hw *hw = priv->hw; + struct iwl_rxon_context *ctx; + + hw->rate_control_algorithm = "iwl-agn-rs"; + + /* Tell mac80211 our characteristics */ + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_NEED_DTIM_PERIOD | + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_QUEUE_CONTROL | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_SCAN_WHILE_IDLE; + + hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; + + /* + * Including the following line will crash some AP's. This + * workaround removes the stimulus which causes the crash until + * the AP software can be fixed. + hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + */ + + if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) + hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_SUPPORTS_STATIC_SMPS; + +#ifndef CONFIG_IWLWIFI_EXPERIMENTAL_MFP + /* enable 11w if the uCode advertise */ + if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) +#endif /* !CONFIG_IWLWIFI_EXPERIMENTAL_MFP */ + hw->flags |= IEEE80211_HW_MFP_CAPABLE; + + hw->sta_data_size = sizeof(struct iwl_station_priv); + hw->vif_data_size = sizeof(struct iwl_vif_priv); + + for_each_context(priv, ctx) { + hw->wiphy->interface_modes |= ctx->interface_modes; + hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; + } + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) { + hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(iwlagn_iface_combinations_p2p); + } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { + hw->wiphy->iface_combinations = + iwlagn_iface_combinations_dualmode; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(iwlagn_iface_combinations_dualmode); + } + + hw->wiphy->max_remain_on_channel_duration = 1000; + + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | + WIPHY_FLAG_DISABLE_BEACON_HINTS | + WIPHY_FLAG_IBSS_RSN; + + if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && + priv->trans->ops->wowlan_suspend && + device_can_wakeup(priv->trans->dev)) { + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE; + if (!iwlwifi_mod_params.sw_crypto) + hw->wiphy->wowlan.flags |= + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE; + + hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; + hw->wiphy->wowlan.pattern_min_len = + IWLAGN_WOWLAN_MIN_PATTERN_LEN; + hw->wiphy->wowlan.pattern_max_len = + IWLAGN_WOWLAN_MAX_PATTERN_LEN; + } + + if (iwlwifi_mod_params.power_save) + hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + else + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + /* we create the 802.11 header and a max-length SSID element */ + hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34; + + /* + * We don't use all queues: 4 and 9 are unused and any + * aggregation queue gets mapped down to the AC queue. + */ + hw->queues = IWLAGN_FIRST_AMPDU_QUEUE; + + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + + if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->bands[IEEE80211_BAND_2GHZ]; + if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &priv->bands[IEEE80211_BAND_5GHZ]; + + hw->wiphy->hw_version = priv->trans->hw_id; + + iwl_leds_init(priv); + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); + return ret; + } + priv->mac80211_registered = 1; + + return 0; +} + +void iwlagn_mac_unregister(struct iwl_priv *priv) +{ + if (!priv->mac80211_registered) + return; + iwl_leds_exit(priv); + ieee80211_unregister_hw(priv->hw); + priv->mac80211_registered = 0; +} + +static int __iwl_up(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int ret; + + lockdep_assert_held(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + for_each_context(priv, ctx) { + ret = iwlagn_alloc_bcast_station(priv, ctx); + if (ret) { + iwl_dealloc_bcast_stations(priv); + return ret; + } + } + + ret = iwl_run_init_ucode(priv); + if (ret) { + IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); + goto error; + } + + ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); + if (ret) { + IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); + goto error; + } + + ret = iwl_alive_start(priv); + if (ret) + goto error; + return 0; + + error: + set_bit(STATUS_EXIT_PENDING, &priv->status); + iwl_down(priv); + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + IWL_ERR(priv, "Unable to initialize device.\n"); + return ret; +} + +static int iwlagn_mac_start(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&priv->mutex); + ret = __iwl_up(priv); + mutex_unlock(&priv->mutex); + if (ret) + return ret; + + IWL_DEBUG_INFO(priv, "Start UP work done.\n"); + + /* Now we should be done, and the READY bit should be set. */ + if (WARN_ON(!test_bit(STATUS_READY, &priv->status))) + ret = -EIO; + + iwlagn_led_enable(priv); + + priv->is_open = 1; + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +static void iwlagn_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!priv->is_open) + return; + + priv->is_open = 0; + + mutex_lock(&priv->mutex); + iwl_down(priv); + mutex_unlock(&priv->mutex); + + iwl_cancel_deferred_work(priv); + + flush_workqueue(priv->workqueue); + + /* User space software may expect getting rfkill changes + * even if interface is down, trans->down will leave the RF + * kill interrupt enabled + */ + iwl_trans_stop_hw(priv->trans, false); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + if (iwlwifi_mod_params.sw_crypto) + return; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) + goto out; + + memcpy(priv->kek, data->kek, NL80211_KEK_LEN); + memcpy(priv->kck, data->kck, NL80211_KCK_LEN); + priv->replay_ctr = + cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); + priv->have_rekey_data = true; + + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +#ifdef CONFIG_PM_SLEEP + +static int iwlagn_mac_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + int ret; + + if (WARN_ON(!wowlan)) + return -EINVAL; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + /* Don't attempt WoWLAN when not associated, tear down instead. */ + if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || + !iwl_is_associated_ctx(ctx)) { + ret = 1; + goto out; + } + + ret = iwlagn_suspend(priv, wowlan); + if (ret) + goto error; + + iwl_trans_wowlan_suspend(priv->trans); + + goto out; + + error: + priv->wowlan = false; + iwlagn_prepare_restart(priv); + ieee80211_restart_hw(priv->hw); + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static int iwlagn_mac_resume(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct ieee80211_vif *vif; + unsigned long flags; + u32 base, status = 0xffffffff; + int ret = -EIO; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + base = priv->device_pointers.error_event_table; + if (iwlagn_hw_valid_rtc_data_addr(base)) { + spin_lock_irqsave(&priv->trans->reg_lock, flags); + ret = iwl_grab_nic_access_silent(priv->trans); + if (likely(ret == 0)) { + iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base); + status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + iwl_release_nic_access(priv->trans); + } + spin_unlock_irqrestore(&priv->trans->reg_lock, flags); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (ret == 0) { + const struct fw_img *img; + + img = &(priv->fw->img[IWL_UCODE_WOWLAN]); + if (!priv->wowlan_sram) { + priv->wowlan_sram = + kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, + GFP_KERNEL); + } + + if (priv->wowlan_sram) + _iwl_read_targ_mem_words( + priv->trans, 0x800000, + priv->wowlan_sram, + img->sec[IWL_UCODE_SECTION_DATA].len / 4); + } +#endif + } + + /* we'll clear ctx->vif during iwlagn_prepare_restart() */ + vif = ctx->vif; + + priv->wowlan = false; + + iwlagn_prepare_restart(priv); + + memset((void *)&ctx->active, 0, sizeof(ctx->active)); + iwl_connection_init_rx_config(priv, ctx); + iwlagn_set_rxon_chain(priv, ctx); + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + ieee80211_resume_disconnect(vif); + + return 1; +} + +static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + device_set_wakeup_enable(priv->trans->dev, enabled); +} +#endif + +static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, + ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); + + if (iwlagn_tx_skb(priv, skb)) + dev_kfree_skb_any(skb); +} + +static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); +} + +static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int ret; + bool is_default_wep_key = false; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (iwlwifi_mod_params.sw_crypto) { + IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + /* fall through */ + case WLAN_CIPHER_SUITE_CCMP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + break; + default: + break; + } + + /* + * We could program these keys into the hardware as well, but we + * don't expect much multicast traffic in IBSS and having keys + * for more stations is probably more useful. + * + * Mark key TX-only and return 0. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + key->hw_key_idx = WEP_INVALID_OFFSET; + return 0; + } + + /* If they key was TX-only, accept deletion */ + if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) + return 0; + + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, 100); + + BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); + + /* + * If we are getting WEP group key and we didn't receive any key mapping + * so far, we are in legacy wep mode (group key only), otherwise we are + * in 1X mode. + * In legacy wep mode, we use another host command to the uCode. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { + if (cmd == SET_KEY) + is_default_wep_key = !ctx->key_mapping_keys; + else + is_default_wep_key = + key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; + } + + + switch (cmd) { + case SET_KEY: + if (is_default_wep_key) { + ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); + break; + } + ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); + if (ret) { + /* + * can't add key for RX, but we don't need it + * in the device for TX so still return 0 + */ + ret = 0; + key->hw_key_idx = WEP_INVALID_OFFSET; + } + + IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (is_default_wep_key) + ret = iwl_remove_default_wep_key(priv, ctx, key); + else + ret = iwl_remove_dynamic_key(priv, ctx, key, sta); + + IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int ret = -EINVAL; + struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; + + IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", + sta->addr, tid); + + if (!(priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE)) + return -EACCES; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + break; + IWL_DEBUG_HT(priv, "start Rx\n"); + ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); + break; + case IEEE80211_AMPDU_RX_STOP: + IWL_DEBUG_HT(priv, "stop Rx\n"); + ret = iwl_sta_rx_agg_stop(priv, sta, tid); + break; + case IEEE80211_AMPDU_TX_START: + if (!priv->trans->ops->tx_agg_setup) + break; + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + break; + IWL_DEBUG_HT(priv, "start Tx\n"); + ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); + break; + case IEEE80211_AMPDU_TX_STOP: + IWL_DEBUG_HT(priv, "stop Tx\n"); + ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); + if ((ret == 0) && (priv->agg_tids_count > 0)) { + priv->agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + } + if (!priv->agg_tids_count && + priv->hw_params.use_rts_for_aggregation) { + /* + * switch off RTS/CTS if it was previously enabled + */ + sta_priv->lq_sta.lq.general_params.flags &= + ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; + iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), + &sta_priv->lq_sta.lq, CMD_ASYNC, false); + } + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); + break; + } + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + return ret; +} + +static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + bool is_ap = vif->type == NL80211_IFTYPE_STATION; + int ret; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", + sta->addr); + sta_priv->sta_id = IWL_INVALID_STATION; + + atomic_set(&sta_priv->pending_frames, 0); + if (vif->type == NL80211_IFTYPE_AP) + sta_priv->client = true; + + ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, + is_ap, sta, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + return ret; + } + + sta_priv->sta_id = sta_id; + + return 0; +} + +static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int ret; + + IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); + + if (vif->type == NL80211_IFTYPE_STATION) { + /* + * Station will be removed from device when the RXON + * is set to unassociated -- just deactivate it here + * to avoid re-programming it. + */ + ret = 0; + iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr); + } else { + ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, + "Error removing station %pM\n", sta->addr); + } + return ret; +} + +static int iwlagn_mac_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + enum { + NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT, + } op = NONE; + int ret; + + IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n", + sta->addr, old_state, new_state); + + mutex_lock(&priv->mutex); + if (vif->type == NL80211_IFTYPE_STATION) { + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + op = ADD; + else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + op = REMOVE; + else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + op = HT_RATE_INIT; + } else { + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + op = ADD_RATE_INIT; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) + op = REMOVE; + } + + switch (op) { + case ADD: + ret = iwlagn_mac_sta_add(hw, vif, sta); + break; + case REMOVE: + ret = iwlagn_mac_sta_remove(hw, vif, sta); + break; + case ADD_RATE_INIT: + ret = iwlagn_mac_sta_add(hw, vif, sta); + if (ret) + break; + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, + "Initializing rate scaling for station %pM\n", + sta->addr); + iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); + ret = 0; + break; + case HT_RATE_INIT: + /* Initialize rate scaling */ + ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta); + if (ret) + break; + IWL_DEBUG_INFO(priv, + "Initializing rate scaling for station %pM\n", + sta->addr); + iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); + ret = 0; + break; + default: + ret = 0; + break; + } + + /* + * mac80211 might WARN if we fail, but due the way we + * (badly) handle hard rfkill, we might fail here + */ + if (iwl_is_rfkill(priv)) + ret = 0; + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_channel_switch *ch_switch) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = ch_switch->channel; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + /* + * MULTI-FIXME + * When we add support for multiple interfaces, we need to + * revisit this. The channel switch command in the device + * only affects the BSS context, but what does that really + * mean? And what if we get a CSA on the second interface? + * This needs a lot of work. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u16 ch; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->mutex); + + if (iwl_is_rfkill(priv)) + goto out; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status) || + test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + goto out; + + if (!iwl_is_associated_ctx(ctx)) + goto out; + + if (!priv->lib->set_channel_switch) + goto out; + + ch = channel->hw_value; + if (le16_to_cpu(ctx->active.channel) == ch) + goto out; + + priv->current_ht_config.smps = conf->smps_mode; + + /* Configure HT40 channels */ + ctx->ht.enabled = conf_is_ht(conf); + if (ctx->ht.enabled) + iwlagn_config_ht40(conf, ctx); + else + ctx->ht.is_40mhz = false; + + if ((le16_to_cpu(ctx->staging.channel) != ch)) + ctx->staging.flags = 0; + + iwl_set_rxon_channel(priv, channel, ctx); + iwl_set_rxon_ht(priv, ht_conf); + iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); + + /* + * at this point, staging_rxon has the + * configuration for channel switch + */ + set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); + priv->switch_channel = cpu_to_le16(ch); + if (priv->lib->set_channel_switch(priv, ch_switch)) { + clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); + priv->switch_channel = 0; + ieee80211_chswitch_done(ctx->vif, false); + } + +out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) +{ + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + ieee80211_chswitch_done(ctx->vif, is_success); +} + +static void iwlagn_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + __le32 filter_or = 0, filter_nand = 0; + struct iwl_rxon_context *ctx; + +#define CHK(test, flag) do { \ + if (*total_flags & (test)) \ + filter_or |= (flag); \ + else \ + filter_nand |= (flag); \ + } while (0) + + IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", + changed_flags, *total_flags); + + CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); + /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ + CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); + CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); + +#undef CHK + + mutex_lock(&priv->mutex); + + for_each_context(priv, ctx) { + ctx->staging.filter_flags &= ~filter_nand; + ctx->staging.filter_flags |= filter_or; + + /* + * Not committing directly because hardware can perform a scan, + * but we'll eventually commit the filter flags change anyway. + */ + } + + mutex_unlock(&priv->mutex); + + /* + * Receiving all multicast frames is always enabled by the + * default flags setup in iwl_connection_init_rx_config() + * since we currently do not support programming multicast + * filters into the device. + */ + *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); + goto done; + } + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); + goto done; + } + + /* + * mac80211 will not push any more frames for transmit + * until the flush is completed + */ + if (drop) { + IWL_DEBUG_MAC80211(priv, "send flush command\n"); + if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } + } + IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n"); + iwl_trans_wait_tx_queue_empty(priv->trans); +done: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + int duration) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; + int err = 0; + + if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) + return -EOPNOTSUPP; + + if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT))) + return -EOPNOTSUPP; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + err = -EBUSY; + goto out; + } + + priv->hw_roc_channel = channel; + priv->hw_roc_chantype = channel_type; + /* convert from ms to TU */ + priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); + priv->hw_roc_start_notified = false; + cancel_delayed_work(&priv->hw_roc_disable_work); + + if (!ctx->is_active) { + static const struct iwl_qos_info default_qos_data = { + .def_qos_parm = { + .ac[0] = { + .cw_min = cpu_to_le16(3), + .cw_max = cpu_to_le16(7), + .aifsn = 2, + .edca_txop = cpu_to_le16(1504), + }, + .ac[1] = { + .cw_min = cpu_to_le16(7), + .cw_max = cpu_to_le16(15), + .aifsn = 2, + .edca_txop = cpu_to_le16(3008), + }, + .ac[2] = { + .cw_min = cpu_to_le16(15), + .cw_max = cpu_to_le16(1023), + .aifsn = 3, + }, + .ac[3] = { + .cw_min = cpu_to_le16(15), + .cw_max = cpu_to_le16(1023), + .aifsn = 7, + }, + }, + }; + + ctx->is_active = true; + ctx->qos_data = default_qos_data; + ctx->staging.dev_type = RXON_DEV_TYPE_P2P; + memcpy(ctx->staging.node_addr, + priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, + ETH_ALEN); + memcpy(ctx->staging.bssid_addr, + priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, + ETH_ALEN); + err = iwlagn_commit_rxon(priv, ctx); + if (err) + goto out; + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK | + RXON_FILTER_PROMISC_MSK | + RXON_FILTER_CTL2HOST_MSK; + + err = iwlagn_commit_rxon(priv, ctx); + if (err) { + iwlagn_disable_roc(priv); + goto out; + } + priv->hw_roc_setup = true; + } + + err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band); + if (err) + iwlagn_disable_roc(priv); + + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return err; +} + +static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) + return -EOPNOTSUPP; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, priv->hw_roc_duration); + iwlagn_disable_roc(priv); + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return 0; +} + +static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, + enum ieee80211_rssi_event rssi_event) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->mutex); + + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + if (rssi_event == RSSI_EVENT_LOW) + priv->bt_enable_pspoll = true; + else if (rssi_event == RSSI_EVENT_HIGH) + priv->bt_enable_pspoll = false; + + iwlagn_send_advance_bt_config(priv); + } else { + IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," + "ignoring RSSI callback\n"); + } + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, bool set) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + queue_work(priv->workqueue, &priv->beacon_update); + + return 0; +} + +static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int q; + + if (WARN_ON(!ctx)) + return -EINVAL; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!iwl_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); + return -EIO; + } + + if (queue >= AC_NUM) { + IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); + return 0; + } + + q = AC_NUM - 1 - queue; + + mutex_lock(&priv->mutex); + + ctx->qos_data.def_qos_parm.ac[q].cw_min = + cpu_to_le16(params->cw_min); + ctx->qos_data.def_qos_parm.ac[q].cw_max = + cpu_to_le16(params->cw_max); + ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + ctx->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->txop * 32)); + + ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + + return priv->ibss_manager == IWL_IBSS_MANAGER; +} + +static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + iwl_connection_init_rx_config(priv, ctx); + + iwlagn_set_rxon_chain(priv, ctx); + + return iwlagn_commit_rxon(priv, ctx); +} + +static int iwl_setup_interface(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct ieee80211_vif *vif = ctx->vif; + int err, ac; + + lockdep_assert_held(&priv->mutex); + + /* + * This variable will be correct only when there's just + * a single context, but all code using it is for hardware + * that supports only one context. + */ + priv->iw_mode = vif->type; + + ctx->is_active = true; + + err = iwl_set_mode(priv, ctx); + if (err) { + if (!ctx->always_active) + ctx->is_active = false; + return err; + } + + if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && + vif->type == NL80211_IFTYPE_ADHOC) { + /* + * pretend to have high BT traffic as long as we + * are operating in IBSS mode, as this will cause + * the rate scaling etc. to behave as intended. + */ + priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + } + + /* set up queue mappings */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + vif->hw_queue[ac] = ctx->ac_to_queue[ac]; + + if (vif->type == NL80211_IFTYPE_AP) + vif->cab_queue = ctx->mcast_queue; + else + vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; + + return 0; +} + +static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *tmp, *ctx = NULL; + int err; + enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); + bool reset = false; + + IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", + viftype, vif->addr); + + cancel_delayed_work_sync(&priv->hw_roc_disable_work); + + mutex_lock(&priv->mutex); + + iwlagn_disable_roc(priv); + + if (!iwl_is_ready_rf(priv)) { + IWL_WARN(priv, "Try to add interface when device not ready\n"); + err = -EINVAL; + goto out; + } + + for_each_context(priv, tmp) { + u32 possible_modes = + tmp->interface_modes | tmp->exclusive_interface_modes; + + if (tmp->vif) { + /* On reset we need to add the same interface again */ + if (tmp->vif == vif) { + reset = true; + ctx = tmp; + break; + } + + /* check if this busy context is exclusive */ + if (tmp->exclusive_interface_modes & + BIT(tmp->vif->type)) { + err = -EINVAL; + goto out; + } + continue; + } + + if (!(possible_modes & BIT(viftype))) + continue; + + /* have maybe usable context w/o interface */ + ctx = tmp; + break; + } + + if (!ctx) { + err = -EOPNOTSUPP; + goto out; + } + + vif_priv->ctx = ctx; + ctx->vif = vif; + + err = iwl_setup_interface(priv, ctx); + if (!err || reset) + goto out; + + ctx->vif = NULL; + priv->iw_mode = NL80211_IFTYPE_STATION; + out: + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return err; +} + +static void iwl_teardown_interface(struct iwl_priv *priv, + struct ieee80211_vif *vif, + bool mode_change) +{ + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + lockdep_assert_held(&priv->mutex); + + if (priv->scan_vif == vif) { + iwl_scan_cancel_timeout(priv, 200); + iwl_force_scan_end(priv); + } + + if (!mode_change) { + iwl_set_mode(priv, ctx); + if (!ctx->always_active) + ctx->is_active = false; + } + + /* + * When removing the IBSS interface, overwrite the + * BT traffic load with the stored one from the last + * notification, if any. If this is a device that + * doesn't implement this, this has no effect since + * both values are the same and zero. + */ + if (vif->type == NL80211_IFTYPE_ADHOC) + priv->bt_traffic_load = priv->last_bt_traffic_load; +} + +static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->mutex); + + if (WARN_ON(ctx->vif != vif)) { + struct iwl_rxon_context *tmp; + IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); + for_each_context(priv, tmp) + IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", + tmp->ctxid, tmp, tmp->vif); + } + ctx->vif = NULL; + + iwl_teardown_interface(priv, vif, false); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + +} + +static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + struct iwl_rxon_context *bss_ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_rxon_context *tmp; + enum nl80211_iftype newviftype = newtype; + u32 interface_modes; + int err; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + newtype = ieee80211_iftype_p2p(newtype, newp2p); + + mutex_lock(&priv->mutex); + + if (!ctx->vif || !iwl_is_ready_rf(priv)) { + /* + * Huh? But wait ... this can maybe happen when + * we're in the middle of a firmware restart! + */ + err = -EBUSY; + goto out; + } + + interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; + + if (!(interface_modes & BIT(newtype))) { + err = -EBUSY; + goto out; + } + + /* + * Refuse a change that should be done by moving from the PAN + * context to the BSS context instead, if the BSS context is + * available and can support the new interface type. + */ + if (ctx->ctxid == IWL_RXON_CTX_PAN && !bss_ctx->vif && + (bss_ctx->interface_modes & BIT(newtype) || + bss_ctx->exclusive_interface_modes & BIT(newtype))) { + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + err = -EBUSY; + goto out; + } + + if (ctx->exclusive_interface_modes & BIT(newtype)) { + for_each_context(priv, tmp) { + if (ctx == tmp) + continue; + + if (!tmp->vif) + continue; + + /* + * The current mode switch would be exclusive, but + * another context is active ... refuse the switch. + */ + err = -EBUSY; + goto out; + } + } + + /* success */ + iwl_teardown_interface(priv, vif, true); + vif->type = newviftype; + vif->p2p = newp2p; + err = iwl_setup_interface(priv, ctx); + WARN_ON(err); + /* + * We've switched internally, but submitting to the + * device may have failed for some reason. Mask this + * error, because otherwise mac80211 will not switch + * (and set the interface type back) and we'll be + * out of sync with it. + */ + err = 0; + + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return err; +} + +static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (req->n_channels == 0) + return -EINVAL; + + mutex_lock(&priv->mutex); + + /* + * If an internal scan is in progress, just set + * up the scan_request as per above. + */ + if (priv->scan_type != IWL_SCAN_NORMAL) { + IWL_DEBUG_SCAN(priv, + "SCAN request during internal scan - defer\n"); + priv->scan_request = req; + priv->scan_vif = vif; + ret = 0; + } else { + priv->scan_request = req; + priv->scan_vif = vif; + /* + * mac80211 will only ask for one band at a time + * so using channels[0] here is ok + */ + ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, + req->channels[0]->band); + if (ret) { + priv->scan_request = NULL; + priv->scan_vif = NULL; + } + } + + IWL_DEBUG_MAC80211(priv, "leave\n"); + + mutex_unlock(&priv->mutex); + + return ret; +} + +static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) +{ + struct iwl_addsta_cmd cmd = { + .mode = STA_CONTROL_MODIFY_MSK, + .station_flags_msk = STA_FLG_PWR_SAVE_MSK, + .sta.sta_id = sta_id, + }; + + iwl_send_add_sta(priv, &cmd, CMD_ASYNC); +} + +static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int sta_id; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + switch (cmd) { + case STA_NOTIFY_SLEEP: + WARN_ON(!sta_priv->client); + sta_priv->asleep = true; + if (atomic_read(&sta_priv->pending_frames) > 0) + ieee80211_sta_block_awake(hw, sta, true); + break; + case STA_NOTIFY_AWAKE: + WARN_ON(!sta_priv->client); + if (!sta_priv->asleep) + break; + sta_priv->asleep = false; + sta_id = iwl_sta_id(sta); + if (sta_id != IWL_INVALID_STATION) + iwl_sta_modify_ps_wake(priv, sta_id); + break; + default: + break; + } + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +struct ieee80211_ops iwlagn_hw_ops = { + .tx = iwlagn_mac_tx, + .start = iwlagn_mac_start, + .stop = iwlagn_mac_stop, +#ifdef CONFIG_PM_SLEEP + .suspend = iwlagn_mac_suspend, + .resume = iwlagn_mac_resume, + .set_wakeup = iwlagn_mac_set_wakeup, +#endif + .add_interface = iwlagn_mac_add_interface, + .remove_interface = iwlagn_mac_remove_interface, + .change_interface = iwlagn_mac_change_interface, + .config = iwlagn_mac_config, + .configure_filter = iwlagn_configure_filter, + .set_key = iwlagn_mac_set_key, + .update_tkip_key = iwlagn_mac_update_tkip_key, + .set_rekey_data = iwlagn_mac_set_rekey_data, + .conf_tx = iwlagn_mac_conf_tx, + .bss_info_changed = iwlagn_bss_info_changed, + .ampdu_action = iwlagn_mac_ampdu_action, + .hw_scan = iwlagn_mac_hw_scan, + .sta_notify = iwlagn_mac_sta_notify, + .sta_state = iwlagn_mac_sta_state, + .channel_switch = iwlagn_mac_channel_switch, + .flush = iwlagn_mac_flush, + .tx_last_beacon = iwlagn_mac_tx_last_beacon, + .remain_on_channel = iwlagn_mac_remain_on_channel, + .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, + .rssi_callback = iwlagn_mac_rssi_callback, + CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd) + CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump) + .set_tim = iwlagn_mac_set_tim, +}; + +/* This function both allocates and initializes hw and priv. */ +struct ieee80211_hw *iwl_alloc_all(void) +{ + struct iwl_priv *priv; + struct iwl_op_mode *op_mode; + /* mac80211 allocates memory for this device instance, including + * space for this driver's private structure */ + struct ieee80211_hw *hw; + + hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) + + sizeof(struct iwl_op_mode), &iwlagn_hw_ops); + if (!hw) + goto out; + + op_mode = hw->priv; + priv = IWL_OP_MODE_GET_DVM(op_mode); + priv->hw = hw; + +out: + return hw; +} diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c new file mode 100644 index 000000000000..d67ae99c00b9 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -0,0 +1,2371 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "iwl-drv.h" +#include "iwl-modparams.h" + +#include "eeprom.h" +#include "dev.h" +#include "calib.h" +#include "agn.h" + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* + * module name, copyright, version, etc. + */ +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" + +#ifdef CONFIG_IWLWIFI_DEBUG +#define VD "d" +#else +#define VD +#endif + +#define DRV_VERSION IWLWIFI_VERSION VD + + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + +void iwl_update_chain_flags(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) { + iwlagn_set_rxon_chain(priv, ctx); + if (ctx->active.rx_chain != ctx->staging.rx_chain) + iwlagn_commit_rxon(priv, ctx); + } +} + +/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ +static void iwl_set_beacon_tim(struct iwl_priv *priv, + struct iwl_tx_beacon_cmd *tx_beacon_cmd, + u8 *beacon, u32 frame_size) +{ + u16 tim_idx; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; + + /* + * The index is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx+1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { + tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); + tx_beacon_cmd->tim_size = beacon[tim_idx+1]; + } else + IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); +} + +int iwlagn_send_beacon_cmd(struct iwl_priv *priv) +{ + struct iwl_tx_beacon_cmd *tx_beacon_cmd; + struct iwl_host_cmd cmd = { + .id = REPLY_TX_BEACON, + .flags = CMD_SYNC, + }; + struct ieee80211_tx_info *info; + u32 frame_size; + u32 rate_flags; + u32 rate; + + /* + * We have to set up the TX command, the TX Beacon command, and the + * beacon contents. + */ + + lockdep_assert_held(&priv->mutex); + + if (!priv->beacon_ctx) { + IWL_ERR(priv, "trying to build beacon w/o beacon context!\n"); + return 0; + } + + if (WARN_ON(!priv->beacon_skb)) + return -EINVAL; + + /* Allocate beacon command */ + if (!priv->beacon_cmd) + priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL); + tx_beacon_cmd = priv->beacon_cmd; + if (!tx_beacon_cmd) + return -ENOMEM; + + frame_size = priv->beacon_skb->len; + + /* Set up TX command fields */ + tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); + tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | + TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; + + /* Set up TX beacon command fields */ + iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data, + frame_size); + + /* Set up packet rate and flags */ + info = IEEE80211_SKB_CB(priv->beacon_skb); + + /* + * Let's set up the rate at least somewhat correctly; + * it will currently not actually be used by the uCode, + * it uses the broadcast station's rate instead. + */ + if (info->control.rates[0].idx < 0 || + info->control.rates[0].flags & IEEE80211_TX_RC_MCS) + rate = 0; + else + rate = info->control.rates[0].idx; + + priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, + priv->hw_params.valid_tx_ant); + rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); + + /* In mac80211, rates for 5 GHz start at 0 */ + if (info->band == IEEE80211_BAND_5GHZ) + rate += IWL_FIRST_OFDM_RATE; + else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + tx_beacon_cmd->tx.rate_n_flags = + iwl_hw_set_rate_n_flags(rate, rate_flags); + + /* Submit command */ + cmd.len[0] = sizeof(*tx_beacon_cmd); + cmd.data[0] = tx_beacon_cmd; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + cmd.len[1] = frame_size; + cmd.data[1] = priv->beacon_skb->data; + cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; + + return iwl_dvm_send_cmd(priv, &cmd); +} + +static void iwl_bg_beacon_update(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, beacon_update); + struct sk_buff *beacon; + + mutex_lock(&priv->mutex); + if (!priv->beacon_ctx) { + IWL_ERR(priv, "updating beacon w/o beacon context!\n"); + goto out; + } + + if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) { + /* + * The ucode will send beacon notifications even in + * IBSS mode, but we don't want to process them. But + * we need to defer the type check to here due to + * requiring locking around the beacon_ctx access. + */ + goto out; + } + + /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ + beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif); + if (!beacon) { + IWL_ERR(priv, "update beacon failed -- keeping old\n"); + goto out; + } + + /* new beacon skb is allocated every time; dispose previous.*/ + dev_kfree_skb(priv->beacon_skb); + + priv->beacon_skb = beacon; + + iwlagn_send_beacon_cmd(priv); + out: + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_bt_runtime_config(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, bt_runtime_config); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* dont send host command if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + return; + iwlagn_send_advance_bt_config(priv); +} + +static void iwl_bg_bt_full_concurrency(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, bt_full_concurrency); + struct iwl_rxon_context *ctx; + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + goto out; + + /* dont send host command if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + goto out; + + IWL_DEBUG_INFO(priv, "BT coex in %s mode\n", + priv->bt_full_concurrent ? + "full concurrency" : "3-wire"); + + /* + * LQ & RXON updated cmds must be sent before BT Config cmd + * to avoid 3-wire collisions + */ + for_each_context(priv, ctx) { + iwlagn_set_rxon_chain(priv, ctx); + iwlagn_commit_rxon(priv, ctx); + } + + iwlagn_send_advance_bt_config(priv); +out: + mutex_unlock(&priv->mutex); +} + +int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) +{ + struct iwl_statistics_cmd statistics_cmd = { + .configuration_flags = + clear ? IWL_STATS_CONF_CLEAR_STATS : 0, + }; + + if (flags & CMD_ASYNC) + return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, + CMD_ASYNC, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd); + else + return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, + CMD_SYNC, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd); +} + +/** + * iwl_bg_statistics_periodic - Timer callback to queue statistics + * + * This callback is provided in order to send a statistics request. + * + * This timer function is continually reset to execute within + * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION + * was received. We need to ensure we receive the statistics in order + * to update the temperature used for calibrating the TXPOWER. + */ +static void iwl_bg_statistics_periodic(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* dont send host command if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + return; + + iwl_send_statistics_request(priv, CMD_ASYNC, false); +} + + +static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, + u32 start_idx, u32 num_events, + u32 capacity, u32 mode) +{ + u32 i; + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + if (mode == 0) + ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); + else + ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); + + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&priv->trans->reg_lock, reg_flags); + if (unlikely(!iwl_grab_nic_access(priv->trans))) { + spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags); + return; + } + + /* Set starting address; reads will auto-increment */ + iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr); + + /* + * Refuse to read more than would have fit into the log from + * the current start_idx. This used to happen due to the race + * described below, but now WARN because the code below should + * prevent it from happening here. + */ + if (WARN_ON(num_events > capacity - start_idx)) + num_events = capacity - start_idx; + + /* + * "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. + */ + for (i = 0; i < num_events; i++) { + ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + trace_iwlwifi_dev_ucode_cont_event( + priv->trans->dev, 0, time, ev); + } else { + data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + trace_iwlwifi_dev_ucode_cont_event( + priv->trans->dev, time, data, ev); + } + } + /* Allow device to power down */ + iwl_release_nic_access(priv->trans); + spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags); +} + +static void iwl_continuous_event_trace(struct iwl_priv *priv) +{ + u32 capacity; /* event log capacity in # entries */ + struct { + u32 capacity; + u32 mode; + u32 wrap_counter; + u32 write_counter; + } __packed read; + u32 base; /* SRAM byte address of event log header */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + + base = priv->device_pointers.log_event_table; + if (iwlagn_hw_valid_rtc_data_addr(base)) { + iwl_read_targ_mem_words(priv->trans, base, &read, sizeof(read)); + capacity = read.capacity; + mode = read.mode; + num_wraps = read.wrap_counter; + next_entry = read.write_counter; + } else + return; + + /* + * Unfortunately, the uCode doesn't use temporary variables. + * Therefore, it can happen that we read next_entry == capacity, + * which really means next_entry == 0. + */ + if (unlikely(next_entry == capacity)) + next_entry = 0; + /* + * Additionally, the uCode increases the write pointer before + * the wraps counter, so if the write pointer is smaller than + * the old write pointer (wrap occurred) but we read that no + * wrap occurred, we actually read between the next_entry and + * num_wraps update (this does happen in practice!!) -- take + * that into account by increasing num_wraps. + */ + if (unlikely(next_entry < priv->event_log.next_entry && + num_wraps == priv->event_log.num_wraps)) + num_wraps++; + + if (num_wraps == priv->event_log.num_wraps) { + iwl_print_cont_event_trace( + priv, base, priv->event_log.next_entry, + next_entry - priv->event_log.next_entry, + capacity, mode); + + priv->event_log.non_wraps_count++; + } else { + if (num_wraps - priv->event_log.num_wraps > 1) + priv->event_log.wraps_more_count++; + else + priv->event_log.wraps_once_count++; + + trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev, + num_wraps - priv->event_log.num_wraps, + next_entry, priv->event_log.next_entry); + + if (next_entry < priv->event_log.next_entry) { + iwl_print_cont_event_trace( + priv, base, priv->event_log.next_entry, + capacity - priv->event_log.next_entry, + capacity, mode); + + iwl_print_cont_event_trace( + priv, base, 0, next_entry, capacity, mode); + } else { + iwl_print_cont_event_trace( + priv, base, next_entry, + capacity - next_entry, + capacity, mode); + + iwl_print_cont_event_trace( + priv, base, 0, next_entry, capacity, mode); + } + } + + priv->event_log.num_wraps = num_wraps; + priv->event_log.next_entry = next_entry; +} + +/** + * iwl_bg_ucode_trace - Timer callback to log ucode event + * + * The timer is continually set to execute every + * UCODE_TRACE_PERIOD milliseconds after the last timer expired + * this function is to perform continuous uCode event logging operation + * if enabled + */ +static void iwl_bg_ucode_trace(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->event_log.ucode_trace) { + iwl_continuous_event_trace(priv); + /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ + mod_timer(&priv->ucode_trace, + jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); + } +} + +static void iwl_bg_tx_flush(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, tx_flush); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* do nothing if rf-kill is on */ + if (!iwl_is_ready_rf(priv)) + return; + + IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); + iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); +} + +/* + * queue/FIFO/AC mapping definitions + */ + +#define IWL_TX_FIFO_BK 0 /* shared */ +#define IWL_TX_FIFO_BE 1 +#define IWL_TX_FIFO_VI 2 /* shared */ +#define IWL_TX_FIFO_VO 3 +#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK +#define IWL_TX_FIFO_BE_IPAN 4 +#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI +#define IWL_TX_FIFO_VO_IPAN 5 +/* re-uses the VO FIFO, uCode will properly flush/schedule */ +#define IWL_TX_FIFO_AUX 5 +#define IWL_TX_FIFO_UNUSED -1 + +#define IWLAGN_CMD_FIFO_NUM 7 + +/* + * This queue number is required for proper operation + * because the ucode will stop/start the scheduler as + * required. + */ +#define IWL_IPAN_MCAST_QUEUE 8 + +static const u8 iwlagn_default_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, + IWLAGN_CMD_FIFO_NUM, +}; + +static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, + IWL_TX_FIFO_BK_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_VI_IPAN, + IWL_TX_FIFO_VO_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWLAGN_CMD_FIFO_NUM, + IWL_TX_FIFO_AUX, +}; + +static const u8 iwlagn_bss_ac_to_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, +}; + +static const u8 iwlagn_bss_ac_to_queue[] = { + 0, 1, 2, 3, +}; + +static const u8 iwlagn_pan_ac_to_fifo[] = { + IWL_TX_FIFO_VO_IPAN, + IWL_TX_FIFO_VI_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_BK_IPAN, +}; + +static const u8 iwlagn_pan_ac_to_queue[] = { + 7, 6, 5, 4, +}; + +static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) +{ + int i; + + /* + * The default context is always valid, + * the PAN context depends on uCode. + */ + priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); + if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) + priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN); + + for (i = 0; i < NUM_IWL_RXON_CTX; i++) + priv->contexts[i].ctxid = i; + + priv->contexts[IWL_RXON_CTX_BSS].always_active = true; + priv->contexts[IWL_RXON_CTX_BSS].is_active = true; + priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON; + priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING; + priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC; + priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM; + priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID; + priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY; + priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; + priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes = + BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR); + priv->contexts[IWL_RXON_CTX_BSS].interface_modes = + BIT(NL80211_IFTYPE_STATION); + priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP; + priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS; + priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS; + priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS; + memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue, + iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue)); + memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo, + iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo)); + + priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON; + priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd = + REPLY_WIPAN_RXON_TIMING; + priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd = + REPLY_WIPAN_RXON_ASSOC; + priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM; + priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN; + priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY; + priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID; + priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION; + priv->contexts[IWL_RXON_CTX_PAN].interface_modes = + BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + + if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P) + priv->contexts[IWL_RXON_CTX_PAN].interface_modes |= + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP; + priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA; + priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P; + memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue, + iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue)); + memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo, + iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo)); + priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); +} + +static void iwl_rf_kill_ct_config(struct iwl_priv *priv) +{ + struct iwl_ct_kill_config cmd; + struct iwl_ct_kill_throttling_config adv_cmd; + int ret = 0; + + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + + priv->thermal_throttle.ct_kill_toggle = false; + + if (priv->cfg->base_params->support_ct_kill_exit) { + adv_cmd.critical_temperature_enter = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + adv_cmd.critical_temperature_exit = + cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); + + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_CT_KILL_CONFIG_CMD, + CMD_SYNC, sizeof(adv_cmd), &adv_cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, critical temperature enter is %d," + "exit is %d\n", + priv->hw_params.ct_kill_threshold, + priv->hw_params.ct_kill_exit_threshold); + } else { + cmd.critical_temperature_R = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_CT_KILL_CONFIG_CMD, + CMD_SYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature is %d\n", + priv->hw_params.ct_kill_threshold); + } +} + +static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg) +{ + struct iwl_calib_cfg_cmd calib_cfg_cmd; + struct iwl_host_cmd cmd = { + .id = CALIBRATION_CFG_CMD, + .len = { sizeof(struct iwl_calib_cfg_cmd), }, + .data = { &calib_cfg_cmd, }, + }; + + memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); + calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg); + + return iwl_dvm_send_cmd(priv, &cmd); +} + + +static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) +{ + struct iwl_tx_ant_config_cmd tx_ant_cmd = { + .valid = cpu_to_le32(valid_tx_ant), + }; + + if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { + IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); + return iwl_dvm_send_cmd_pdu(priv, + TX_ANT_CONFIGURATION_CMD, + CMD_SYNC, + sizeof(struct iwl_tx_ant_config_cmd), + &tx_ant_cmd); + } else { + IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); + return -EOPNOTSUPP; + } +} + +static void iwl_send_bt_config(struct iwl_priv *priv) +{ + struct iwl_bt_cmd bt_cmd = { + .lead_time = BT_LEAD_TIME_DEF, + .max_kill = BT_MAX_KILL_DEF, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + if (!iwlwifi_mod_params.bt_coex_active) + bt_cmd.flags = BT_COEX_DISABLE; + else + bt_cmd.flags = BT_COEX_ENABLE; + + priv->bt_enable_flag = bt_cmd.flags; + IWL_DEBUG_INFO(priv, "BT coex %s\n", + (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); + + if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, + CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd)) + IWL_ERR(priv, "failed to send BT Coex Config\n"); +} + +/** + * iwl_alive_start - called after REPLY_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by iwl_init_alive_start()). + */ +int iwl_alive_start(struct iwl_priv *priv) +{ + int ret = 0; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); + + /* After the ALIVE response, we can send host commands to the uCode */ + set_bit(STATUS_ALIVE, &priv->status); + + if (iwl_is_rfkill(priv)) + return -ERFKILL; + + if (priv->event_log.ucode_trace) { + /* start collecting data now */ + mod_timer(&priv->ucode_trace, jiffies); + } + + /* download priority table before any calibration request */ + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + /* Configure Bluetooth device coexistence support */ + if (priv->cfg->bt_params->bt_sco_disable) + priv->bt_enable_pspoll = false; + else + priv->bt_enable_pspoll = true; + + priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; + priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; + priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; + iwlagn_send_advance_bt_config(priv); + priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS; + priv->cur_rssi_ctx = NULL; + + iwl_send_prio_tbl(priv); + + /* FIXME: w/a to force change uCode BT state machine */ + ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + } else { + /* + * default is 2-wire BT coexexistence support + */ + iwl_send_bt_config(priv); + } + + /* + * Perform runtime calibrations, including DC calibration. + */ + iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX); + + ieee80211_wake_queues(priv->hw); + + /* Configure Tx antenna selection based on H/W config */ + iwlagn_send_tx_ant_config(priv, priv->hw_params.valid_tx_ant); + + if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { + struct iwl_rxon_cmd *active_rxon = + (struct iwl_rxon_cmd *)&ctx->active; + /* apply any changes in staging */ + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + } else { + struct iwl_rxon_context *tmp; + /* Initialize our rx_config data */ + for_each_context(priv, tmp) + iwl_connection_init_rx_config(priv, tmp); + + iwlagn_set_rxon_chain(priv, ctx); + } + + if (!priv->wowlan) { + /* WoWLAN ucode will not reply in the same way, skip it */ + iwl_reset_run_time_calib(priv); + } + + set_bit(STATUS_READY, &priv->status); + + /* Configure the adapter for unassociated operation */ + ret = iwlagn_commit_rxon(priv, ctx); + if (ret) + return ret; + + /* At this point, the NIC is initialized and operational */ + iwl_rf_kill_ct_config(priv); + + IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); + + return iwl_power_update_mode(priv, true); +} + +/** + * iwl_clear_driver_stations - clear knowledge of all stations from driver + * @priv: iwl priv struct + * + * This is called during iwl_down() to make sure that in the case + * we're coming there from a hardware restart mac80211 will be + * able to reconfigure stations -- if we're getting there in the + * normal down flow then the stations will already be cleared. + */ +static void iwl_clear_driver_stations(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + spin_lock_bh(&priv->sta_lock); + memset(priv->stations, 0, sizeof(priv->stations)); + priv->num_stations = 0; + + priv->ucode_key_table = 0; + + for_each_context(priv, ctx) { + /* + * Remove all key information that is not stored as part + * of station information since mac80211 may not have had + * a chance to remove all the keys. When device is + * reconfigured by mac80211 after an error all keys will + * be reconfigured. + */ + memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys)); + ctx->key_mapping_keys = 0; + } + + spin_unlock_bh(&priv->sta_lock); +} + +void iwl_down(struct iwl_priv *priv) +{ + int exit_pending; + + IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); + + lockdep_assert_held(&priv->mutex); + + iwl_scan_cancel_timeout(priv, 200); + + /* + * If active, scanning won't cancel it, so say it expired. + * No race since we hold the mutex here and a new one + * can't come in at this time. + */ + ieee80211_remain_on_channel_expired(priv->hw); + + exit_pending = + test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl_clear_ucode_stations(priv, NULL); + iwl_dealloc_bcast_stations(priv); + iwl_clear_driver_stations(priv); + + /* reset BT coex data */ + priv->bt_status = 0; + priv->cur_rssi_ctx = NULL; + priv->bt_is_sco = 0; + if (priv->cfg->bt_params) + priv->bt_traffic_load = + priv->cfg->bt_params->bt_init_traffic_load; + else + priv->bt_traffic_load = 0; + priv->bt_full_concurrent = false; + priv->bt_ci_compliance = 0; + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + priv->ucode_loaded = false; + iwl_trans_stop_device(priv->trans); + + /* Set num_aux_in_flight must be done after the transport is stopped */ + atomic_set(&priv->num_aux_in_flight, 0); + + /* Clear out all status bits but a few that are stable across reset */ + priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << + STATUS_RF_KILL_HW | + test_bit(STATUS_GEO_CONFIGURED, &priv->status) << + STATUS_GEO_CONFIGURED | + test_bit(STATUS_FW_ERROR, &priv->status) << + STATUS_FW_ERROR | + test_bit(STATUS_EXIT_PENDING, &priv->status) << + STATUS_EXIT_PENDING; + + dev_kfree_skb(priv->beacon_skb); + priv->beacon_skb = NULL; +} + +/***************************************************************************** + * + * Workqueue callbacks + * + *****************************************************************************/ + +static void iwl_bg_run_time_calib_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, + run_time_calib_work); + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) { + mutex_unlock(&priv->mutex); + return; + } + + if (priv->start_calib) { + iwl_chain_noise_calibration(priv); + iwl_sensitivity_calibration(priv); + } + + mutex_unlock(&priv->mutex); +} + +void iwlagn_prepare_restart(struct iwl_priv *priv) +{ + bool bt_full_concurrent; + u8 bt_ci_compliance; + u8 bt_load; + u8 bt_status; + bool bt_is_sco; + int i; + + lockdep_assert_held(&priv->mutex); + + priv->is_open = 0; + + /* + * __iwl_down() will clear the BT status variables, + * which is correct, but when we restart we really + * want to keep them so restore them afterwards. + * + * The restart process will later pick them up and + * re-configure the hw when we reconfigure the BT + * command. + */ + bt_full_concurrent = priv->bt_full_concurrent; + bt_ci_compliance = priv->bt_ci_compliance; + bt_load = priv->bt_traffic_load; + bt_status = priv->bt_status; + bt_is_sco = priv->bt_is_sco; + + iwl_down(priv); + + priv->bt_full_concurrent = bt_full_concurrent; + priv->bt_ci_compliance = bt_ci_compliance; + priv->bt_traffic_load = bt_load; + priv->bt_status = bt_status; + priv->bt_is_sco = bt_is_sco; + + /* reset aggregation queues */ + for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++) + priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; + /* and stop counts */ + for (i = 0; i < IWL_MAX_HW_QUEUES; i++) + atomic_set(&priv->queue_stop_count[i], 0); + + memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc)); +} + +static void iwl_bg_restart(struct work_struct *data) +{ + struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) { + mutex_lock(&priv->mutex); + iwlagn_prepare_restart(priv); + mutex_unlock(&priv->mutex); + iwl_cancel_deferred_work(priv); + ieee80211_restart_hw(priv->hw); + } else { + WARN_ON(1); + } +} + + + + +void iwlagn_disable_roc(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; + + lockdep_assert_held(&priv->mutex); + + if (!priv->hw_roc_setup) + return; + + ctx->staging.dev_type = RXON_DEV_TYPE_P2P; + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + + priv->hw_roc_channel = NULL; + + memset(ctx->staging.node_addr, 0, ETH_ALEN); + + iwlagn_commit_rxon(priv, ctx); + + ctx->is_active = false; + priv->hw_roc_setup = false; +} + +static void iwlagn_disable_roc_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, + hw_roc_disable_work.work); + + mutex_lock(&priv->mutex); + iwlagn_disable_roc(priv); + mutex_unlock(&priv->mutex); +} + +/***************************************************************************** + * + * driver setup and teardown + * + *****************************************************************************/ + +static void iwl_setup_deferred_work(struct iwl_priv *priv) +{ + priv->workqueue = create_singlethread_workqueue(DRV_NAME); + + INIT_WORK(&priv->restart, iwl_bg_restart); + INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); + INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); + INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush); + INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency); + INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); + INIT_DELAYED_WORK(&priv->hw_roc_disable_work, + iwlagn_disable_roc_work); + + iwl_setup_scan_deferred_work(priv); + + if (priv->cfg->bt_params) + iwlagn_bt_setup_deferred_work(priv); + + init_timer(&priv->statistics_periodic); + priv->statistics_periodic.data = (unsigned long)priv; + priv->statistics_periodic.function = iwl_bg_statistics_periodic; + + init_timer(&priv->ucode_trace); + priv->ucode_trace.data = (unsigned long)priv; + priv->ucode_trace.function = iwl_bg_ucode_trace; +} + +void iwl_cancel_deferred_work(struct iwl_priv *priv) +{ + if (priv->cfg->bt_params) + iwlagn_bt_cancel_deferred_work(priv); + + cancel_work_sync(&priv->run_time_calib_work); + cancel_work_sync(&priv->beacon_update); + + iwl_cancel_scan_deferred_work(priv); + + cancel_work_sync(&priv->bt_full_concurrency); + cancel_work_sync(&priv->bt_runtime_config); + cancel_delayed_work_sync(&priv->hw_roc_disable_work); + + del_timer_sync(&priv->statistics_periodic); + del_timer_sync(&priv->ucode_trace); +} + +static void iwl_init_hw_rates(struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) { + rates[i].bitrate = iwl_rates[i].ieee * 5; + rates[i].hw_value = i; /* Rate scaling will work on indexes */ + rates[i].hw_value_short = i; + rates[i].flags = 0; + if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) { + /* + * If CCK != 1M then set short preamble rate flag. + */ + rates[i].flags |= + (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ? + 0 : IEEE80211_RATE_SHORT_PREAMBLE; + } + } +} + +#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ +#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ +static void iwl_init_ht_hw_capab(const struct iwl_priv *priv, + struct ieee80211_sta_ht_cap *ht_info, + enum ieee80211_band band) +{ + u16 max_bit_rate = 0; + u8 rx_chains_num = priv->hw_params.rx_chains_num; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + + ht_info->cap = 0; + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + ht_info->ht_supported = true; + + if (priv->cfg->ht_params && + priv->cfg->ht_params->ht_greenfield_support) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + max_bit_rate = MAX_BIT_RATE_20_MHZ; + if (priv->hw_params.ht40_channel & BIT(band)) { + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + ht_info->mcs.rx_mask[4] = 0x01; + max_bit_rate = MAX_BIT_RATE_40_MHZ; + } + + if (iwlwifi_mod_params.amsdu_size_8K) + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; + ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; + + ht_info->mcs.rx_mask[0] = 0xFF; + if (rx_chains_num >= 2) + ht_info->mcs.rx_mask[1] = 0xFF; + if (rx_chains_num >= 3) + ht_info->mcs.rx_mask[2] = 0xFF; + + /* Highest supported Rx data rate */ + max_bit_rate *= rx_chains_num; + WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); + ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); + + /* Tx MCS capabilities */ + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chains_num != rx_chains_num) { + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_chains_num - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } +} + +/** + * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom + */ +static int iwl_init_geos(struct iwl_priv *priv) +{ + struct iwl_channel_info *ch; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channels; + struct ieee80211_channel *geo_ch; + struct ieee80211_rate *rates; + int i = 0; + s8 max_tx_power = IWLAGN_TX_POWER_TARGET_POWER_MIN; + + if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates || + priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) { + IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n"); + set_bit(STATUS_GEO_CONFIGURED, &priv->status); + return 0; + } + + channels = kcalloc(priv->channel_count, + sizeof(struct ieee80211_channel), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate), + GFP_KERNEL); + if (!rates) { + kfree(channels); + return -ENOMEM; + } + + /* 5.2GHz channels start after the 2.4GHz channels */ + sband = &priv->bands[IEEE80211_BAND_5GHZ]; + sband->channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)]; + /* just OFDM */ + sband->bitrates = &rates[IWL_FIRST_OFDM_RATE]; + sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE; + + if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) + iwl_init_ht_hw_capab(priv, &sband->ht_cap, + IEEE80211_BAND_5GHZ); + + sband = &priv->bands[IEEE80211_BAND_2GHZ]; + sband->channels = channels; + /* OFDM & CCK */ + sband->bitrates = rates; + sband->n_bitrates = IWL_RATE_COUNT_LEGACY; + + if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) + iwl_init_ht_hw_capab(priv, &sband->ht_cap, + IEEE80211_BAND_2GHZ); + + priv->ieee_channels = channels; + priv->ieee_rates = rates; + + for (i = 0; i < priv->channel_count; i++) { + ch = &priv->channel_info[i]; + + /* FIXME: might be removed if scan is OK */ + if (!is_channel_valid(ch)) + continue; + + sband = &priv->bands[ch->band]; + + geo_ch = &sband->channels[sband->n_channels++]; + + geo_ch->center_freq = + ieee80211_channel_to_frequency(ch->channel, ch->band); + geo_ch->max_power = ch->max_power_avg; + geo_ch->max_antenna_gain = 0xff; + geo_ch->hw_value = ch->channel; + + if (is_channel_valid(ch)) { + if (!(ch->flags & EEPROM_CHANNEL_IBSS)) + geo_ch->flags |= IEEE80211_CHAN_NO_IBSS; + + if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) + geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN; + + if (ch->flags & EEPROM_CHANNEL_RADAR) + geo_ch->flags |= IEEE80211_CHAN_RADAR; + + geo_ch->flags |= ch->ht40_extension_channel; + + if (ch->max_power_avg > max_tx_power) + max_tx_power = ch->max_power_avg; + } else { + geo_ch->flags |= IEEE80211_CHAN_DISABLED; + } + + IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", + ch->channel, geo_ch->center_freq, + is_channel_a_band(ch) ? "5.2" : "2.4", + geo_ch->flags & IEEE80211_CHAN_DISABLED ? + "restricted" : "valid", + geo_ch->flags); + } + + priv->tx_power_device_lmt = max_tx_power; + priv->tx_power_user_lmt = max_tx_power; + priv->tx_power_next = max_tx_power; + + if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && + priv->hw_params.sku & EEPROM_SKU_CAP_BAND_52GHZ) { + IWL_INFO(priv, "Incorrectly detected BG card as ABG. " + "Please send your %s to maintainer.\n", + priv->trans->hw_id_str); + priv->hw_params.sku &= ~EEPROM_SKU_CAP_BAND_52GHZ; + } + + if (iwlwifi_mod_params.disable_5ghz) + priv->bands[IEEE80211_BAND_5GHZ].n_channels = 0; + + IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n", + priv->bands[IEEE80211_BAND_2GHZ].n_channels, + priv->bands[IEEE80211_BAND_5GHZ].n_channels); + + set_bit(STATUS_GEO_CONFIGURED, &priv->status); + + return 0; +} + +/* + * iwl_free_geos - undo allocations in iwl_init_geos + */ +static void iwl_free_geos(struct iwl_priv *priv) +{ + kfree(priv->ieee_channels); + kfree(priv->ieee_rates); + clear_bit(STATUS_GEO_CONFIGURED, &priv->status); +} + +static int iwl_init_drv(struct iwl_priv *priv) +{ + int ret; + + spin_lock_init(&priv->sta_lock); + + mutex_init(&priv->mutex); + + INIT_LIST_HEAD(&priv->calib_results); + + priv->ieee_channels = NULL; + priv->ieee_rates = NULL; + priv->band = IEEE80211_BAND_2GHZ; + + priv->plcp_delta_threshold = + priv->cfg->base_params->plcp_delta_threshold; + + priv->iw_mode = NL80211_IFTYPE_STATION; + priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; + priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; + priv->agg_tids_count = 0; + + priv->ucode_owner = IWL_OWNERSHIP_DRIVER; + + priv->rx_statistics_jiffies = jiffies; + + /* Choose which receivers/antennas to use */ + iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]); + + iwl_init_scan_params(priv); + + /* init bt coex */ + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; + priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; + priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; + priv->bt_on_thresh = BT_ON_THRESHOLD_DEF; + priv->bt_duration = BT_DURATION_LIMIT_DEF; + priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; + } + + ret = iwl_init_channel_map(priv); + if (ret) { + IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); + goto err; + } + + ret = iwl_init_geos(priv); + if (ret) { + IWL_ERR(priv, "initializing geos failed: %d\n", ret); + goto err_free_channel_map; + } + iwl_init_hw_rates(priv->ieee_rates); + + return 0; + +err_free_channel_map: + iwl_free_channel_map(priv); +err: + return ret; +} + +static void iwl_uninit_drv(struct iwl_priv *priv) +{ + iwl_free_geos(priv); + iwl_free_channel_map(priv); + kfree(priv->scan_cmd); + kfree(priv->beacon_cmd); + kfree(rcu_dereference_raw(priv->noa_data)); + iwl_calib_free_results(priv); +#ifdef CONFIG_IWLWIFI_DEBUGFS + kfree(priv->wowlan_sram); +#endif +} + +static void iwl_set_hw_params(struct iwl_priv *priv) +{ + if (priv->cfg->ht_params) + priv->hw_params.use_rts_for_aggregation = + priv->cfg->ht_params->use_rts_for_aggregation; + + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) + priv->hw_params.sku &= ~EEPROM_SKU_CAP_11N_ENABLE; + + /* Device-specific setup */ + priv->lib->set_hw_params(priv); +} + + + +/* show what optional capabilities we have */ +static void iwl_option_config(struct iwl_priv *priv) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n"); +#endif + +#ifdef CONFIG_IWLWIFI_DEBUGFS + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n"); +#endif + +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); +#endif + +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE disabled\n"); +#endif + +#ifdef CONFIG_IWLWIFI_P2P + IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n"); +#else + IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n"); +#endif +} + +static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, + const struct iwl_cfg *cfg, + const struct iwl_fw *fw) +{ + struct iwl_priv *priv; + struct ieee80211_hw *hw; + struct iwl_op_mode *op_mode; + u16 num_mac; + u32 ucode_flags; + struct iwl_trans_config trans_cfg; + static const u8 no_reclaim_cmds[] = { + REPLY_RX_PHY_CMD, + REPLY_RX, + REPLY_RX_MPDU_CMD, + REPLY_COMPRESSED_BA, + STATISTICS_NOTIFICATION, + REPLY_TX, + }; + int i; + + /************************ + * 1. Allocating HW data + ************************/ + hw = iwl_alloc_all(); + if (!hw) { + pr_err("%s: Cannot allocate network device\n", cfg->name); + goto out; + } + + op_mode = hw->priv; + op_mode->ops = &iwl_dvm_ops; + priv = IWL_OP_MODE_GET_DVM(op_mode); + priv->trans = trans; + priv->dev = trans->dev; + priv->cfg = cfg; + priv->fw = fw; + + switch (priv->cfg->device_family) { + case IWL_DEVICE_FAMILY_1000: + case IWL_DEVICE_FAMILY_100: + priv->lib = &iwl1000_lib; + break; + case IWL_DEVICE_FAMILY_2000: + case IWL_DEVICE_FAMILY_105: + priv->lib = &iwl2000_lib; + break; + case IWL_DEVICE_FAMILY_2030: + case IWL_DEVICE_FAMILY_135: + priv->lib = &iwl2030_lib; + break; + case IWL_DEVICE_FAMILY_5000: + priv->lib = &iwl5000_lib; + break; + case IWL_DEVICE_FAMILY_5150: + priv->lib = &iwl5150_lib; + break; + case IWL_DEVICE_FAMILY_6000: + case IWL_DEVICE_FAMILY_6005: + case IWL_DEVICE_FAMILY_6000i: + case IWL_DEVICE_FAMILY_6050: + case IWL_DEVICE_FAMILY_6150: + priv->lib = &iwl6000_lib; + break; + case IWL_DEVICE_FAMILY_6030: + priv->lib = &iwl6030_lib; + break; + default: + break; + } + + if (WARN_ON(!priv->lib)) + goto out_free_hw; + + /* + * Populate the state variables that the transport layer needs + * to know about. + */ + trans_cfg.op_mode = op_mode; + trans_cfg.no_reclaim_cmds = no_reclaim_cmds; + trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; + if (!iwlwifi_mod_params.wd_disable) + trans_cfg.queue_watchdog_timeout = + priv->cfg->base_params->wd_timeout; + else + trans_cfg.queue_watchdog_timeout = IWL_WATCHHDOG_DISABLED; + trans_cfg.command_names = iwl_dvm_cmd_strings; + + ucode_flags = fw->ucode_capa.flags; + +#ifndef CONFIG_IWLWIFI_P2P + ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; +#endif + + if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { + priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; + trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; + trans_cfg.queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo; + trans_cfg.n_queue_to_fifo = + ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); + } else { + priv->sta_key_max_num = STA_KEY_MAX_NUM; + trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; + trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo; + trans_cfg.n_queue_to_fifo = + ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); + } + + /* Configure transport layer */ + iwl_trans_configure(priv->trans, &trans_cfg); + + /* At this point both hw and priv are allocated. */ + + SET_IEEE80211_DEV(priv->hw, priv->trans->dev); + + iwl_option_config(priv); + + IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); + + /* is antenna coupling more than 35dB ? */ + priv->bt_ant_couple_ok = + (iwlwifi_mod_params.ant_coupling > + IWL_BT_ANTENNA_COUPLING_THRESHOLD) ? + true : false; + + /* enable/disable bt channel inhibition */ + priv->bt_ch_announce = iwlwifi_mod_params.bt_ch_announce; + IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n", + (priv->bt_ch_announce) ? "On" : "Off"); + + /* these spin locks will be used in apm_ops.init and EEPROM access + * we should init now + */ + spin_lock_init(&priv->statistics.lock); + + /*********************** + * 2. Read REV register + ***********************/ + IWL_INFO(priv, "Detected %s, REV=0x%X\n", + priv->cfg->name, priv->trans->hw_rev); + + if (iwl_trans_start_hw(priv->trans)) + goto out_free_hw; + + /* Read the EEPROM */ + if (iwl_eeprom_init(priv, priv->trans->hw_rev)) { + IWL_ERR(priv, "Unable to init EEPROM\n"); + goto out_free_hw; + } + /* Reset chip to save power until we load uCode during "up". */ + iwl_trans_stop_hw(priv->trans, false); + + if (iwl_eeprom_check_version(priv)) + goto out_free_eeprom; + + if (iwl_eeprom_init_hw_params(priv)) + goto out_free_eeprom; + + /* extract MAC Address */ + iwl_eeprom_get_mac(priv, priv->addresses[0].addr); + IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); + priv->hw->wiphy->addresses = priv->addresses; + priv->hw->wiphy->n_addresses = 1; + num_mac = iwl_eeprom_query16(priv, EEPROM_NUM_MAC_ADDRESS); + if (num_mac > 1) { + memcpy(priv->addresses[1].addr, priv->addresses[0].addr, + ETH_ALEN); + priv->addresses[1].addr[5]++; + priv->hw->wiphy->n_addresses++; + } + + /************************ + * 4. Setup HW constants + ************************/ + iwl_set_hw_params(priv); + + if (!(priv->hw_params.sku & EEPROM_SKU_CAP_IPAN_ENABLE)) { + IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN"); + ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; + /* + * if not PAN, then don't support P2P -- might be a uCode + * packaging bug or due to the eeprom check above + */ + ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; + priv->sta_key_max_num = STA_KEY_MAX_NUM; + trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; + trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo; + trans_cfg.n_queue_to_fifo = + ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); + + /* Configure transport layer again*/ + iwl_trans_configure(priv->trans, &trans_cfg); + } + + /******************* + * 5. Setup priv + *******************/ + for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { + priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; + if (i < IWLAGN_FIRST_AMPDU_QUEUE && + i != IWL_DEFAULT_CMD_QUEUE_NUM && + i != IWL_IPAN_CMD_QUEUE_NUM) + priv->queue_to_mac80211[i] = i; + atomic_set(&priv->queue_stop_count[i], 0); + } + + WARN_ON(trans_cfg.queue_to_fifo[trans_cfg.cmd_queue] != + IWLAGN_CMD_FIFO_NUM); + + if (iwl_init_drv(priv)) + goto out_free_eeprom; + + /* At this point both hw and priv are initialized. */ + + /******************** + * 6. Setup services + ********************/ + iwl_setup_deferred_work(priv); + iwl_setup_rx_handlers(priv); + iwl_testmode_init(priv); + + iwl_power_initialize(priv); + iwl_tt_initialize(priv); + + snprintf(priv->hw->wiphy->fw_version, + sizeof(priv->hw->wiphy->fw_version), + "%s", fw->fw_version); + + priv->new_scan_threshold_behaviour = + !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); + + priv->phy_calib_chain_noise_reset_cmd = + fw->ucode_capa.standard_phy_calibration_size; + priv->phy_calib_chain_noise_gain_cmd = + fw->ucode_capa.standard_phy_calibration_size + 1; + + /* initialize all valid contexts */ + iwl_init_context(priv, ucode_flags); + + /************************************************** + * This is still part of probe() in a sense... + * + * 7. Setup and register with mac80211 and debugfs + **************************************************/ + if (iwlagn_mac_setup_register(priv, &fw->ucode_capa)) + goto out_destroy_workqueue; + + if (iwl_dbgfs_register(priv, DRV_NAME)) + IWL_ERR(priv, + "failed to create debugfs files. Ignoring error\n"); + + return op_mode; + +out_destroy_workqueue: + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + iwl_uninit_drv(priv); +out_free_eeprom: + iwl_eeprom_free(priv); +out_free_hw: + ieee80211_free_hw(priv->hw); +out: + op_mode = NULL; + return op_mode; +} + +static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); + + iwl_dbgfs_unregister(priv); + + iwl_testmode_cleanup(priv); + iwlagn_mac_unregister(priv); + + iwl_tt_exit(priv); + + /*This will stop the queues, move the device to low power state */ + priv->ucode_loaded = false; + iwl_trans_stop_device(priv->trans); + + iwl_eeprom_free(priv); + + /*netif_stop_queue(dev); */ + flush_workqueue(priv->workqueue); + + /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes + * priv->workqueue... so we can't take down the workqueue + * until now... */ + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + iwl_uninit_drv(priv); + + dev_kfree_skb(priv->beacon_skb); + + iwl_trans_stop_hw(priv->trans, true); + ieee80211_free_hw(priv->hw); +} + +static const char * const desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STABLE", + "FH_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT", + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", +}; + +static struct { char *name; u8 num; } advanced_lookup[] = { + { "NMI_INTERRUPT_WDG", 0x34 }, + { "SYSASSERT", 0x35 }, + { "UCODE_VERSION_MISMATCH", 0x37 }, + { "BAD_COMMAND", 0x38 }, + { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, + { "FATAL_ERROR", 0x3D }, + { "NMI_TRM_HW_ERR", 0x46 }, + { "NMI_INTERRUPT_TRM", 0x4C }, + { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, + { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, + { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, + { "NMI_INTERRUPT_HOST", 0x66 }, + { "NMI_INTERRUPT_ACTION_PT", 0x7C }, + { "NMI_INTERRUPT_UNKNOWN", 0x84 }, + { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, + { "ADVANCED_SYSASSERT", 0 }, +}; + +static const char *desc_lookup(u32 num) +{ + int i; + int max = ARRAY_SIZE(desc_lookup_text); + + if (num < max) + return desc_lookup_text[num]; + + max = ARRAY_SIZE(advanced_lookup) - 1; + for (i = 0; i < max; i++) { + if (advanced_lookup[i].num == num) + break; + } + return advanced_lookup[i].name; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +static void iwl_dump_nic_error_log(struct iwl_priv *priv) +{ + struct iwl_trans *trans = priv->trans; + u32 base; + struct iwl_error_event_table table; + + base = priv->device_pointers.error_event_table; + if (priv->cur_ucode == IWL_UCODE_INIT) { + if (!base) + base = priv->fw->init_errlog_ptr; + } else { + if (!base) + base = priv->fw->inst_errlog_ptr; + } + + if (!iwlagn_hw_valid_rtc_data_addr(base)) { + IWL_ERR(priv, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, + (priv->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return; + } + + /*TODO: Update dbgfs with ISR error stats obtained below */ + iwl_read_targ_mem_words(trans, base, &table, sizeof(table)); + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + IWL_ERR(trans, "Start IWL Error Log Dump:\n"); + IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", + priv->status, table.valid); + } + + trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, + table.data1, table.data2, table.line, + table.blink1, table.blink2, table.ilink1, + table.ilink2, table.bcon_time, table.gp1, + table.gp2, table.gp3, table.ucode_ver, + table.hw_ver, table.brd_ver); + IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, + desc_lookup(table.error_id)); + IWL_ERR(priv, "0x%08X | uPc\n", table.pc); + IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1); + IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2); + IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1); + IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2); + IWL_ERR(priv, "0x%08X | data1\n", table.data1); + IWL_ERR(priv, "0x%08X | data2\n", table.data2); + IWL_ERR(priv, "0x%08X | line\n", table.line); + IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time); + IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low); + IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi); + IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1); + IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2); + IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3); + IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver); + IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver); + IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver); + IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd); + IWL_ERR(priv, "0x%08X | isr0\n", table.isr0); + IWL_ERR(priv, "0x%08X | isr1\n", table.isr1); + IWL_ERR(priv, "0x%08X | isr2\n", table.isr2); + IWL_ERR(priv, "0x%08X | isr3\n", table.isr3); + IWL_ERR(priv, "0x%08X | isr4\n", table.isr4); + IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref); + IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event); + IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control); + IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration); + IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); + IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); + IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); + IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp); + IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler); +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) + +/** + * iwl_print_event_log - Dump error event log to syslog + * + */ +static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode, + int pos, char **buf, size_t bufsz) +{ + u32 i; + u32 base; /* SRAM byte address of event log header */ + u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + struct iwl_trans *trans = priv->trans; + + if (num_events == 0) + return pos; + + base = priv->device_pointers.log_event_table; + if (priv->cur_ucode == IWL_UCODE_INIT) { + if (!base) + base = priv->fw->init_evtlog_ptr; + } else { + if (!base) + base = priv->fw->inst_evtlog_ptr; + } + + if (mode == 0) + event_size = 2 * sizeof(u32); + else + event_size = 3 * sizeof(u32); + + ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&trans->reg_lock, reg_flags); + if (unlikely(!iwl_grab_nic_access(trans))) + goto out_unlock; + + /* Set starting address; reads will auto-increment */ + iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr); + + /* "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. */ + for (i = 0; i < num_events; i++) { + ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + time = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + /* data, ev */ + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOG:0x%08x:%04u\n", + time, ev); + } else { + trace_iwlwifi_dev_ucode_event(trans->dev, 0, + time, ev); + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", + time, ev); + } + } else { + data = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } else { + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + trace_iwlwifi_dev_ucode_event(trans->dev, time, + data, ev); + } + } + } + + /* Allow device to power down */ + iwl_release_nic_access(trans); +out_unlock: + spin_unlock_irqrestore(&trans->reg_lock, reg_flags); + return pos; +} + +/** + * iwl_print_last_event_logs - Dump the newest # of event log to syslog + */ +static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, + u32 num_wraps, u32 next_entry, + u32 size, u32 mode, + int pos, char **buf, size_t bufsz) +{ + /* + * display the newest DEFAULT_LOG_ENTRIES entries + * i.e the entries just before the next ont that uCode would fill. + */ + if (num_wraps) { + if (next_entry < size) { + pos = iwl_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode, + pos, buf, bufsz); + pos = iwl_print_event_log(priv, 0, + next_entry, mode, + pos, buf, bufsz); + } else + pos = iwl_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } else { + if (next_entry < size) { + pos = iwl_print_event_log(priv, 0, next_entry, + mode, pos, buf, bufsz); + } else { + pos = iwl_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } + } + return pos; +} + +#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) + +int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf, bool display) +{ + u32 base; /* SRAM byte address of event log header */ + u32 capacity; /* event log capacity in # entries */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + u32 size; /* # entries that we'll print */ + u32 logsize; + int pos = 0; + size_t bufsz = 0; + struct iwl_trans *trans = priv->trans; + + base = priv->device_pointers.log_event_table; + if (priv->cur_ucode == IWL_UCODE_INIT) { + logsize = priv->fw->init_evtlog_size; + if (!base) + base = priv->fw->init_evtlog_ptr; + } else { + logsize = priv->fw->inst_evtlog_size; + if (!base) + base = priv->fw->inst_evtlog_ptr; + } + + if (!iwlagn_hw_valid_rtc_data_addr(base)) { + IWL_ERR(priv, + "Invalid event log pointer 0x%08X for %s uCode\n", + base, + (priv->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return -EINVAL; + } + + /* event log header */ + capacity = iwl_read_targ_mem(trans, base); + mode = iwl_read_targ_mem(trans, base + (1 * sizeof(u32))); + num_wraps = iwl_read_targ_mem(trans, base + (2 * sizeof(u32))); + next_entry = iwl_read_targ_mem(trans, base + (3 * sizeof(u32))); + + if (capacity > logsize) { + IWL_ERR(priv, "Log capacity %d is bogus, limit to %d " + "entries\n", capacity, logsize); + capacity = logsize; + } + + if (next_entry > logsize) { + IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", + next_entry, logsize); + next_entry = logsize; + } + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n"); + return pos; + } + +#ifdef CONFIG_IWLWIFI_DEBUG + if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log) + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; +#else + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; +#endif + IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", + size); + +#ifdef CONFIG_IWLWIFI_DEBUG + if (display) { + if (full_log) + bufsz = capacity * 48; + else + bufsz = size * 48; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + } + if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) { + /* + * if uCode has wrapped back to top of log, + * start at the oldest entry, + * i.e the next one that uCode would fill. + */ + if (num_wraps) + pos = iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode, + pos, buf, bufsz); + /* (then/else) start at top of log */ + pos = iwl_print_event_log(priv, 0, + next_entry, mode, pos, buf, bufsz); + } else + pos = iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); +#else + pos = iwl_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); +#endif + return pos; +} + +static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) +{ + unsigned int reload_msec; + unsigned long reload_jiffies; + +#ifdef CONFIG_IWLWIFI_DEBUG + if (iwl_have_debug_level(IWL_DL_FW_ERRORS)) + iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS); +#endif + + /* uCode is no longer loaded. */ + priv->ucode_loaded = false; + + /* Set the FW error flag -- cleared on iwl_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + + iwl_abort_notification_waits(&priv->notif_wait); + + /* Keep the restart process from trying to send host + * commands by clearing the ready bit */ + clear_bit(STATUS_READY, &priv->status); + + wake_up(&priv->trans->wait_command_queue); + + if (!ondemand) { + /* + * If firmware keep reloading, then it indicate something + * serious wrong and firmware having problem to recover + * from it. Instead of keep trying which will fill the syslog + * and hang the system, let's just stop it + */ + reload_jiffies = jiffies; + reload_msec = jiffies_to_msecs((long) reload_jiffies - + (long) priv->reload_jiffies); + priv->reload_jiffies = reload_jiffies; + if (reload_msec <= IWL_MIN_RELOAD_DURATION) { + priv->reload_count++; + if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) { + IWL_ERR(priv, "BUG_ON, Stop restarting\n"); + return; + } + } else + priv->reload_count = 0; + } + + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { + if (iwlwifi_mod_params.restart_fw) { + IWL_DEBUG_FW_ERRORS(priv, + "Restarting adapter due to uCode error.\n"); + queue_work(priv->workqueue, &priv->restart); + } else + IWL_DEBUG_FW_ERRORS(priv, + "Detected FW error, but not restarting\n"); + } +} + +static void iwl_nic_error(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_ERR(priv, "Loaded firmware version: %s\n", + priv->fw->fw_version); + + iwl_dump_nic_error_log(priv); + iwl_dump_nic_event_log(priv, false, NULL, false); + + iwlagn_fw_error(priv, false); +} + +static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + if (!iwl_check_for_ct_kill(priv)) { + IWL_ERR(priv, "Restarting adapter queue is full\n"); + iwlagn_fw_error(priv, false); + } +} + +static void iwl_nic_config(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + priv->lib->nic_config(priv); +} + +static void iwl_wimax_active(struct iwl_op_mode *op_mode) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + clear_bit(STATUS_READY, &priv->status); + IWL_ERR(priv, "RF is used by WiMAX\n"); +} + +static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + int mq = priv->queue_to_mac80211[queue]; + + if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) + return; + + if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) { + IWL_DEBUG_TX_QUEUES(priv, + "queue %d (mac80211 %d) already stopped\n", + queue, mq); + return; + } + + set_bit(mq, &priv->transport_queue_stop); + ieee80211_stop_queue(priv->hw, mq); +} + +static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + int mq = priv->queue_to_mac80211[queue]; + + if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) + return; + + if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) { + IWL_DEBUG_TX_QUEUES(priv, + "queue %d (mac80211 %d) already awake\n", + queue, mq); + return; + } + + clear_bit(mq, &priv->transport_queue_stop); + + if (!priv->passive_no_rx) + ieee80211_wake_queue(priv->hw, mq); +} + +void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) +{ + int mq; + + if (!priv->passive_no_rx) + return; + + for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { + if (!test_bit(mq, &priv->transport_queue_stop)) { + IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq); + ieee80211_wake_queue(priv->hw, mq); + } else { + IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq); + } + } + + priv->passive_no_rx = false; +} + +static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info; + + info = IEEE80211_SKB_CB(skb); + kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1])); + dev_kfree_skb_any(skb); +} + +static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + if (state) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); +} + +const struct iwl_op_mode_ops iwl_dvm_ops = { + .start = iwl_op_mode_dvm_start, + .stop = iwl_op_mode_dvm_stop, + .rx = iwl_rx_dispatch, + .queue_full = iwl_stop_sw_queue, + .queue_not_full = iwl_wake_sw_queue, + .hw_rf_kill = iwl_set_hw_rfkill_state, + .free_skb = iwl_free_skb, + .nic_error = iwl_nic_error, + .cmd_queue_full = iwl_cmd_queue_full, + .nic_config = iwl_nic_config, + .wimax_active = iwl_wimax_active, +}; + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +struct kmem_cache *iwl_tx_cmd_pool; + +static int __init iwl_init(void) +{ + + int ret; + pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_COPYRIGHT "\n"); + + iwl_tx_cmd_pool = kmem_cache_create("iwl_dev_cmd", + sizeof(struct iwl_device_cmd), + sizeof(void *), 0, NULL); + if (!iwl_tx_cmd_pool) + return -ENOMEM; + + ret = iwlagn_rate_control_register(); + if (ret) { + pr_err("Unable to register rate control algorithm: %d\n", ret); + goto error_rc_register; + } + + ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); + if (ret) { + pr_err("Unable to register op_mode: %d\n", ret); + goto error_opmode_register; + } + return ret; + +error_opmode_register: + iwlagn_rate_control_unregister(); +error_rc_register: + kmem_cache_destroy(iwl_tx_cmd_pool); + return ret; +} +module_init(iwl_init); + +static void __exit iwl_exit(void) +{ + iwl_opmode_deregister("iwldvm"); + iwlagn_rate_control_unregister(); + kmem_cache_destroy(iwl_tx_cmd_pool); +} +module_exit(iwl_exit); diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c new file mode 100644 index 000000000000..72c940759ac7 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -0,0 +1,388 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + + +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-debug.h" +#include "iwl-trans.h" +#include "iwl-modparams.h" +#include "eeprom.h" +#include "dev.h" +#include "agn.h" +#include "commands.h" +#include "power.h" + +/* + * Setting power level allows the card to go to sleep when not busy. + * + * We calculate a sleep command based on the required latency, which + * we get from mac80211. In order to handle thermal throttling, we can + * also use pre-defined power levels. + */ + +/* + * This defines the old power levels. They are still used by default + * (level 1) and for thermal throttle (levels 3 through 5) + */ + +struct iwl_power_vec_entry { + struct iwl_powertable_cmd cmd; + u8 no_dtim; /* number of skip dtim */ +}; + +#define IWL_DTIM_RANGE_0_MAX 2 +#define IWL_DTIM_RANGE_1_MAX 10 + +#define NOSLP cpu_to_le16(0), 0, 0 +#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 +#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \ + IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \ + IWL_POWER_ADVANCE_PM_ENA_MSK) +#define ASLP_TOUT(T) cpu_to_le32(T) +#define TU_TO_USEC 1024 +#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) +#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ + cpu_to_le32(X1), \ + cpu_to_le32(X2), \ + cpu_to_le32(X3), \ + cpu_to_le32(X4)} +/* default power management (not Tx power) table values */ +/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ +/* DTIM 0 - 2 */ +static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} +}; + + +/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ +/* DTIM 3 - 10 */ +static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} +}; + +/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ +/* DTIM 11 - */ +static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} +}; + +/* advance power management */ +/* DTIM 0 - 2 */ +static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = { + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} +}; + + +/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ +/* DTIM 3 - 10 */ +static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = { + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2} +}; + +/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ +/* DTIM 11 - */ +static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = { + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, + {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), + SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} +}; + +static void iwl_static_sleep_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd, + enum iwl_power_level lvl, int period) +{ + const struct iwl_power_vec_entry *table; + int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; + int i; + u8 skip; + u32 slp_itrvl; + + if (priv->cfg->adv_pm) { + table = apm_range_2; + if (period <= IWL_DTIM_RANGE_1_MAX) + table = apm_range_1; + if (period <= IWL_DTIM_RANGE_0_MAX) + table = apm_range_0; + } else { + table = range_2; + if (period <= IWL_DTIM_RANGE_1_MAX) + table = range_1; + if (period <= IWL_DTIM_RANGE_0_MAX) + table = range_0; + } + + if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) + memset(cmd, 0, sizeof(*cmd)); + else + *cmd = table[lvl].cmd; + + if (period == 0) { + skip = 0; + period = 1; + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + max_sleep[i] = 1; + + } else { + skip = table[lvl].no_dtim; + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); + max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; + } + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + /* figure out the listen interval based on dtim period and skip */ + if (slp_itrvl == 0xFF) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32(period * (skip + 1)); + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + if (slp_itrvl > period) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32((slp_itrvl / period) * period); + + if (skip) + cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; + else + cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; + + if (priv->cfg->base_params->shadow_reg_enable) + cmd->flags |= IWL_POWER_SHADOW_REG_ENA; + else + cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; + + if (iwl_advanced_bt_coexist(priv)) { + if (!priv->cfg->bt_params->bt_sco_disable) + cmd->flags |= IWL_POWER_BT_SCO_ENA; + else + cmd->flags &= ~IWL_POWER_BT_SCO_ENA; + } + + + slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); + if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) + cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = + cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); + + /* enforce max sleep interval */ + for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { + if (le32_to_cpu(cmd->sleep_interval[i]) > + (max_sleep[i] * period)) + cmd->sleep_interval[i] = + cpu_to_le32(max_sleep[i] * period); + if (i != (IWL_POWER_VEC_SIZE - 1)) { + if (le32_to_cpu(cmd->sleep_interval[i]) > + le32_to_cpu(cmd->sleep_interval[i+1])) + cmd->sleep_interval[i] = + cmd->sleep_interval[i+1]; + } + } + + if (priv->power_data.bus_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + else + cmd->flags &= ~IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", + skip, period); + /* The power level here is 0-4 (used as array index), but user expects + to see 1-5 (according to spec). */ + IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); +} + +static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + if (priv->power_data.bus_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); +} + +static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) +{ + IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); + IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); + IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); + IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); + IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", + le32_to_cpu(cmd->sleep_interval[0]), + le32_to_cpu(cmd->sleep_interval[1]), + le32_to_cpu(cmd->sleep_interval[2]), + le32_to_cpu(cmd->sleep_interval[3]), + le32_to_cpu(cmd->sleep_interval[4])); + + return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC, + sizeof(struct iwl_powertable_cmd), cmd); +} + +static void iwl_power_build_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; + int dtimper; + + dtimper = priv->hw->conf.ps_dtim_period ?: 1; + + if (priv->wowlan) + iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); + else if (!priv->cfg->base_params->no_idle_support && + priv->hw->conf.flags & IEEE80211_CONF_IDLE) + iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); + else if (iwl_tt_is_low_power_state(priv)) { + /* in thermal throttling low power state */ + iwl_static_sleep_cmd(priv, cmd, + iwl_tt_current_power_mode(priv), dtimper); + } else if (!enabled) + iwl_power_sleep_cam_cmd(priv, cmd); + else if (priv->power_data.debug_sleep_level_override >= 0) + iwl_static_sleep_cmd(priv, cmd, + priv->power_data.debug_sleep_level_override, + dtimper); + else { + /* Note that the user parameter is 1-5 (according to spec), + but we pass 0-4 because it acts as an array index. */ + if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 && + iwlwifi_mod_params.power_level <= IWL_POWER_NUM) + iwl_static_sleep_cmd(priv, cmd, + iwlwifi_mod_params.power_level - 1, dtimper); + else + iwl_static_sleep_cmd(priv, cmd, + IWL_POWER_INDEX_1, dtimper); + } +} + +int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, + bool force) +{ + int ret; + bool update_chains; + + lockdep_assert_held(&priv->mutex); + + /* Don't update the RX chain when chain noise calibration is running */ + update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || + priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; + + if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) + return 0; + + if (!iwl_is_ready_rf(priv)) + return -EIO; + + /* scan complete use sleep_power_next, need to be updated */ + memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); + if (test_bit(STATUS_SCANNING, &priv->status) && !force) { + IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); + return 0; + } + + if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) + iwl_dvm_set_pmi(priv, true); + + ret = iwl_set_power(priv, cmd); + if (!ret) { + if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) + iwl_dvm_set_pmi(priv, false); + + if (update_chains) + iwl_update_chain_flags(priv); + else + IWL_DEBUG_POWER(priv, + "Cannot update the power, chain noise " + "calibration running: %d\n", + priv->chain_noise_data.state); + + memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); + } else + IWL_ERR(priv, "set power fail, ret = %d", ret); + + return ret; +} + +int iwl_power_update_mode(struct iwl_priv *priv, bool force) +{ + struct iwl_powertable_cmd cmd; + + iwl_power_build_cmd(priv, &cmd); + return iwl_power_set_mode(priv, &cmd, force); +} + +/* initialize to default */ +void iwl_power_initialize(struct iwl_priv *priv) +{ + priv->power_data.bus_pm = priv->trans->pm_support; + + priv->power_data.debug_sleep_level_override = -1; + + memset(&priv->power_data.sleep_cmd, 0, + sizeof(priv->power_data.sleep_cmd)); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/power.h b/drivers/net/wireless/iwlwifi/dvm/power.h new file mode 100644 index 000000000000..a2cee7f04848 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/power.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#ifndef __iwl_power_setting_h__ +#define __iwl_power_setting_h__ + +#include "commands.h" + +struct iwl_power_mgr { + struct iwl_powertable_cmd sleep_cmd; + struct iwl_powertable_cmd sleep_cmd_next; + int debug_sleep_level_override; + bool bus_pm; +}; + +int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, + bool force); +int iwl_power_update_mode(struct iwl_priv *priv, bool force); +void iwl_power_initialize(struct iwl_priv *priv); + +extern bool no_sleep_autoadjust; + +#endif /* __iwl_power_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c new file mode 100644 index 000000000000..af102910e689 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -0,0 +1,3367 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "dev.h" +#include "agn.h" + +#define RS_NAME "iwl-agn-rs" + +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_NUMBER_TRY 1 +#define IWL_HT_NUMBER_TRY 3 + +#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ +#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ + +/* max allowed rate miss before sync LQ cmd */ +#define IWL_MISSED_RATE_MAX 15 +/* max time to accum history 2 seconds */ +#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) + +static u8 rs_ht_to_legacy[] = { + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX +}; + +static const u8 ant_toggle_lookup[] = { + /*ANT_NONE -> */ ANT_NONE, + /*ANT_A -> */ ANT_B, + /*ANT_B -> */ ANT_C, + /*ANT_AB -> */ ANT_BC, + /*ANT_C -> */ ANT_A, + /*ANT_AC -> */ ANT_AB, + /*ANT_BC -> */ ANT_AC, + /*ANT_ABC -> */ ANT_ABC, +}; + +#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ + [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ + IWL_RATE_SISO_##s##M_PLCP, \ + IWL_RATE_MIMO2_##s##M_PLCP,\ + IWL_RATE_MIMO3_##s##M_PLCP,\ + IWL_RATE_##r##M_IEEE, \ + IWL_RATE_##ip##M_INDEX, \ + IWL_RATE_##in##M_INDEX, \ + IWL_RATE_##rp##M_INDEX, \ + IWL_RATE_##rn##M_INDEX, \ + IWL_RATE_##pp##M_INDEX, \ + IWL_RATE_##np##M_INDEX } + +/* + * Parameter order: + * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate + * + * If there isn't a valid next or previous rate then INV is used which + * maps to IWL_RATE_INVALID + * + */ +const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ + IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ + IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ + IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ + IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ + IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ + IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ + IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ + IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ + IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ + IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ + IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ + /* FIXME:RS: ^^ should be INV (legacy) */ +}; + +static inline u8 rs_extract_rate(u32 rate_n_flags) +{ + return (u8)(rate_n_flags & RATE_MCS_RATE_MSK); +} + +static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) +{ + int idx = 0; + + /* HT rate format */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = rs_extract_rate(rate_n_flags); + + if (idx >= IWL_RATE_MIMO3_6M_PLCP) + idx = idx - IWL_RATE_MIMO3_6M_PLCP; + else if (idx >= IWL_RATE_MIMO2_6M_PLCP) + idx = idx - IWL_RATE_MIMO2_6M_PLCP; + + idx += IWL_FIRST_OFDM_RATE; + /* skip 9M not supported in ht*/ + if (idx >= IWL_RATE_9M_INDEX) + idx += 1; + if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) + return idx; + + /* legacy rate format, search for match in table */ + } else { + for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) + if (iwl_rates[idx].plcp == + rs_extract_rate(rate_n_flags)) + return idx; + } + + return -1; +} + +static void rs_rate_scale_perform(struct iwl_priv *priv, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta); +static void rs_fill_link_cmd(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 rate_n_flags); +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); + + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index); +#else +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{} +#endif + +/** + * The following tables contain the expected throughput metrics for all rates + * + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * + * where invalid entries are zeros. + * + * CCK rates are only valid in legacy table and will only be used in G + * (2.4 GHz) band. + */ + +static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 +}; + +static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ + {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ + {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ + {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ +}; + +static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ + {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ + {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ +}; + +static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ + {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ + {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ + {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ +}; + +static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ + {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ + {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ +}; + +static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ + {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ + {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ + {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ +}; + +static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ + {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ + {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ + {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ +}; + +/* mbps, mcs */ +static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { + { "1", "BPSK DSSS"}, + { "2", "QPSK DSSS"}, + {"5.5", "BPSK CCK"}, + { "11", "QPSK CCK"}, + { "6", "BPSK 1/2"}, + { "9", "BPSK 1/2"}, + { "12", "QPSK 1/2"}, + { "18", "QPSK 3/4"}, + { "24", "16QAM 1/2"}, + { "36", "16QAM 3/4"}, + { "48", "64QAM 2/3"}, + { "54", "64QAM 3/4"}, + { "60", "64QAM 5/6"}, +}; + +#define MCS_INDEX_PER_STREAM (8) + +static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) +{ + window->data = 0; + window->success_counter = 0; + window->success_ratio = IWL_INVALID_VALUE; + window->counter = 0; + window->average_tpt = IWL_INVALID_VALUE; + window->stamp = 0; +} + +static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return (ant_type & valid_antenna) == ant_type; +} + +/* + * removes the old data from the statistics. All data that is older than + * TID_MAX_TIME_DIFF, will be deleted. + */ +static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) +{ + /* The oldest age we want to keep */ + u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; + + while (tl->queue_count && + (tl->time_stamp < oldest_time)) { + tl->total -= tl->packet_count[tl->head]; + tl->packet_count[tl->head] = 0; + tl->time_stamp += TID_QUEUE_CELL_SPACING; + tl->queue_count--; + tl->head++; + if (tl->head >= TID_QUEUE_MAX_SIZE) + tl->head = 0; + } +} + +/* + * increment traffic load value for tid and also remove + * any old values if passed the certain time period + */ +static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, + struct ieee80211_hdr *hdr) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + u8 tid; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } else + return IWL_MAX_TID_COUNT; + + if (unlikely(tid >= IWL_MAX_TID_COUNT)) + return IWL_MAX_TID_COUNT; + + tl = &lq_data->load[tid]; + + curr_time -= curr_time % TID_ROUND_VALUE; + + /* Happens only for the first packet. Initialize the data */ + if (!(tl->queue_count)) { + tl->total = 1; + tl->time_stamp = curr_time; + tl->queue_count = 1; + tl->head = 0; + tl->packet_count[0] = 1; + return IWL_MAX_TID_COUNT; + } + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + rs_tl_rm_old_stats(tl, curr_time); + + index = (tl->head + index) % TID_QUEUE_MAX_SIZE; + tl->packet_count[index] = tl->packet_count[index] + 1; + tl->total = tl->total + 1; + + if ((index + 1) > tl->queue_count) + tl->queue_count = index + 1; + + return tid; +} + +#ifdef CONFIG_MAC80211_DEBUGFS +/** + * Program the device to use fixed rate for frame transmit + * This is for debugging/testing only + * once the device start use fixed rate, we need to reload the module + * to being back the normal operation. + */ +static void rs_program_fix_rate(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_station_priv *sta_priv = + container_of(lq_sta, struct iwl_station_priv, lq_sta); + struct iwl_rxon_context *ctx = sta_priv->ctx; + + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + /* testmode has higher priority to overwirte the fixed rate */ + if (priv->tm_fixed_rate) + lq_sta->dbg_fixed_rate = priv->tm_fixed_rate; +#endif + + IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); + iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, + false); + } +} +#endif + +/* + get the traffic load value for tid +*/ +static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + + if (tid >= IWL_MAX_TID_COUNT) + return 0; + + tl = &(lq_data->load[tid]); + + curr_time -= curr_time % TID_ROUND_VALUE; + + if (!(tl->queue_count)) + return 0; + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + rs_tl_rm_old_stats(tl, curr_time); + + return tl->total; +} + +static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, + struct iwl_lq_sta *lq_data, u8 tid, + struct ieee80211_sta *sta) +{ + int ret = -EAGAIN; + u32 load; + + /* + * Don't create TX aggregation sessions when in high + * BT traffic, as they would just be disrupted by BT. + */ + if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) { + IWL_ERR(priv, "BT traffic (%d), no aggregation allowed\n", + priv->bt_traffic_load); + return ret; + } + + load = rs_tl_get_load(lq_data, tid); + + if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) { + IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", + sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + } else { + IWL_DEBUG_HT(priv, "Aggregation not enabled for tid %d " + "because load = %u\n", tid, load); + } + return ret; +} + +static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, + struct iwl_lq_sta *lq_data, + struct ieee80211_sta *sta) +{ + if (tid < IWL_MAX_TID_COUNT) + rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); + else + IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", + tid, IWL_MAX_TID_COUNT); +} + +static inline int get_num_of_ant_from_rate(u32 rate_n_flags) +{ + return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); +} + +/* + * Static function to get the expected throughput from an iwl_scale_tbl_info + * that wraps a NULL pointer check + */ +static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) +{ + if (tbl->expected_tpt) + return tbl->expected_tpt[rs_index]; + return 0; +} + +/** + * rs_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 62 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes) +{ + struct iwl_rate_scale_data *window = NULL; + static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); + s32 fail_count, tpt; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + /* Select window for current tx bit rate */ + window = &(tbl->win[scale_index]); + + /* Get expected throughput */ + tpt = get_expected_tpt(tbl, scale_index); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history window; anything older isn't really relevant any more. + * If we have filled up the sliding window, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ + while (attempts > 0) { + if (window->counter >= IWL_RATE_MAX_WINDOW) { + + /* remove earliest */ + window->counter = IWL_RATE_MAX_WINDOW - 1; + + if (window->data & mask) { + window->data &= ~mask; + window->success_counter--; + } + } + + /* Increment frames-attempted counter */ + window->counter++; + + /* Shift bitmap by one frame to throw away oldest history */ + window->data <<= 1; + + /* Mark the most recent #successes attempts as successful */ + if (successes > 0) { + window->success_counter++; + window->data |= 0x1; + successes--; + } + + attempts--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (window->counter > 0) + window->success_ratio = 128 * (100 * window->success_counter) + / window->counter; + else + window->success_ratio = IWL_INVALID_VALUE; + + fail_count = window->counter - window->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) + window->average_tpt = (window->success_ratio * tpt + 64) / 128; + else + window->average_tpt = IWL_INVALID_VALUE; + + /* Tag this window as having been updated */ + window->stamp = jiffies; + + return 0; +} + +/* + * Fill uCode API rate_n_flags field, based on "search" or "active" table. + */ +/* FIXME:RS:remove this function and put the flags statically in the table */ +static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, + struct iwl_scale_tbl_info *tbl, + int index, u8 use_green) +{ + u32 rate_n_flags = 0; + + if (is_legacy(tbl->lq_type)) { + rate_n_flags = iwl_rates[index].plcp; + if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) + rate_n_flags |= RATE_MCS_CCK_MSK; + + } else if (is_Ht(tbl->lq_type)) { + if (index > IWL_LAST_OFDM_RATE) { + IWL_ERR(priv, "Invalid HT rate index %d\n", index); + index = IWL_LAST_OFDM_RATE; + } + rate_n_flags = RATE_MCS_HT_MSK; + + if (is_siso(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_siso; + else if (is_mimo2(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_mimo2; + else + rate_n_flags |= iwl_rates[index].plcp_mimo3; + } else { + IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); + } + + rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); + + if (is_Ht(tbl->lq_type)) { + if (tbl->is_ht40) { + if (tbl->is_dup) + rate_n_flags |= RATE_MCS_DUP_MSK; + else + rate_n_flags |= RATE_MCS_HT40_MSK; + } + if (tbl->is_SGI) + rate_n_flags |= RATE_MCS_SGI_MSK; + + if (use_green) { + rate_n_flags |= RATE_MCS_GF_MSK; + if (is_siso(tbl->lq_type) && tbl->is_SGI) { + rate_n_flags &= ~RATE_MCS_SGI_MSK; + IWL_ERR(priv, "GF was set with SGI:SISO\n"); + } + } + } + return rate_n_flags; +} + +/* + * Interpret uCode API's rate_n_flags format, + * fill "search" or "active" tx mode table. + */ +static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, + enum ieee80211_band band, + struct iwl_scale_tbl_info *tbl, + int *rate_idx) +{ + u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); + u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); + u8 mcs; + + memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); + *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); + + if (*rate_idx == IWL_RATE_INVALID) { + *rate_idx = -1; + return -EINVAL; + } + tbl->is_SGI = 0; /* default legacy setup */ + tbl->is_ht40 = 0; + tbl->is_dup = 0; + tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); + tbl->lq_type = LQ_NONE; + tbl->max_search = IWL_MAX_SEARCH; + + /* legacy rate format */ + if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + if (num_of_ant == 1) { + if (band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + } + /* HT rate format */ + } else { + if (rate_n_flags & RATE_MCS_SGI_MSK) + tbl->is_SGI = 1; + + if ((rate_n_flags & RATE_MCS_HT40_MSK) || + (rate_n_flags & RATE_MCS_DUP_MSK)) + tbl->is_ht40 = 1; + + if (rate_n_flags & RATE_MCS_DUP_MSK) + tbl->is_dup = 1; + + mcs = rs_extract_rate(rate_n_flags); + + /* SISO */ + if (mcs <= IWL_RATE_SISO_60M_PLCP) { + if (num_of_ant == 1) + tbl->lq_type = LQ_SISO; /*else NONE*/ + /* MIMO2 */ + } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { + if (num_of_ant == 2) + tbl->lq_type = LQ_MIMO2; + /* MIMO3 */ + } else { + if (num_of_ant == 3) { + tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; + tbl->lq_type = LQ_MIMO3; + } + } + } + return 0; +} + +/* switch to another antenna/antennas and return 1 */ +/* if no other valid antenna found, return 0 */ +static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, + struct iwl_scale_tbl_info *tbl) +{ + u8 new_ant_type; + + if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + return 0; + + if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) + return 0; + + new_ant_type = ant_toggle_lookup[tbl->ant_type]; + + while ((new_ant_type != tbl->ant_type) && + !rs_is_valid_ant(valid_ant, new_ant_type)) + new_ant_type = ant_toggle_lookup[new_ant_type]; + + if (new_ant_type == tbl->ant_type) + return 0; + + tbl->ant_type = new_ant_type; + *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; + *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; + return 1; +} + +/** + * Green-field mode is valid if the station supports it and + * there are no non-GF stations present in the BSS. + */ +static bool rs_use_green(struct ieee80211_sta *sta) +{ + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ctx->ht.non_gf_sta_present); +} + +/** + * rs_get_supported_rates - get the available rates + * + * if management frame or broadcast frame only return + * basic available rates. + * + */ +static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, + struct ieee80211_hdr *hdr, + enum iwl_table_type rate_type) +{ + if (is_legacy(rate_type)) { + return lq_sta->active_legacy_rate; + } else { + if (is_siso(rate_type)) + return lq_sta->active_siso_rate; + else if (is_mimo2(rate_type)) + return lq_sta->active_mimo2_rate; + else + return lq_sta->active_mimo3_rate; + } +} + +static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, + int rate_type) +{ + u8 high = IWL_RATE_INVALID; + u8 low = IWL_RATE_INVALID; + + /* 802.11A or ht walks to the next literal adjacent rate in + * the rate table */ + if (is_a_band(rate_type) || !is_legacy(rate_type)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = index - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = index + 1; + for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = index; + while (low != IWL_RATE_INVALID) { + low = iwl_rates[low].prev_rs; + if (low == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); + } + + high = index; + while (high != IWL_RATE_INVALID) { + high = iwl_rates[high].next_rs; + if (high == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); + } + + return (high << 8) | low; +} + +static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + u8 scale_index, u8 ht_possible) +{ + s32 low; + u16 rate_mask; + u16 high_low; + u8 switch_to_legacy = 0; + u8 is_green = lq_sta->is_green; + struct iwl_priv *priv = lq_sta->drv; + + /* check if we need to switch from HT to legacy rates. + * assumption is that mandatory rates (1Mbps or 6Mbps) + * are always supported (spec demand) */ + if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { + switch_to_legacy = 1; + scale_index = rs_ht_to_legacy[scale_index]; + if (lq_sta->band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + + if (num_of_ant(tbl->ant_type) > 1) + tbl->ant_type = + first_antenna(priv->hw_params.valid_tx_ant); + + tbl->is_ht40 = 0; + tbl->is_SGI = 0; + tbl->max_search = IWL_MAX_SEARCH; + } + + rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); + + /* Mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + /* supp_rates has no CCK bits in A mode */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate_mask = (u16)(rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_mask = (u16)(rate_mask & lq_sta->supp_rates); + } + + /* If we switched from HT to legacy, check current rate */ + if (switch_to_legacy && (rate_mask & (1 << scale_index))) { + low = scale_index; + goto out; + } + + high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, + tbl->lq_type); + low = high_low & 0xff; + + if (low == IWL_RATE_INVALID) + low = scale_index; + +out: + return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); +} + +/* + * Simple function to compare two rate scale table types + */ +static bool table_type_matches(struct iwl_scale_tbl_info *a, + struct iwl_scale_tbl_info *b) +{ + return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && + (a->is_SGI == b->is_SGI); +} + +static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_scale_tbl_info *tbl; + bool full_concurrent = priv->bt_full_concurrent; + + if (priv->bt_ant_couple_ok) { + /* + * Is there a need to switch between + * full concurrency and 3-wire? + */ + if (priv->bt_ci_compliance && priv->bt_ant_couple_ok) + full_concurrent = true; + else + full_concurrent = false; + } + if ((priv->bt_traffic_load != priv->last_bt_traffic_load) || + (priv->bt_full_concurrent != full_concurrent)) { + priv->bt_full_concurrent = full_concurrent; + priv->last_bt_traffic_load = priv->bt_traffic_load; + + /* Update uCode's rate table. */ + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + + queue_work(priv->workqueue, &priv->bt_full_concurrency); + } +} + +/* + * mac80211 sends us Tx status + */ +static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + struct sk_buff *skb) +{ + int legacy_success; + int retries; + int rs_index, mac_index, i; + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_link_quality_cmd *table; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r; + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + enum mac80211_rate_control_flags mac_flags; + u32 tx_rate; + struct iwl_scale_tbl_info tbl_type; + struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + return; + } + + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + /* + * Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + table = &lq_sta->lq; + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); + if (priv->band == IEEE80211_BAND_5GHZ) + rs_index -= IWL_FIRST_OFDM_RATE; + mac_flags = info->status.rates[0].flags; + mac_index = info->status.rates[0].idx; + /* For HT packets, map MCS to PLCP */ + if (mac_flags & IEEE80211_TX_RC_MCS) { + mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ + if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) + mac_index++; + /* + * mac80211 HT index is always zero-indexed; we need to move + * HT OFDM rates after CCK rates in 2.4 GHz band + */ + if (priv->band == IEEE80211_BAND_2GHZ) + mac_index += IWL_FIRST_OFDM_RATE; + } + /* Here we actually compare this rate to the latest LQ command */ + if ((mac_index < 0) || + (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || + (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || + (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || + (tbl_type.ant_type != info->status.antenna) || + (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || + (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || + (rs_index != mac_index)) { + IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); + /* + * Since rates mis-match, the last LQ command may have failed. + * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. + */ + lq_sta->missed_rate_counter++; + if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { + lq_sta->missed_rate_counter = 0; + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + } + /* Regardless, ignore this status info for outdated rate */ + return; + } else + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + /* Figure out if rate scale algorithm is in active or search table */ + if (table_type_matches(&tbl_type, + &(lq_sta->lq_info[lq_sta->active_tbl]))) { + curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + } else if (table_type_matches(&tbl_type, + &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + } else { + IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); + tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", + tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); + tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", + tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); + IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", + tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); + /* + * no matching table found, let's by-pass the data collection + * and continue to perform rate scale to find the rate table + */ + rs_stay_in_table(lq_sta, true); + goto done; + } + + /* + * Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first index into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, + &rs_index); + rs_collect_tx_data(curr_tbl, rs_index, + info->status.ampdu_len, + info->status.ampdu_ack_len); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += info->status.ampdu_ack_len; + lq_sta->total_failed += (info->status.ampdu_len - + info->status.ampdu_ack_len); + } + } else { + /* + * For legacy, update frame history with for each Tx retry. + */ + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, + &tbl_type, &rs_index); + /* + * Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (table_type_matches(&tbl_type, curr_tbl)) + tmp_tbl = curr_tbl; + else if (table_type_matches(&tbl_type, other_tbl)) + tmp_tbl = other_tbl; + else + continue; + rs_collect_tx_data(tmp_tbl, rs_index, 1, + i < retries ? 0 : legacy_success); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); + } + } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = tx_rate; +done: + /* See if there's a better rate or modulation mode to try. */ + if (sta && sta->supp_rates[sband->band]) + rs_rate_scale_perform(priv, skb, sta, lq_sta); + +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_TESTMODE) + if ((priv->tm_fixed_rate) && + (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) + rs_program_fix_rate(priv, lq_sta); +#endif + if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) + rs_bt_update_lq(priv, ctx, lq_sta); +} + +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history statistics. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ +static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, + struct iwl_lq_sta *lq_sta) +{ + IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); + lq_sta->stay_in_tbl = 1; /* only place this gets set */ + if (is_legacy) { + lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; + } else { + lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; + } + lq_sta->table_count = 0; + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = jiffies; + lq_sta->action_counter = 0; +} + +/* + * Find correct throughput table for given mode of modulation + */ +static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + /* Used to choose among HT tables */ + s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; + + /* Check for invalid LQ type */ + if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Legacy rates have only one table */ + if (is_legacy(tbl->lq_type)) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation + * status */ + if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_siso20MHz; + else if (is_siso(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_siso40MHz; + else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + else if (is_mimo2(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo3_20MHz; + else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ + ht_tbl_pointer = expected_tpt_mimo3_40MHz; + + if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ + tbl->expected_tpt = ht_tbl_pointer[0]; + else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ + tbl->expected_tpt = ht_tbl_pointer[1]; + else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ + tbl->expected_tpt = ht_tbl_pointer[2]; + else /* AGG+SGI */ + tbl->expected_tpt = ht_tbl_pointer[3]; +} + +/* + * Find starting rate for new "search" high-throughput mode of modulation. + * Goal is to find lowest expected rate (under perfect conditions) that is + * above the current measured throughput of "active" mode, to give new mode + * a fair chance to prove itself without too many challenges. + * + * This gets called when transitioning to more aggressive modulation + * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive + * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need + * to decrease to match "active" throughput. When moving from MIMO to SISO, + * bit rate will typically need to increase, but not if performance was bad. + */ +static s32 rs_get_best_rate(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, /* "search" */ + u16 rate_mask, s8 index) +{ + /* "active" values */ + struct iwl_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 active_sr = active_tbl->win[index].success_ratio; + s32 active_tpt = active_tbl->expected_tpt[index]; + + /* expected "search" throughput */ + s32 *tpt_tbl = tbl->expected_tpt; + + s32 new_rate, high, low, start_hi; + u16 high_low; + s8 rate = index; + + new_rate = high = low = start_hi = IWL_RATE_INVALID; + + for (; ;) { + high_low = rs_get_adjacent_rate(priv, rate, rate_mask, + tbl->lq_type); + + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* + * Lower the "search" bit rate, to give new "search" mode + * approximately the same throughput as "active" if: + * + * 1) "Active" mode has been working modestly well (but not + * great), and expected "search" throughput (under perfect + * conditions) at candidate rate is above the actual + * measured "active" throughput (but less than expected + * "active" throughput under perfect conditions). + * OR + * 2) "Active" mode has been working perfectly or very well + * and expected "search" throughput (under perfect + * conditions) at candidate rate is above expected + * "active" throughput (under perfect conditions). + */ + if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && + ((active_sr > IWL_RATE_DECREASE_TH) && + (active_sr <= IWL_RATE_HIGH_TH) && + (tpt_tbl[rate] <= active_tpt))) || + ((active_sr >= IWL_RATE_SCALE_SWITCH) && + (tpt_tbl[rate] > active_tpt))) { + + /* (2nd or later pass) + * If we've already tried to raise the rate, and are + * now trying to lower it, use the higher rate. */ + if (start_hi != IWL_RATE_INVALID) { + new_rate = start_hi; + break; + } + + new_rate = rate; + + /* Loop again with lower rate */ + if (low != IWL_RATE_INVALID) + rate = low; + + /* Lower rate not available, use the original */ + else + break; + + /* Else try to raise the "search" rate to match "active" */ + } else { + /* (2nd or later pass) + * If we've already tried to lower the rate, and are + * now trying to raise it, use the lower rate. */ + if (new_rate != IWL_RATE_INVALID) + break; + + /* Loop again with higher rate */ + else if (high != IWL_RATE_INVALID) { + start_hi = high; + rate = high; + + /* Higher rate not available, use the original */ + } else { + new_rate = rate; + break; + } + } + } + + return new_rate; +} + +/* + * Set up search table for MIMO2 + */ +static int rs_switch_to_mimo2(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) + == WLAN_HT_CAP_SM_PS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (priv->hw_params.tx_chains_num < 2) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); + + tbl->lq_type = LQ_MIMO2; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + tbl->max_search = IWL_MAX_SEARCH; + rate_mask = lq_sta->active_mimo2_rate; + + if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + rs_set_expected_tpt_table(lq_sta, tbl); + + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); + + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Set up search table for MIMO3 + */ +static int rs_switch_to_mimo3(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) + == WLAN_HT_CAP_SM_PS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (priv->hw_params.tx_chains_num < 3) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n"); + + tbl->lq_type = LQ_MIMO3; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; + rate_mask = lq_sta->active_mimo3_rate; + + if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + rs_set_expected_tpt_table(lq_sta, tbl); + + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n", + rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); + + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Set up search table for SISO + */ +static int rs_switch_to_siso(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + u8 is_green = lq_sta->is_green; + s32 rate; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); + + tbl->is_dup = lq_sta->is_dup; + tbl->lq_type = LQ_SISO; + tbl->action = 0; + tbl->max_search = IWL_MAX_SEARCH; + rate_mask = lq_sta->active_siso_rate; + + if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + if (is_green) + tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ + + rs_set_expected_tpt_table(lq_sta, tbl); + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Try to switch to new modulation mode from legacy + */ +static int rs_move_legacy_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + int index) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + int ret = 0; + u8 update_search_tbl_counter = 0; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) + tbl->action = IWL_LEGACY_SWITCH_SISO; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + valid_tx_ant = + first_antenna(priv->hw_params.valid_tx_ant); + if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && + tbl->action != IWL_LEGACY_SWITCH_SISO) + tbl->action = IWL_LEGACY_SWITCH_SISO; + break; + default: + IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + break; + } + + if (!iwl_ht_enabled(priv)) + /* stay in Legacy */ + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_LEGACY_SWITCH_SISO) + tbl->action = IWL_LEGACY_SWITCH_SISO; + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent) { + if (!iwl_ht_enabled(priv)) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) + tbl->action = IWL_LEGACY_SWITCH_SISO; + valid_tx_ant = + first_antenna(priv->hw_params.valid_tx_ant); + } + + start_action = tbl->action; + for (; ;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_LEGACY_SWITCH_ANTENNA1: + case IWL_LEGACY_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); + + if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + /* Don't change antenna if success has been great */ + if (window->success_ratio >= IWL_RS_GOOD_RATIO && + !priv->bt_full_concurrent && + priv->bt_traffic_load == + IWL_BT_COEX_TRAFFIC_LOAD_NONE) + break; + + /* Set up search table to try other antenna */ + memcpy(search_tbl, tbl, sz); + + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + rs_set_expected_tpt_table(lq_sta, search_tbl); + goto out; + } + break; + case IWL_LEGACY_SWITCH_SISO: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); + + /* Set up search table to try SISO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + + break; + case IWL_LEGACY_SWITCH_MIMO2_AB: + case IWL_LEGACY_SWITCH_MIMO2_AC: + case IWL_LEGACY_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); + + /* Set up search table to try MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + break; + + case IWL_LEGACY_SWITCH_MIMO3_ABC: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n"); + + /* Set up search table to try MIMO3 */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + search_tbl->ant_type = ANT_ABC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + break; + } + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + + } + search_tbl->lq_type = LQ_NONE; + return 0; + +out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + return 0; + +} + +/* + * Try to switch to new modulation mode from SISO + */ +static int rs_move_siso_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + u8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) + tbl->action = IWL_SISO_SWITCH_MIMO2_AB; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + valid_tx_ant = + first_antenna(priv->hw_params.valid_tx_ant); + if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + break; + default: + IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + break; + } + + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_SISO_SWITCH_ANTENNA2) { + /* stay in SISO */ + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + } + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent) { + valid_tx_ant = + first_antenna(priv->hw_params.valid_tx_ant); + if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + } + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_SISO_SWITCH_ANTENNA1: + case IWL_SISO_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); + if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO && + !priv->bt_full_concurrent && + priv->bt_traffic_load == + IWL_BT_COEX_TRAFFIC_LOAD_NONE) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IWL_SISO_SWITCH_MIMO2_AB: + case IWL_SISO_SWITCH_MIMO2_AC: + case IWL_SISO_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + break; + case IWL_SISO_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); + + memcpy(search_tbl, tbl, sz); + if (is_green) { + if (!tbl->is_SGI) + break; + else + IWL_ERR(priv, + "SGI was set in GF+SISO\n"); + } + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + case IWL_SISO_SWITCH_MIMO3_ABC: + IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + search_tbl->ant_type = ANT_ABC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + break; + } + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; + + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; +} + +/* + * Try to switch to new modulation mode from MIMO2 + */ +static int rs_move_mimo2_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + s8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || + tbl->action == IWL_MIMO2_SWITCH_SISO_C) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + break; + default: + IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + break; + } + + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO2_SWITCH_SISO_A || + tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + } + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent && + (tbl->action < IWL_MIMO2_SWITCH_SISO_A || + tbl->action > IWL_MIMO2_SWITCH_SISO_C)) + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_MIMO2_SWITCH_ANTENNA1: + case IWL_MIMO2_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); + + if (tx_chains_num <= 2) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IWL_MIMO2_SWITCH_SISO_A: + case IWL_MIMO2_SWITCH_SISO_B: + case IWL_MIMO2_SWITCH_SISO_C: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) + search_tbl->ant_type = ANT_A; + else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) + search_tbl->ant_type = ANT_B; + else + search_tbl->ant_type = ANT_C; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO2_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); + + /* Set up new search table for MIMO2 */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + + case IWL_MIMO2_SWITCH_MIMO3_ABC: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + search_tbl->ant_type = ANT_ABC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + } + tbl->action++; + if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) + tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) + tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; + +} + +/* + * Try to switch to new modulation mode from MIMO3 + */ +static int rs_move_mimo3_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + s8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + int ret; + u8 update_search_tbl_counter = 0; + + switch (priv->bt_traffic_load) { + case IWL_BT_COEX_TRAFFIC_LOAD_NONE: + /* nothing */ + break; + case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: + case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: + /* avoid antenna B and MIMO */ + if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + break; + case IWL_BT_COEX_TRAFFIC_LOAD_LOW: + /* avoid antenna B unless MIMO */ + if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || + tbl->action == IWL_MIMO3_SWITCH_SISO_C) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + break; + default: + IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + break; + } + + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO3_SWITCH_SISO_A || + tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + } + + /* configure as 1x1 if bt full concurrency */ + if (priv->bt_full_concurrent && + (tbl->action < IWL_MIMO3_SWITCH_SISO_A || + tbl->action > IWL_MIMO3_SWITCH_SISO_C)) + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_MIMO3_SWITCH_ANTENNA1: + case IWL_MIMO3_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n"); + + if (tx_chains_num <= 3) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) + goto out; + break; + case IWL_MIMO3_SWITCH_SISO_A: + case IWL_MIMO3_SWITCH_SISO_B: + case IWL_MIMO3_SWITCH_SISO_C: + IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) + search_tbl->ant_type = ANT_A; + else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) + search_tbl->ant_type = ANT_B; + else + search_tbl->ant_type = ANT_C; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO3_SWITCH_MIMO2_AB: + case IWL_MIMO3_SWITCH_MIMO2_AC: + case IWL_MIMO3_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n"); + + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) + break; + + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO3_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); + + /* Set up new search table for MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + } + tbl->action++; + if (tbl->action > IWL_MIMO3_SWITCH_GI) + tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_MIMO3_SWITCH_GI) + tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; + +} + +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) +{ + struct iwl_scale_tbl_info *tbl; + int i; + int active_tbl; + int flush_interval_passed = 0; + struct iwl_priv *priv; + + priv = lq_sta->drv; + active_tbl = lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + /* If we've been disallowing search, see if we should now allow it */ + if (lq_sta->stay_in_tbl) { + + /* Elapsed time using current modulation mode */ + if (lq_sta->flush_timer) + flush_interval_passed = + time_after(jiffies, + (unsigned long)(lq_sta->flush_timer + + IWL_RATE_SCALE_FLUSH_INTVL)); + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ + if (force_search || + (lq_sta->total_failed > lq_sta->max_failure_limit) || + (lq_sta->total_success > lq_sta->max_success_limit) || + ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) + && (flush_interval_passed))) { + IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n", + lq_sta->total_failed, + lq_sta->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = 0; + + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { + lq_sta->table_count++; + if (lq_sta->table_count >= + lq_sta->table_count_limit) { + lq_sta->table_count = 0; + + IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n"); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window( + &(tbl->win[i])); + } + } + + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ + if (!lq_sta->stay_in_tbl) { + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(tbl->win[i])); + } + } +} + +/* + * setup rate table in uCode + */ +static void rs_update_rate_tbl(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int index, u8 is_green) +{ + u32 rate; + + /* Update uCode's rate table. */ + rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); + rs_fill_link_cmd(priv, lq_sta, rate); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); +} + +/* + * Do rate scaling and search for new modulation mode. + */ +static void rs_rate_scale_perform(struct iwl_priv *priv, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int low = IWL_RATE_INVALID; + int high = IWL_RATE_INVALID; + int index; + int i; + struct iwl_rate_scale_data *window = NULL; + int current_tpt = IWL_INVALID_VALUE; + int low_tpt = IWL_INVALID_VALUE; + int high_tpt = IWL_INVALID_VALUE; + u32 fail_count; + s8 scale_action = 0; + u16 rate_mask; + u8 update_lq = 0; + struct iwl_scale_tbl_info *tbl, *tbl1; + u16 rate_scale_index_msk = 0; + u8 is_green = 0; + u8 active_tbl = 0; + u8 done_search = 0; + u16 high_low; + s32 sr; + u8 tid = IWL_MAX_TID_COUNT; + struct iwl_tid_data *tid_data; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->ctx; + + IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); + + /* Send management frames and NO_ACK data using lowest rate. */ + /* TODO: this could probably be improved.. */ + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; + + tid = rs_tl_add_packet(lq_sta, hdr); + if ((tid != IWL_MAX_TID_COUNT) && + (lq_sta->tx_agg_tid_en & (1 << tid))) { + tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; + if (tid_data->agg.state == IWL_AGG_OFF) + lq_sta->is_agg = 0; + else + lq_sta->is_agg = 1; + } else + lq_sta->is_agg = 0; + + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + if (is_legacy(tbl->lq_type)) + lq_sta->is_green = 0; + else + lq_sta->is_green = rs_use_green(sta); + is_green = lq_sta->is_green; + + /* current tx rate */ + index = lq_sta->last_txrate_idx; + + IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, + tbl->lq_type); + + /* rates available for this association, and for modulation mode */ + rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); + + IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); + + /* mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + /* supp_rates has no CCK bits in A mode */ + rate_scale_index_msk = (u16) (rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_scale_index_msk = (u16) (rate_mask & + lq_sta->supp_rates); + + } else + rate_scale_index_msk = rate_mask; + + if (!rate_scale_index_msk) + rate_scale_index_msk = rate_mask; + + if (!((1 << index) & rate_scale_index_msk)) { + IWL_ERR(priv, "Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid*/ + tbl->lq_type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + /* get "active" rate info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + rs_update_rate_tbl(priv, ctx, lq_sta, tbl, + index, is_green); + } + return; + } + + /* Get expected throughput table and history window for current rate */ + if (!tbl->expected_tpt) { + IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); + return; + } + + /* force user max rate if set by user */ + if ((lq_sta->max_rate_idx != -1) && + (lq_sta->max_rate_idx < index)) { + index = lq_sta->max_rate_idx; + update_lq = 1; + window = &(tbl->win[index]); + goto lq_update; + } + + window = &(tbl->win[index]); + + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ + fail_count = window->counter - window->success_counter; + if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { + IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " + "for index %d\n", + window->success_counter, window->counter, index); + + /* Can't calculate this yet; not enough history */ + window->average_tpt = IWL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta, false); + + goto out; + } + /* Else we have enough samples; calculate estimate of + * actual average throughput */ + if (window->average_tpt != ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128)) { + IWL_ERR(priv, "expected_tpt should have been calculated by now\n"); + window->average_tpt = ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128); + } + + /* If we are searching for better modulation mode, check success. */ + if (lq_sta->search_better_tbl && + (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ + if (window->average_tpt > lq_sta->last_tpt) { + + IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + if (!is_legacy(tbl->lq_type)) + lq_sta->enable_counter = 1; + + /* Swap tables; "search" becomes "active" */ + lq_sta->active_tbl = active_tbl; + current_tpt = window->average_tpt; + + /* Else poor success; go back to mode in "active" table */ + } else { + + IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + /* Nullify "search" table */ + tbl->lq_type = LQ_NONE; + + /* Revert to "active" table */ + active_tbl = lq_sta->active_tbl; + tbl = &(lq_sta->lq_info[active_tbl]); + + /* Revert to "active" rate and throughput info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + current_tpt = lq_sta->last_tpt; + + /* Need to set up a new rate table in uCode */ + update_lq = 1; + } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ + lq_sta->search_better_tbl = 0; + done_search = 1; /* Don't switch modes below! */ + goto lq_update; + } + + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ + high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, + tbl->lq_type); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* If user set max rate, dont allow higher than user constrain */ + if ((lq_sta->max_rate_idx != -1) && + (lq_sta->max_rate_idx < high)) + high = IWL_RATE_INVALID; + + sr = window->success_ratio; + + /* Collect measured throughputs for current and adjacent rates */ + current_tpt = window->average_tpt; + if (low != IWL_RATE_INVALID) + low_tpt = tbl->win[low].average_tpt; + if (high != IWL_RATE_INVALID) + high_tpt = tbl->win[high].average_tpt; + + scale_action = 0; + + /* Too many failures, decrease rate */ + if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { + IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); + scale_action = -1; + + /* No throughput measured yet for adjacent rates; try increase. */ + } else if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE)) { + + if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) + scale_action = 1; + else if (low != IWL_RATE_INVALID) + scale_action = 0; + } + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! */ + else if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) + scale_action = 0; + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. */ + else { + /* Higher adjacent rate's throughput is measured */ + if (high_tpt != IWL_INVALID_VALUE) { + /* Higher rate has better throughput */ + if (high_tpt > current_tpt && + sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } else { + scale_action = 0; + } + + /* Lower adjacent rate's throughput is measured */ + } else if (low_tpt != IWL_INVALID_VALUE) { + /* Lower rate has better throughput */ + if (low_tpt > current_tpt) { + IWL_DEBUG_RATE(priv, + "decrease rate because of low tpt\n"); + scale_action = -1; + } else if (sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ + if ((scale_action == -1) && (low != IWL_RATE_INVALID) && + ((sr > IWL_RATE_HIGH_TH) || + (current_tpt > (100 * tbl->expected_tpt[low])))) + scale_action = 0; + if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) + scale_action = -1; + if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) + scale_action = -1; + + if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + if (lq_sta->last_bt_traffic > priv->bt_traffic_load) { + /* + * don't set scale_action, don't want to scale up if + * the rate scale doesn't otherwise think that is a + * good idea. + */ + } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) { + scale_action = -1; + } + } + lq_sta->last_bt_traffic = priv->bt_traffic_load; + + if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { + /* search for a new modulation */ + rs_stay_in_table(lq_sta, true); + goto lq_update; + } + + switch (scale_action) { + case -1: + /* Decrease starting rate, update uCode's rate table */ + if (low != IWL_RATE_INVALID) { + update_lq = 1; + index = low; + } + + break; + case 1: + /* Increase starting rate, update uCode's rate table */ + if (high != IWL_RATE_INVALID) { + update_lq = 1; + index = high; + } + + break; + case 0: + /* No change */ + default: + break; + } + + IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " + "high %d type %d\n", + index, scale_action, low, high, tbl->lq_type); + +lq_update: + /* Replace uCode's rate table for the destination station. */ + if (update_lq) + rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); + + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta, false); + } + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { + /* Save current throughput to compare with "search" throughput*/ + lq_sta->last_tpt = current_tpt; + + /* Select a new "search" modulation mode to try. + * If one is found, set up the new "search" table. */ + if (is_legacy(tbl->lq_type)) + rs_move_legacy_other(priv, lq_sta, conf, sta, index); + else if (is_siso(tbl->lq_type)) + rs_move_siso_to_other(priv, lq_sta, conf, sta, index); + else if (is_mimo2(tbl->lq_type)) + rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index); + else + rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index); + + /* If new "search" mode was selected, set up in uCode table */ + if (lq_sta->search_better_tbl) { + /* Access the "search" table, clear its history. */ + tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(tbl->win[i])); + + /* Use new "search" start rate */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + + IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", + tbl->current_rate, index); + rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + } else + done_search = 1; + } + + if (done_search && !lq_sta->stay_in_tbl) { + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ + tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); + if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && + lq_sta->action_counter > tbl1->max_search) { + IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); + rs_set_stay_in_table(priv, 1, lq_sta); + } + + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ + if (lq_sta->enable_counter && + (lq_sta->action_counter >= tbl1->max_search) && + iwl_ht_enabled(priv)) { + if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && + (lq_sta->tx_agg_tid_en & (1 << tid)) && + (tid != IWL_MAX_TID_COUNT)) { + u8 sta_id = lq_sta->lq.sta_id; + tid_data = &priv->tid_data[sta_id][tid]; + if (tid_data->agg.state == IWL_AGG_OFF) { + IWL_DEBUG_RATE(priv, + "try to aggregate tid %d\n", + tid); + rs_tl_turn_on_agg(priv, tid, + lq_sta, sta); + } + } + rs_set_stay_in_table(priv, 0, lq_sta); + } + } + +out: + tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); + lq_sta->last_txrate_idx = index; +} + +/** + * rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ +static void rs_initialize_lq(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_scale_tbl_info *tbl; + int rate_idx; + int i; + u32 rate; + u8 use_green = rs_use_green(sta); + u8 active_tbl = 0; + u8 valid_tx_ant; + struct iwl_station_priv *sta_priv; + struct iwl_rxon_context *ctx; + + if (!sta || !lq_sta) + return; + + sta_priv = (void *)sta->drv_priv; + ctx = sta_priv->ctx; + + i = lq_sta->last_txrate_idx; + + valid_tx_ant = priv->hw_params.valid_tx_ant; + + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + if ((i < 0) || (i >= IWL_RATE_COUNT)) + i = 0; + + rate = iwl_rates[i].plcp; + tbl->ant_type = first_antenna(valid_tx_ant); + rate |= tbl->ant_type << RATE_MCS_ANT_POS; + + if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) + rate |= RATE_MCS_CCK_MSK; + + rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); + if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) + rs_toggle_antenna(valid_tx_ant, &rate, tbl); + + rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); + tbl->current_rate = rate; + rs_set_expected_tpt_table(lq_sta, tbl); + rs_fill_link_cmd(NULL, lq_sta, rate); + priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true); +} + +static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc) +{ + + struct sk_buff *skb = txrc->skb; + struct ieee80211_supported_band *sband = txrc->sband; + struct iwl_op_mode *op_mode __maybe_unused = + (struct iwl_op_mode *)priv_r; + struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_lq_sta *lq_sta = priv_sta; + int rate_idx; + + IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); + + /* Get max rate if user set max rate */ + if (lq_sta) { + lq_sta->max_rate_idx = txrc->max_rate_idx; + if ((sband->band == IEEE80211_BAND_5GHZ) && + (lq_sta->max_rate_idx != -1)) + lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; + if ((lq_sta->max_rate_idx < 0) || + (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) + lq_sta->max_rate_idx = -1; + } + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + priv_sta = NULL; + } + + /* Send management frames and NO_ACK data using lowest rate. */ + if (rate_control_send_low(sta, priv_sta, txrc)) + return; + + rate_idx = lq_sta->last_txrate_idx; + + if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { + rate_idx -= IWL_FIRST_OFDM_RATE; + /* 6M and 9M shared same MCS index */ + rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; + if (rs_extract_rate(lq_sta->last_rate_n_flags) >= + IWL_RATE_MIMO3_6M_PLCP) + rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); + else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= + IWL_RATE_MIMO2_6M_PLCP) + rate_idx = rate_idx + MCS_INDEX_PER_STREAM; + info->control.rates[0].flags = IEEE80211_TX_RC_MCS; + if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; + if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; + if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) + info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; + } else { + /* Check for invalid rates */ + if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || + ((sband->band == IEEE80211_BAND_5GHZ) && + (rate_idx < IWL_FIRST_OFDM_RATE))) + rate_idx = rate_lowest_index(sband, sta); + /* On valid 5 GHz rate, adjust index */ + else if (sband->band == IEEE80211_BAND_5GHZ) + rate_idx -= IWL_FIRST_OFDM_RATE; + info->control.rates[0].flags = 0; + } + info->control.rates[0].idx = rate_idx; + +} + +static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, + gfp_t gfp) +{ + struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; + struct iwl_op_mode *op_mode __maybe_unused = + (struct iwl_op_mode *)priv_rate; + struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_DEBUG_RATE(priv, "create station rate scale window\n"); + + return &sta_priv->lq_sta; +} + +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) +{ + int i, j; + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct iwl_station_priv *sta_priv; + struct iwl_lq_sta *lq_sta; + struct ieee80211_supported_band *sband; + unsigned long supp; /* must be unsigned long for for_each_set_bit */ + + sta_priv = (struct iwl_station_priv *) sta->drv_priv; + lq_sta = &sta_priv->lq_sta; + sband = hw->wiphy->bands[conf->channel->band]; + + + lq_sta->lq.sta_id = sta_id; + + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); + + lq_sta->flush_timer = 0; + lq_sta->supp_rates = sta->supp_rates[sband->band]; + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); + + IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", + sta_id); + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + lq_sta->is_dup = 0; + lq_sta->max_rate_idx = -1; + lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; + lq_sta->is_green = rs_use_green(sta); + lq_sta->band = sband->band; + /* + * active legacy rates as per supported rates bitmap + */ + supp = sta->supp_rates[sband->band]; + lq_sta->active_legacy_rate = 0; + for_each_set_bit(i, &supp, BITS_PER_LONG) + lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); + + /* + * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), + * supp_rates[] does not; shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; + lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; + lq_sta->active_mimo3_rate &= ~((u16)0x2); + lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; + + IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", + lq_sta->active_siso_rate, + lq_sta->active_mimo2_rate, + lq_sta->active_mimo3_rate); + + /* These values will be overridden later */ + lq_sta->lq.general_params.single_stream_ant_msk = + first_antenna(priv->hw_params.valid_tx_ant); + lq_sta->lq.general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant & + ~first_antenna(priv->hw_params.valid_tx_ant); + if (!lq_sta->lq.general_params.dual_stream_ant_msk) { + lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; + } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { + lq_sta->lq.general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant; + } + + /* as default allow aggregation for all tids */ + lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; + lq_sta->drv = priv; + + /* Set last_txrate_idx to lowest rate */ + lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); + if (sband->band == IEEE80211_BAND_5GHZ) + lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; + lq_sta->is_agg = 0; +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + priv->tm_fixed_rate = 0; +#endif +#ifdef CONFIG_MAC80211_DEBUGFS + lq_sta->dbg_fixed_rate = 0; +#endif + + rs_initialize_lq(priv, sta, lq_sta); +} + +static void rs_fill_link_cmd(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 new_rate) +{ + struct iwl_scale_tbl_info tbl_type; + int index = 0; + int rate_idx; + int repeat_rate = 0; + u8 ant_toggle_cnt = 0; + u8 use_ht_possible = 1; + u8 valid_tx_ant = 0; + struct iwl_station_priv *sta_priv = + container_of(lq_sta, struct iwl_station_priv, lq_sta); + struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; + + /* Override starting rate (index 0) if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Interpret new_rate (rate_n_flags) */ + rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, + &tbl_type, &rate_idx); + + if (priv && priv->bt_full_concurrent) { + /* 1x1 only */ + tbl_type.ant_type = + first_antenna(priv->hw_params.valid_tx_ant); + } + + /* How many times should we repeat the initial rate? */ + if (is_legacy(tbl_type.lq_type)) { + ant_toggle_cnt = 1; + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = min(IWL_HT_NUMBER_TRY, + LINK_QUAL_AGG_DISABLE_START_DEF - 1); + } + + lq_cmd->general_params.mimo_delimiter = + is_mimo(tbl_type.lq_type) ? 1 : 0; + + /* Fill 1st table entry (index 0) */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + if (num_of_ant(tbl_type.ant_type) == 1) { + lq_cmd->general_params.single_stream_ant_msk = + tbl_type.ant_type; + } else if (num_of_ant(tbl_type.ant_type) == 2) { + lq_cmd->general_params.dual_stream_ant_msk = + tbl_type.ant_type; + } /* otherwise we don't modify the existing value */ + + index++; + repeat_rate--; + if (priv) { + if (priv->bt_full_concurrent) + valid_tx_ant = ANT_A; + else + valid_tx_ant = priv->hw_params.valid_tx_ant; + } + + /* Fill rest of rate table */ + while (index < LINK_QUAL_MAX_RETRY_NUM) { + /* Repeat initial/next rate. + * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. + * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ + while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + } + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = + cpu_to_le32(new_rate); + repeat_rate--; + index++; + } + + rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, + &rate_idx); + + if (priv && priv->bt_full_concurrent) { + /* 1x1 only */ + tbl_type.ant_type = + first_antenna(priv->hw_params.valid_tx_ant); + } + + /* Indicate to uCode which entries might be MIMO. + * If initial rate was MIMO, this will finally end up + * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ + if (is_mimo(tbl_type.lq_type)) + lq_cmd->general_params.mimo_delimiter = index; + + /* Get next rate */ + new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, + use_ht_possible); + + /* How many times should we repeat the next rate? */ + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = IWL_HT_NUMBER_TRY; + } + + /* Don't allow HT rates after next pass. + * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ + use_ht_possible = 0; + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + index++; + repeat_rate--; + } + + lq_cmd->agg_params.agg_frame_cnt_limit = + sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF; + lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + + lq_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + /* + * overwrite if needed, pass aggregation time limit + * to uCode in uSec + */ + if (priv && priv->cfg->bt_params && + priv->cfg->bt_params->agg_time_limit && + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) + lq_cmd->agg_params.agg_time_limit = + cpu_to_le16(priv->cfg->bt_params->agg_time_limit); +} + +static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + return hw->priv; +} +/* rate scale requires free function to be implemented */ +static void rs_free(void *priv_rate) +{ + return; +} + +static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, + void *priv_sta) +{ + struct iwl_op_mode *op_mode __maybe_unused = priv_r; + struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); + + IWL_DEBUG_RATE(priv, "enter\n"); + IWL_DEBUG_RATE(priv, "leave\n"); +} + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{ + struct iwl_priv *priv; + u8 valid_tx_ant; + u8 ant_sel_tx; + + priv = lq_sta->drv; + valid_tx_ant = priv->hw_params.valid_tx_ant; + if (lq_sta->dbg_fixed_rate) { + ant_sel_tx = + ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) + >> RATE_MCS_ANT_POS); + if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { + *rate_n_flags = lq_sta->dbg_fixed_rate; + IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); + } else { + lq_sta->dbg_fixed_rate = 0; + IWL_ERR(priv, + "Invalid antenna selection 0x%X, Valid is 0x%X\n", + ant_sel_tx, valid_tx_ant); + IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); + } + } else { + IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); + } +} + +static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + char buf[64]; + size_t buf_size; + u32 parsed_rate; + + + priv = lq_sta->drv; + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + lq_sta->dbg_fixed_rate = parsed_rate; + else + lq_sta->dbg_fixed_rate = 0; + + rs_program_fix_rate(priv, lq_sta); + + return count; +} + +static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i = 0; + int index = 0; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + + priv = lq_sta->drv; + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", + lq_sta->total_failed, lq_sta->total_success, + lq_sta->active_legacy_rate); + desc += sprintf(buff+desc, "fixed rate 0x%X\n", + lq_sta->dbg_fixed_rate); + desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", + (priv->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", + (priv->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", + (priv->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); + desc += sprintf(buff+desc, "lq type %s\n", + (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); + if (is_Ht(tbl->lq_type)) { + desc += sprintf(buff+desc, " %s", + (is_siso(tbl->lq_type)) ? "SISO" : + ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); + desc += sprintf(buff+desc, " %s", + (tbl->is_ht40) ? "40MHz" : "20MHz"); + desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "", + (lq_sta->is_green) ? "GF enabled" : "", + (lq_sta->is_agg) ? "AGG on" : ""); + } + desc += sprintf(buff+desc, "last tx rate=0x%X\n", + lq_sta->last_rate_n_flags); + desc += sprintf(buff+desc, "general:" + "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + lq_sta->lq.general_params.flags, + lq_sta->lq.general_params.mimo_delimiter, + lq_sta->lq.general_params.single_stream_ant_msk, + lq_sta->lq.general_params.dual_stream_ant_msk); + + desc += sprintf(buff+desc, "agg:" + "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), + lq_sta->lq.agg_params.agg_dis_start_th, + lq_sta->lq.agg_params.agg_frame_cnt_limit); + + desc += sprintf(buff+desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + lq_sta->lq.general_params.start_rate_index[0], + lq_sta->lq.general_params.start_rate_index[1], + lq_sta->lq.general_params.start_rate_index[2], + lq_sta->lq.general_params.start_rate_index[3]); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + index = iwl_hwrate_to_plcp_idx( + le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); + if (is_legacy(tbl->lq_type)) { + desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", + i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), + iwl_rate_mcs[index].mbps); + } else { + desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n", + i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), + iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = rs_sta_dbgfs_scale_table_write, + .read = rs_sta_dbgfs_scale_table_read, + .open = simple_open, + .llseek = default_llseek, +}; +static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i, j; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + for (i = 0; i < LQ_SIZE; i++) { + desc += sprintf(buff+desc, + "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" + "rate=0x%X\n", + lq_sta->active_tbl == i ? "*" : "x", + lq_sta->lq_info[i].lq_type, + lq_sta->lq_info[i].is_SGI, + lq_sta->lq_info[i].is_ht40, + lq_sta->lq_info[i].is_dup, + lq_sta->is_green, + lq_sta->lq_info[i].current_rate); + for (j = 0; j < IWL_RATE_COUNT; j++) { + desc += sprintf(buff+desc, + "counter=%d success=%d %%=%d\n", + lq_sta->lq_info[i].win[j].counter, + lq_sta->lq_info[i].win[j].success_counter, + lq_sta->lq_info[i].win[j].success_ratio); + } + } + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = rs_sta_dbgfs_stats_table_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + char buff[120]; + int desc = 0; + + if (is_Ht(tbl->lq_type)) + desc += sprintf(buff+desc, + "Bit Rate= %d Mb/s\n", + tbl->expected_tpt[lq_sta->last_txrate_idx]); + else + desc += sprintf(buff+desc, + "Bit Rate= %d Mb/s\n", + iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); + + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { + .read = rs_sta_dbgfs_rate_scale_data_read, + .open = simple_open, + .llseek = default_llseek, +}; + +static void rs_add_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + lq_sta->rs_sta_dbgfs_scale_table_file = + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + lq_sta->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_stats_table_ops); + lq_sta->rs_sta_dbgfs_rate_scale_data_file = + debugfs_create_file("rate_scale_data", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); + lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, + &lq_sta->tx_agg_tid_en); + +} + +static void rs_remove_debugfs(void *priv, void *priv_sta) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); +} +#endif + +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ +} +static struct rate_control_ops rs_ops = { + .module = NULL, + .name = RS_NAME, + .tx_status = rs_tx_status, + .get_rate = rs_get_rate, + .rate_init = rs_rate_init_stub, + .alloc = rs_alloc, + .free = rs_free, + .alloc_sta = rs_alloc_sta, + .free_sta = rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rs_add_debugfs, + .remove_sta_debugfs = rs_remove_debugfs, +#endif +}; + +int iwlagn_rate_control_register(void) +{ + return ieee80211_rate_control_register(&rs_ops); +} + +void iwlagn_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_ops); +} + diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h new file mode 100644 index 000000000000..ad3aea8f626a --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/rs.h @@ -0,0 +1,433 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_agn_rs_h__ +#define __iwl_agn_rs_h__ + +#include + +#include "iwl-config.h" + +#include "commands.h" + +struct iwl_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ +}; + +/* + * These serve as indexes into + * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; + */ +enum { + IWL_RATE_1M_INDEX = 0, + IWL_RATE_2M_INDEX, + IWL_RATE_5M_INDEX, + IWL_RATE_11M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, + IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, + IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, + IWL_RATE_54M_INDEX, + IWL_RATE_60M_INDEX, + IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ + IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */ + IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, + IWL_RATE_INVALID = IWL_RATE_COUNT, +}; + +enum { + IWL_RATE_6M_INDEX_TABLE = 0, + IWL_RATE_9M_INDEX_TABLE, + IWL_RATE_12M_INDEX_TABLE, + IWL_RATE_18M_INDEX_TABLE, + IWL_RATE_24M_INDEX_TABLE, + IWL_RATE_36M_INDEX_TABLE, + IWL_RATE_48M_INDEX_TABLE, + IWL_RATE_54M_INDEX_TABLE, + IWL_RATE_1M_INDEX_TABLE, + IWL_RATE_2M_INDEX_TABLE, + IWL_RATE_5M_INDEX_TABLE, + IWL_RATE_11M_INDEX_TABLE, + IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, +}; + +enum { + IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, + IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, + IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, +}; + +/* #define vs. enum to keep from defaulting to 'large integer' */ +#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) +#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) +#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) +#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) +#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) +#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) +#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) +#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) +#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) +#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) +#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) +#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) +#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) + +/* uCode API values for legacy bit rates, both OFDM and CCK */ +enum { + IWL_RATE_6M_PLCP = 13, + IWL_RATE_9M_PLCP = 15, + IWL_RATE_12M_PLCP = 5, + IWL_RATE_18M_PLCP = 7, + IWL_RATE_24M_PLCP = 9, + IWL_RATE_36M_PLCP = 11, + IWL_RATE_48M_PLCP = 1, + IWL_RATE_54M_PLCP = 3, + IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ + IWL_RATE_1M_PLCP = 10, + IWL_RATE_2M_PLCP = 20, + IWL_RATE_5M_PLCP = 55, + IWL_RATE_11M_PLCP = 110, + /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ + /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ +}; + +/* uCode API values for OFDM high-throughput (HT) bit rates */ +enum { + IWL_RATE_SISO_6M_PLCP = 0, + IWL_RATE_SISO_12M_PLCP = 1, + IWL_RATE_SISO_18M_PLCP = 2, + IWL_RATE_SISO_24M_PLCP = 3, + IWL_RATE_SISO_36M_PLCP = 4, + IWL_RATE_SISO_48M_PLCP = 5, + IWL_RATE_SISO_54M_PLCP = 6, + IWL_RATE_SISO_60M_PLCP = 7, + IWL_RATE_MIMO2_6M_PLCP = 0x8, + IWL_RATE_MIMO2_12M_PLCP = 0x9, + IWL_RATE_MIMO2_18M_PLCP = 0xa, + IWL_RATE_MIMO2_24M_PLCP = 0xb, + IWL_RATE_MIMO2_36M_PLCP = 0xc, + IWL_RATE_MIMO2_48M_PLCP = 0xd, + IWL_RATE_MIMO2_54M_PLCP = 0xe, + IWL_RATE_MIMO2_60M_PLCP = 0xf, + IWL_RATE_MIMO3_6M_PLCP = 0x10, + IWL_RATE_MIMO3_12M_PLCP = 0x11, + IWL_RATE_MIMO3_18M_PLCP = 0x12, + IWL_RATE_MIMO3_24M_PLCP = 0x13, + IWL_RATE_MIMO3_36M_PLCP = 0x14, + IWL_RATE_MIMO3_48M_PLCP = 0x15, + IWL_RATE_MIMO3_54M_PLCP = 0x16, + IWL_RATE_MIMO3_60M_PLCP = 0x17, + IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, +}; + +/* MAC header values for bit rates */ +enum { + IWL_RATE_6M_IEEE = 12, + IWL_RATE_9M_IEEE = 18, + IWL_RATE_12M_IEEE = 24, + IWL_RATE_18M_IEEE = 36, + IWL_RATE_24M_IEEE = 48, + IWL_RATE_36M_IEEE = 72, + IWL_RATE_48M_IEEE = 96, + IWL_RATE_54M_IEEE = 108, + IWL_RATE_60M_IEEE = 120, + IWL_RATE_1M_IEEE = 2, + IWL_RATE_2M_IEEE = 4, + IWL_RATE_5M_IEEE = 11, + IWL_RATE_11M_IEEE = 22, +}; + +#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) + +#define IWL_INVALID_VALUE -1 + +#define IWL_MIN_RSSI_VAL -100 +#define IWL_MAX_RSSI_VAL 0 + +/* These values specify how many Tx frame attempts before + * searching for a new modulation mode */ +#define IWL_LEGACY_FAILURE_LIMIT 160 +#define IWL_LEGACY_SUCCESS_LIMIT 480 +#define IWL_LEGACY_TABLE_COUNT 160 + +#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 +#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_NONE_LEGACY_TABLE_COUNT 1500 + +/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ +#define IWL_RS_GOOD_RATIO 12800 /* 100% */ +#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ +#define IWL_RATE_HIGH_TH 10880 /* 85% */ +#define IWL_RATE_INCREASE_TH 6400 /* 50% */ +#define IWL_RATE_DECREASE_TH 1920 /* 15% */ + +/* possible actions when in legacy mode */ +#define IWL_LEGACY_SWITCH_ANTENNA1 0 +#define IWL_LEGACY_SWITCH_ANTENNA2 1 +#define IWL_LEGACY_SWITCH_SISO 2 +#define IWL_LEGACY_SWITCH_MIMO2_AB 3 +#define IWL_LEGACY_SWITCH_MIMO2_AC 4 +#define IWL_LEGACY_SWITCH_MIMO2_BC 5 +#define IWL_LEGACY_SWITCH_MIMO3_ABC 6 + +/* possible actions when in siso mode */ +#define IWL_SISO_SWITCH_ANTENNA1 0 +#define IWL_SISO_SWITCH_ANTENNA2 1 +#define IWL_SISO_SWITCH_MIMO2_AB 2 +#define IWL_SISO_SWITCH_MIMO2_AC 3 +#define IWL_SISO_SWITCH_MIMO2_BC 4 +#define IWL_SISO_SWITCH_GI 5 +#define IWL_SISO_SWITCH_MIMO3_ABC 6 + + +/* possible actions when in mimo mode */ +#define IWL_MIMO2_SWITCH_ANTENNA1 0 +#define IWL_MIMO2_SWITCH_ANTENNA2 1 +#define IWL_MIMO2_SWITCH_SISO_A 2 +#define IWL_MIMO2_SWITCH_SISO_B 3 +#define IWL_MIMO2_SWITCH_SISO_C 4 +#define IWL_MIMO2_SWITCH_GI 5 +#define IWL_MIMO2_SWITCH_MIMO3_ABC 6 + + +/* possible actions when in mimo3 mode */ +#define IWL_MIMO3_SWITCH_ANTENNA1 0 +#define IWL_MIMO3_SWITCH_ANTENNA2 1 +#define IWL_MIMO3_SWITCH_SISO_A 2 +#define IWL_MIMO3_SWITCH_SISO_B 3 +#define IWL_MIMO3_SWITCH_SISO_C 4 +#define IWL_MIMO3_SWITCH_MIMO2_AB 5 +#define IWL_MIMO3_SWITCH_MIMO2_AC 6 +#define IWL_MIMO3_SWITCH_MIMO2_BC 7 +#define IWL_MIMO3_SWITCH_GI 8 + + +#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI +#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC + +/*FIXME:RS:add possible actions for MIMO3*/ + +#define IWL_ACTION_LIMIT 3 /* # possible actions */ + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +/* load per tid defines for A-MPDU activation */ +#define IWL_AGG_TPT_THREHOLD 0 +#define IWL_AGG_LOAD_THRESHOLD 10 +#define IWL_AGG_ALL_TID 0xff +#define TID_QUEUE_CELL_SPACING 50 /*mS */ +#define TID_QUEUE_MAX_SIZE 20 +#define TID_ROUND_VALUE 5 /* mS */ + +#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) +#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) + +extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; + +enum iwl_table_type { + LQ_NONE, + LQ_G, /* legacy types */ + LQ_A, + LQ_SISO, /* high-throughput types */ + LQ_MIMO2, + LQ_MIMO3, + LQ_MAX, +}; + +#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) +#define is_siso(tbl) ((tbl) == LQ_SISO) +#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) +#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) +#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) +#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) +#define is_a_band(tbl) ((tbl) == LQ_A) +#define is_g_and(tbl) ((tbl) == LQ_G) + +#define IWL_MAX_MCS_DISPLAY_SIZE 12 + +struct iwl_rate_mcs_info { + char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; + char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; +}; + +/** + * struct iwl_rate_scale_data -- tx success history for one rate + */ +struct iwl_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ + unsigned long stamp; +}; + +/** + * struct iwl_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_lq_sta, + * one for "active", and one for "search". + */ +struct iwl_scale_tbl_info { + enum iwl_table_type lq_type; + u8 ant_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_ht40; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ + u8 max_search; /* maximun number of tables we can search */ + s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + u32 current_rate; /* rate_n_flags, uCode API format */ + struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ +}; + +struct iwl_traffic_load { + unsigned long time_stamp; /* age of the oldest statistics */ + u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time + * slice */ + u32 total; /* total num of packets during the + * last TID_MAX_TIME_DIFF */ + u8 queue_count; /* number of queues that has + * been used since the last cleanup */ + u8 head; /* start of the circular buffer */ +}; + +/** + * struct iwl_lq_sta -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl_lq_sta { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u64 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ + u8 is_green; + u8 is_dup; + enum ieee80211_band band; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + u32 supp_rates; + u16 active_legacy_rate; + u16 active_siso_rate; + u16 active_mimo2_rate; + u16 active_mimo3_rate; + s8 max_rate_idx; /* Max rate set by user */ + u8 missed_rate_counter; + + struct iwl_link_quality_cmd lq; + struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; + u8 tx_agg_tid_en; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_rate_scale_data_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + u32 dbg_fixed_rate; +#endif + struct iwl_priv *drv; + + /* used to be in sta_info */ + int last_txrate_idx; + /* last tx rate_n_flags */ + u32 last_rate_n_flags; + /* packets destined for this STA are aggregated */ + u8 is_agg; + /* BT traffic this sta was last updated in */ + u8 last_bt_traffic; +}; + +static inline u8 num_of_ant(u8 mask) +{ + return !!((mask) & ANT_A) + + !!((mask) & ANT_B) + + !!((mask) & ANT_C); +} + +static inline u8 first_antenna(u8 mask) +{ + if (mask & ANT_A) + return ANT_A; + if (mask & ANT_B) + return ANT_B; + return ANT_C; +} + + +/* Initialize station's rate scaling information after adding station */ +extern void iwl_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); + +/** + * iwl_rate_control_register - Register the rate control algorithm callbacks + * + * Since the rate control algorithm is hardware specific, there is no need + * or reason to place it as a stand alone module. The driver can call + * iwl_rate_control_register in order to register the rate control callbacks + * with the mac80211 subsystem. This should be performed prior to calling + * ieee80211_register_hw + * + */ +extern int iwlagn_rate_control_register(void); + +/** + * iwl_rate_control_unregister - Unregister the rate control callbacks + * + * This should be called after calling ieee80211_unregister_hw, but before + * the driver is unloaded. + */ +extern void iwlagn_rate_control_unregister(void); + +#endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c new file mode 100644 index 000000000000..d61980248408 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -0,0 +1,1167 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portionhelp of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "eeprom.h" +#include "dev.h" +#include "calib.h" +#include "agn.h" + +#define IWL_CMD_ENTRY(x) [x] = #x + +const char *iwl_dvm_cmd_strings[REPLY_MAX] = { + IWL_CMD_ENTRY(REPLY_ALIVE), + IWL_CMD_ENTRY(REPLY_ERROR), + IWL_CMD_ENTRY(REPLY_ECHO), + IWL_CMD_ENTRY(REPLY_RXON), + IWL_CMD_ENTRY(REPLY_RXON_ASSOC), + IWL_CMD_ENTRY(REPLY_QOS_PARAM), + IWL_CMD_ENTRY(REPLY_RXON_TIMING), + IWL_CMD_ENTRY(REPLY_ADD_STA), + IWL_CMD_ENTRY(REPLY_REMOVE_STA), + IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA), + IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH), + IWL_CMD_ENTRY(REPLY_WEPKEY), + IWL_CMD_ENTRY(REPLY_TX), + IWL_CMD_ENTRY(REPLY_LEDS_CMD), + IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD), + IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD), + IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION), + IWL_CMD_ENTRY(COEX_EVENT_CMD), + IWL_CMD_ENTRY(REPLY_QUIET_CMD), + IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH), + IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD), + IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION), + IWL_CMD_ENTRY(POWER_TABLE_CMD), + IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION), + IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC), + IWL_CMD_ENTRY(REPLY_SCAN_CMD), + IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD), + IWL_CMD_ENTRY(SCAN_START_NOTIFICATION), + IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION), + IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION), + IWL_CMD_ENTRY(BEACON_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_TX_BEACON), + IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION), + IWL_CMD_ENTRY(QUIET_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD), + IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_BT_CONFIG), + IWL_CMD_ENTRY(REPLY_STATISTICS_CMD), + IWL_CMD_ENTRY(STATISTICS_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD), + IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION), + IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD), + IWL_CMD_ENTRY(SENSITIVITY_CMD), + IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), + IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), + IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), + IWL_CMD_ENTRY(REPLY_RX), + IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), + IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), + IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), + IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD), + IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION), + IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD), + IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF), + IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE), + IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV), + IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS), + IWL_CMD_ENTRY(REPLY_WIPAN_RXON), + IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING), + IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC), + IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM), + IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY), + IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH), + IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION), + IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE), + IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS), + IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER), + IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS), + IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS), + IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL), + IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS), + IWL_CMD_ENTRY(REPLY_D3_CONFIG), +}; +#undef IWL_CMD_ENTRY + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ + +static int iwlagn_rx_reply_error(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_error_resp *err_resp = (void *)pkt->data; + + IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) " + "seq 0x%04X ser 0x%08X\n", + le32_to_cpu(err_resp->error_type), + err_resp->cmd_id, + le16_to_cpu(err_resp->bad_cmd_seq_num), + le32_to_cpu(err_resp->error_info)); + return 0; +} + +static int iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_csa_notification *csa = (void *)pkt->data; + /* + * MULTI-FIXME + * See iwlagn_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_rxon_cmd *rxon = (void *)&ctx->active; + + if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) + return 0; + + if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) { + rxon->channel = csa->channel; + ctx->staging.channel = csa->channel; + IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", + le16_to_cpu(csa->channel)); + iwl_chswitch_done(priv, true); + } else { + IWL_ERR(priv, "CSA notif (fail) : channel %d\n", + le16_to_cpu(csa->channel)); + iwl_chswitch_done(priv, false); + } + return 0; +} + + +static int iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_spectrum_notification *report = (void *)pkt->data; + + if (!report->state) { + IWL_DEBUG_11H(priv, + "Spectrum Measure Notification: Start\n"); + return 0; + } + + memcpy(&priv->measure_report, report, sizeof(*report)); + priv->measurement_status |= MEASUREMENT_READY; + return 0; +} + +static int iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_sleep_notification *sleep = (void *)pkt->data; + IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", + sleep->pm_sleep_mode, sleep->pm_wakeup_src); +#endif + return 0; +} + +static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 __maybe_unused len = + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " + "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len); + iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len); + return 0; +} + +static int iwlagn_rx_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwlagn_beacon_notif *beacon = (void *)pkt->data; +#ifdef CONFIG_IWLWIFI_DEBUG + u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status); + u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + + IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d " + "tsf:0x%.8x%.8x rate:%d\n", + status & TX_STATUS_MSK, + beacon->beacon_notify_hdr.failure_frame, + le32_to_cpu(beacon->ibss_mgr_status), + le32_to_cpu(beacon->high_tsf), + le32_to_cpu(beacon->low_tsf), rate); +#endif + + priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); + + return 0; +} + +/** + * iwl_good_plcp_health - checks for plcp error. + * + * When the plcp error is exceeding the thresholds, reset the radio + * to improve the throughput. + */ +static bool iwlagn_good_plcp_health(struct iwl_priv *priv, + struct statistics_rx_phy *cur_ofdm, + struct statistics_rx_ht_phy *cur_ofdm_ht, + unsigned int msecs) +{ + int delta; + int threshold = priv->plcp_delta_threshold; + + if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { + IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); + return true; + } + + delta = le32_to_cpu(cur_ofdm->plcp_err) - + le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) + + le32_to_cpu(cur_ofdm_ht->plcp_err) - + le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err); + + /* Can be negative if firmware reset statistics */ + if (delta <= 0) + return true; + + if ((delta * 100 / msecs) > threshold) { + IWL_DEBUG_RADIO(priv, + "plcp health threshold %u delta %d msecs %u\n", + threshold, delta, msecs); + return false; + } + + return true; +} + +int iwl_force_rf_reset(struct iwl_priv *priv, bool external) +{ + struct iwl_rf_reset *rf_reset; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return -EAGAIN; + + if (!iwl_is_any_associated(priv)) { + IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); + return -ENOLINK; + } + + rf_reset = &priv->rf_reset; + rf_reset->reset_request_count++; + if (!external && rf_reset->last_reset_jiffies && + time_after(rf_reset->last_reset_jiffies + + IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) { + IWL_DEBUG_INFO(priv, "RF reset rejected\n"); + rf_reset->reset_reject_count++; + return -EAGAIN; + } + rf_reset->reset_success_count++; + rf_reset->last_reset_jiffies = jiffies; + + /* + * There is no easy and better way to force reset the radio, + * the only known method is switching channel which will force to + * reset and tune the radio. + * Use internal short scan (single channel) operation to should + * achieve this objective. + * Driver should reset the radio when number of consecutive missed + * beacon, or any other uCode error condition detected. + */ + IWL_DEBUG_INFO(priv, "perform radio reset.\n"); + iwl_internal_short_hw_scan(priv); + return 0; +} + + +static void iwlagn_recover_from_statistics(struct iwl_priv *priv, + struct statistics_rx_phy *cur_ofdm, + struct statistics_rx_ht_phy *cur_ofdm_ht, + struct statistics_tx *tx, + unsigned long stamp) +{ + unsigned int msecs; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies); + + /* Only gather statistics and update time stamp when not associated */ + if (!iwl_is_any_associated(priv)) + return; + + /* Do not check/recover when do not have enough statistics data */ + if (msecs < 99) + return; + + if (iwlwifi_mod_params.plcp_check && + !iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) + iwl_force_rf_reset(priv, false); +} + +/* Calculate noise level, based on measurements during network silence just + * before arriving beacon. This measurement can be done only if we know + * exactly when to expect beacons, therefore only when we're associated. */ +static void iwlagn_rx_calc_noise(struct iwl_priv *priv) +{ + struct statistics_rx_non_phy *rx_info; + int num_active_rx = 0; + int total_silence = 0; + int bcn_silence_a, bcn_silence_b, bcn_silence_c; + int last_rx_noise; + + rx_info = &priv->statistics.rx_non_phy; + + bcn_silence_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; + bcn_silence_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; + bcn_silence_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; + + if (bcn_silence_a) { + total_silence += bcn_silence_a; + num_active_rx++; + } + if (bcn_silence_b) { + total_silence += bcn_silence_b; + num_active_rx++; + } + if (bcn_silence_c) { + total_silence += bcn_silence_c; + num_active_rx++; + } + + /* Average among active antennas */ + if (num_active_rx) + last_rx_noise = (total_silence / num_active_rx) - 107; + else + last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; + + IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", + bcn_silence_a, bcn_silence_b, bcn_silence_c, + last_rx_noise); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* + * based on the assumption of all statistics counter are in DWORD + * FIXME: This function is for debugging, do not deal with + * the case of counters roll-over. + */ +static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta, + __le32 *max_delta, __le32 *accum, int size) +{ + int i; + + for (i = 0; + i < size / sizeof(__le32); + i++, prev++, cur++, delta++, max_delta++, accum++) { + if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) { + *delta = cpu_to_le32( + le32_to_cpu(*cur) - le32_to_cpu(*prev)); + le32_add_cpu(accum, le32_to_cpu(*delta)); + if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta)) + *max_delta = *delta; + } + } +} + +static void +iwlagn_accumulative_statistics(struct iwl_priv *priv, + struct statistics_general_common *common, + struct statistics_rx_non_phy *rx_non_phy, + struct statistics_rx_phy *rx_ofdm, + struct statistics_rx_ht_phy *rx_ofdm_ht, + struct statistics_rx_phy *rx_cck, + struct statistics_tx *tx, + struct statistics_bt_activity *bt_activity) +{ +#define ACCUM(_name) \ + accum_stats((__le32 *)&priv->statistics._name, \ + (__le32 *)_name, \ + (__le32 *)&priv->delta_stats._name, \ + (__le32 *)&priv->max_delta_stats._name, \ + (__le32 *)&priv->accum_stats._name, \ + sizeof(*_name)); + + ACCUM(common); + ACCUM(rx_non_phy); + ACCUM(rx_ofdm); + ACCUM(rx_ofdm_ht); + ACCUM(rx_cck); + ACCUM(tx); + if (bt_activity) + ACCUM(bt_activity); +#undef ACCUM +} +#else +static inline void +iwlagn_accumulative_statistics(struct iwl_priv *priv, + struct statistics_general_common *common, + struct statistics_rx_non_phy *rx_non_phy, + struct statistics_rx_phy *rx_ofdm, + struct statistics_rx_ht_phy *rx_ofdm_ht, + struct statistics_rx_phy *rx_cck, + struct statistics_tx *tx, + struct statistics_bt_activity *bt_activity) +{ +} +#endif + +static int iwlagn_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + unsigned long stamp = jiffies; + const int reg_recalib_period = 60; + int change; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + __le32 *flag; + struct statistics_general_common *common; + struct statistics_rx_non_phy *rx_non_phy; + struct statistics_rx_phy *rx_ofdm; + struct statistics_rx_ht_phy *rx_ofdm_ht; + struct statistics_rx_phy *rx_cck; + struct statistics_tx *tx; + struct statistics_bt_activity *bt_activity; + + len -= sizeof(struct iwl_cmd_header); /* skip header */ + + IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", + len); + + spin_lock(&priv->statistics.lock); + + if (len == sizeof(struct iwl_bt_notif_statistics)) { + struct iwl_bt_notif_statistics *stats; + stats = (void *)&pkt->data; + flag = &stats->flag; + common = &stats->general.common; + rx_non_phy = &stats->rx.general.common; + rx_ofdm = &stats->rx.ofdm; + rx_ofdm_ht = &stats->rx.ofdm_ht; + rx_cck = &stats->rx.cck; + tx = &stats->tx; + bt_activity = &stats->general.activity; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* handle this exception directly */ + priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills; + le32_add_cpu(&priv->statistics.accum_num_bt_kills, + le32_to_cpu(stats->rx.general.num_bt_kills)); +#endif + } else if (len == sizeof(struct iwl_notif_statistics)) { + struct iwl_notif_statistics *stats; + stats = (void *)&pkt->data; + flag = &stats->flag; + common = &stats->general.common; + rx_non_phy = &stats->rx.general; + rx_ofdm = &stats->rx.ofdm; + rx_ofdm_ht = &stats->rx.ofdm_ht; + rx_cck = &stats->rx.cck; + tx = &stats->tx; + bt_activity = NULL; + } else { + WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n", + len, sizeof(struct iwl_bt_notif_statistics), + sizeof(struct iwl_notif_statistics)); + spin_unlock(&priv->statistics.lock); + return 0; + } + + change = common->temperature != priv->statistics.common.temperature || + (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK); + + iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm, + rx_ofdm_ht, rx_cck, tx, bt_activity); + + iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp); + + priv->statistics.flag = *flag; + memcpy(&priv->statistics.common, common, sizeof(*common)); + memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy)); + memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm)); + memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht)); + memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck)); + memcpy(&priv->statistics.tx, tx, sizeof(*tx)); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (bt_activity) + memcpy(&priv->statistics.bt_activity, bt_activity, + sizeof(*bt_activity)); +#endif + + priv->rx_statistics_jiffies = stamp; + + set_bit(STATUS_STATISTICS, &priv->status); + + /* Reschedule the statistics timer to occur in + * reg_recalib_period seconds to ensure we get a + * thermal update even if the uCode doesn't give + * us one */ + mod_timer(&priv->statistics_periodic, jiffies + + msecs_to_jiffies(reg_recalib_period * 1000)); + + if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && + (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { + iwlagn_rx_calc_noise(priv); + queue_work(priv->workqueue, &priv->run_time_calib_work); + } + if (priv->lib->temperature && change) + priv->lib->temperature(priv); + + spin_unlock(&priv->statistics.lock); + + return 0; +} + +static int iwlagn_rx_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_notif_statistics *stats = (void *)pkt->data; + + if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) { +#ifdef CONFIG_IWLWIFI_DEBUGFS + memset(&priv->accum_stats, 0, + sizeof(priv->accum_stats)); + memset(&priv->delta_stats, 0, + sizeof(priv->delta_stats)); + memset(&priv->max_delta_stats, 0, + sizeof(priv->max_delta_stats)); +#endif + IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); + } + iwlagn_rx_statistics(priv, rxb, cmd); + return 0; +} + +/* Handle notification from uCode that card's power state is changing + * due to software, hardware, or critical temperature RFKILL */ +static int iwlagn_rx_card_state_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; + u32 flags = le32_to_cpu(card_state_notif->flags); + unsigned long status = priv->status; + + IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On", + (flags & CT_CARD_DISABLED) ? + "Reached" : "Not reached"); + + if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | + CT_CARD_DISABLED)) { + + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + if (!(flags & RXON_CARD_DISABLED)) { + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + } + if (flags & CT_CARD_DISABLED) + iwl_tt_enter_ct_kill(priv); + } + if (!(flags & CT_CARD_DISABLED)) + iwl_tt_exit_ct_kill(priv); + + if (flags & HW_CARD_DISABLED) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + + if (!(flags & RXON_CARD_DISABLED)) + iwl_scan_cancel(priv); + + if ((test_bit(STATUS_RF_KILL_HW, &status) != + test_bit(STATUS_RF_KILL_HW, &priv->status))) + wiphy_rfkill_set_hw_state(priv->hw->wiphy, + test_bit(STATUS_RF_KILL_HW, &priv->status)); + else + wake_up(&priv->trans->wait_command_queue); + return 0; +} + +static int iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) + +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data; + + if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > + priv->missed_beacon_threshold) { + IWL_DEBUG_CALIB(priv, + "missed bcn cnsq %d totl %d rcd %d expctd %d\n", + le32_to_cpu(missed_beacon->consecutive_missed_beacons), + le32_to_cpu(missed_beacon->total_missed_becons), + le32_to_cpu(missed_beacon->num_recvd_beacons), + le32_to_cpu(missed_beacon->num_expected_beacons)); + if (!test_bit(STATUS_SCANNING, &priv->status)) + iwl_init_sensitivity(priv); + } + return 0; +} + +/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). + * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ +static int iwlagn_rx_reply_rx_phy(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + priv->last_phy_res_valid = true; + memcpy(&priv->last_phy_res, pkt->data, + sizeof(struct iwl_rx_phy_res)); + return 0; +} + +/* + * returns non-zero if packet should be dropped + */ +static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u32 decrypt_res, + struct ieee80211_rx_status *stats) +{ + u16 fc = le16_to_cpu(hdr->frame_control); + + /* + * All contexts have the same setting here due to it being + * a module parameter, so OK to check any context. + */ + if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags & + RXON_FILTER_DIS_DECRYPT_MSK) + return 0; + + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return 0; + + IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); + switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { + case RX_RES_STATUS_SEC_TYPE_TKIP: + /* The uCode has got a bad phase 1 Key, pushes the packet. + * Decryption will be done in SW. */ + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_KEY_TTAK) + break; + + case RX_RES_STATUS_SEC_TYPE_WEP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_ICV_MIC) { + /* bad ICV, the packet is destroyed since the + * decryption is inplace, drop it */ + IWL_DEBUG_RX(priv, "Packet destroyed\n"); + return -1; + } + case RX_RES_STATUS_SEC_TYPE_CCMP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_DECRYPT_OK) { + IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); + stats->flag |= RX_FLAG_DECRYPTED; + } + break; + + default: + break; + } + return 0; +} + +static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u16 len, + u32 ampdu_status, + struct iwl_rx_cmd_buffer *rxb, + struct ieee80211_rx_status *stats) +{ + struct sk_buff *skb; + __le16 fc = hdr->frame_control; + struct iwl_rxon_context *ctx; + unsigned int hdrlen, fraglen; + + /* We only process data packets if the interface is open */ + if (unlikely(!priv->is_open)) { + IWL_DEBUG_DROP_LIMIT(priv, + "Dropping packet while interface is not open.\n"); + return; + } + + /* In case of HW accelerated crypto and bad decryption, drop */ + if (!iwlwifi_mod_params.sw_crypto && + iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) + return; + + /* Dont use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(priv, "alloc_skb failed\n"); + return; + } + /* If frame is small enough to fit in skb->head, pull it completely. + * If not, only pull ieee80211_hdr so that splice() or TCP coalesce + * are more efficient. + */ + hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr); + + memcpy(skb_put(skb, hdrlen), hdr, hdrlen); + fraglen = len - hdrlen; + + if (fraglen) { + int offset = (void *)hdr + hdrlen - + rxb_addr(rxb) + rxb_offset(rxb); + + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } + + /* + * Wake any queues that were stopped due to a passive channel tx + * failure. This can happen because the regulatory enforcement in + * the device waits for a beacon before allowing transmission, + * sometimes even after already having transmitted frames for the + * association because the new RXON may reset the information. + */ + if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) { + for_each_context(priv, ctx) { + if (!ether_addr_equal(hdr->addr3, + ctx->active.bssid_addr)) + continue; + iwlagn_lift_passive_no_rx(priv); + } + } + + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx(priv->hw, skb); +} + +static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) +{ + u32 decrypt_out = 0; + + if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == + RX_RES_STATUS_STATION_FOUND) + decrypt_out |= (RX_RES_STATUS_STATION_FOUND | + RX_RES_STATUS_NO_STATION_INFO_MISMATCH); + + decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); + + /* packet was not encrypted */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_NONE) + return decrypt_out; + + /* packet was encrypted with unknown alg */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_ERR) + return decrypt_out; + + /* decryption was not done in HW */ + if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != + RX_MPDU_RES_STATUS_DEC_DONE_MSK) + return decrypt_out; + + switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { + + case RX_RES_STATUS_SEC_TYPE_CCMP: + /* alg is CCM: check MIC only */ + if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) + /* Bad MIC */ + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + + break; + + case RX_RES_STATUS_SEC_TYPE_TKIP: + if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { + /* Bad TTAK */ + decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; + break; + } + /* fall through if TTAK OK */ + default: + if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + break; + } + + IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n", + decrypt_in, decrypt_out); + + return decrypt_out; +} + +/* Calc max signal level (dBm) among 3 possible receivers */ +static int iwlagn_calc_rssi(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp) +{ + /* data from PHY/DSP regarding signal strength, etc., + * contents are always there, not configurable by host + */ + struct iwlagn_non_cfg_phy *ncphy = + (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf; + u32 val, rssi_a, rssi_b, rssi_c, max_rssi; + u8 agc; + + val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]); + agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS; + + /* Find max rssi among 3 possible receivers. + * These values are measured by the digital signal processor (DSP). + * They should stay fairly constant even as the signal strength varies, + * if the radio's automatic gain control (AGC) is working right. + * AGC value (see below) will provide the "interesting" info. + */ + val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]); + rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >> + IWLAGN_OFDM_RSSI_A_BIT_POS; + rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >> + IWLAGN_OFDM_RSSI_B_BIT_POS; + val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]); + rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >> + IWLAGN_OFDM_RSSI_C_BIT_POS; + + max_rssi = max_t(u32, rssi_a, rssi_b); + max_rssi = max_t(u32, max_rssi, rssi_c); + + IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", + rssi_a, rssi_b, rssi_c, max_rssi, agc); + + /* dBm = max_rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. */ + return max_rssi - agc - IWLAGN_RSSI_OFFSET; +} + +/* Called for REPLY_RX (legacy ABG frames), or + * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */ +static int iwlagn_rx_reply_rx(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct ieee80211_hdr *header; + struct ieee80211_rx_status rx_status; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_phy_res *phy_res; + __le32 rx_pkt_status; + struct iwl_rx_mpdu_res_start *amsdu; + u32 len; + u32 ampdu_status; + u32 rate_n_flags; + + /** + * REPLY_RX and REPLY_RX_MPDU_CMD are handled differently. + * REPLY_RX: physical layer info is in this buffer + * REPLY_RX_MPDU_CMD: physical layer info was sent in separate + * command and cached in priv->last_phy_res + * + * Here we set up local variables depending on which command is + * received. + */ + if (pkt->hdr.cmd == REPLY_RX) { + phy_res = (struct iwl_rx_phy_res *)pkt->data; + header = (struct ieee80211_hdr *)(pkt->data + sizeof(*phy_res) + + phy_res->cfg_phy_cnt); + + len = le16_to_cpu(phy_res->byte_count); + rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*phy_res) + + phy_res->cfg_phy_cnt + len); + ampdu_status = le32_to_cpu(rx_pkt_status); + } else { + if (!priv->last_phy_res_valid) { + IWL_ERR(priv, "MPDU frame without cached PHY data\n"); + return 0; + } + phy_res = &priv->last_phy_res; + amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; + header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); + ampdu_status = iwlagn_translate_rx_status(priv, + le32_to_cpu(rx_pkt_status)); + } + + if ((unlikely(phy_res->cfg_phy_cnt > 20))) { + IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n", + phy_res->cfg_phy_cnt); + return 0; + } + + if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(rx_pkt_status)); + return 0; + } + + /* This will be used in several places later */ + rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); + + /* rx_status carries information about the packet to mac80211 */ + rx_status.mactime = le64_to_cpu(phy_res->timestamp); + rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), + rx_status.band); + rx_status.rate_idx = + iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); + rx_status.flag = 0; + + /* TSF isn't reliable. In order to allow smooth user experience, + * this W/A doesn't propagate it to the mac80211 */ + /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ + + priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); + + /* Find max signal strength (dBm) among 3 antenna/receiver chains */ + rx_status.signal = iwlagn_calc_rssi(priv, phy_res); + + IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n", + rx_status.signal, (unsigned long long)rx_status.mactime); + + /* + * "antenna number" + * + * It seems that the antenna field in the phy flags value + * is actually a bit field. This is undefined by radiotap, + * it wants an actual antenna number but I always get "7" + * for most legacy frames I receive indicating that the + * same frame was received on all three RX chains. + * + * I think this field should be removed in favor of a + * new 802.11n radiotap field "RX chains" that is defined + * as a bitmask. + */ + rx_status.antenna = + (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; + + /* set the preamble flag if appropriate */ + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + + /* Set up the HT phy flags */ + if (rate_n_flags & RATE_MCS_HT_MSK) + rx_status.flag |= RX_FLAG_HT; + if (rate_n_flags & RATE_MCS_HT40_MSK) + rx_status.flag |= RX_FLAG_40MHZ; + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status.flag |= RX_FLAG_SHORT_GI; + if (rate_n_flags & RATE_MCS_GF_MSK) + rx_status.flag |= RX_FLAG_HT_GF; + + iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, + rxb, &rx_status); + return 0; +} + +static int iwlagn_rx_noa_notification(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_wipan_noa_data *new_data, *old_data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data; + + /* no condition -- we're in softirq */ + old_data = rcu_dereference_protected(priv->noa_data, true); + + if (noa_notif->noa_active) { + u32 len = le16_to_cpu(noa_notif->noa_attribute.length); + u32 copylen = len; + + /* EID, len, OUI, subtype */ + len += 1 + 1 + 3 + 1; + /* P2P id, P2P length */ + len += 1 + 2; + copylen += 1 + 2; + + new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); + if (new_data) { + new_data->length = len; + new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; + new_data->data[1] = len - 2; /* not counting EID, len */ + new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff; + new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff; + new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff; + new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P; + memcpy(&new_data->data[6], &noa_notif->noa_attribute, + copylen); + } + } else + new_data = NULL; + + rcu_assign_pointer(priv->noa_data, new_data); + + if (old_data) + kfree_rcu(old_data, rcu_head); + + return 0; +} + +/** + * iwl_setup_rx_handlers - Initialize Rx handler callbacks + * + * Setup the RX handlers for each of the reply types sent from the uCode + * to the host. + */ +void iwl_setup_rx_handlers(struct iwl_priv *priv) +{ + int (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + + handlers = priv->rx_handlers; + + handlers[REPLY_ERROR] = iwlagn_rx_reply_error; + handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa; + handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwlagn_rx_spectrum_measure_notif; + handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif; + handlers[PM_DEBUG_STATISTIC_NOTIFIC] = + iwlagn_rx_pm_debug_statistics_notif; + handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif; + handlers[REPLY_ADD_STA] = iwl_add_sta_callback; + + handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification; + + /* + * The same handler is used for both the REPLY to a discrete + * statistics request from the host as well as for the periodic + * statistics notifications (after received beacons) from the uCode. + */ + handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics; + handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics; + + iwl_setup_rx_scan_handlers(priv); + + handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif; + handlers[MISSED_BEACONS_NOTIFICATION] = + iwlagn_rx_missed_beacon_notif; + + /* Rx handlers */ + handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy; + handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx; + + /* block ack */ + handlers[REPLY_COMPRESSED_BA] = + iwlagn_rx_reply_compressed_ba; + + priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; + + /* set up notification wait support */ + iwl_notification_wait_init(&priv->notif_wait); + + /* Set up BT Rx handlers */ + if (priv->cfg->bt_params) + iwlagn_bt_rx_handler_setup(priv); +} + +int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + void (*pre_rx_handler)(struct iwl_priv *, + struct iwl_rx_cmd_buffer *); + int err = 0; + + /* + * Do the notification wait before RX handlers so + * even if the RX handler consumes the RXB we have + * access to it in the notification wait entry. + */ + iwl_notification_wait_notify(&priv->notif_wait, pkt); + + /* RX data may be forwarded to userspace (using pre_rx_handler) in one + * of two cases: the first, that the user owns the uCode through + * testmode - in such case the pre_rx_handler is set and no further + * processing takes place. The other case is when the user want to + * monitor the rx w/o affecting the regular flow - the pre_rx_handler + * will be set but the ownership flag != IWL_OWNERSHIP_TM and the flow + * continues. + * We need to use ACCESS_ONCE to prevent a case where the handler + * changes between the check and the call. + */ + pre_rx_handler = ACCESS_ONCE(priv->pre_rx_handler); + if (pre_rx_handler) + pre_rx_handler(priv, rxb); + if (priv->ucode_owner != IWL_OWNERSHIP_TM) { + /* Based on type of command response or notification, + * handle those that need handling via function in + * rx_handlers table. See iwl_setup_rx_handlers() */ + if (priv->rx_handlers[pkt->hdr.cmd]) { + priv->rx_handlers_stats[pkt->hdr.cmd]++; + err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd); + } else { + /* No handling needed */ + IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", + iwl_dvm_get_cmd_string(pkt->hdr.cmd), + pkt->hdr.cmd); + } + } + return err; +} diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c new file mode 100644 index 000000000000..4f55689ac6a7 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -0,0 +1,1577 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include "iwl-trans.h" +#include "iwl-modparams.h" +#include "dev.h" +#include "agn.h" +#include "calib.h" + +/* + * initialize rxon structure with default values from eeprom + */ +void iwl_connection_init_rx_config(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + memset(&ctx->staging, 0, sizeof(ctx->staging)); + + if (!ctx->vif) { + ctx->staging.dev_type = ctx->unused_devtype; + } else + switch (ctx->vif->type) { + case NL80211_IFTYPE_AP: + ctx->staging.dev_type = ctx->ap_devtype; + break; + + case NL80211_IFTYPE_STATION: + ctx->staging.dev_type = ctx->station_devtype; + ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case NL80211_IFTYPE_ADHOC: + ctx->staging.dev_type = ctx->ibss_devtype; + ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | + RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case NL80211_IFTYPE_MONITOR: + ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER; + break; + + default: + IWL_ERR(priv, "Unsupported interface type %d\n", + ctx->vif->type); + break; + } + +#if 0 + /* TODO: Figure out when short_preamble would be set and cache from + * that */ + if (!hw_to_local(priv->hw)->short_preamble) + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + else + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; +#endif + + ctx->staging.channel = cpu_to_le16(priv->hw->conf.channel->hw_value); + priv->band = priv->hw->conf.channel->band; + + iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); + + /* clear both MIX and PURE40 mode flag */ + ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | + RXON_FLG_CHANNEL_MODE_PURE_40); + if (ctx->vif) + memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); + + ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; +} + +static int iwlagn_disable_bss(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_rxon_cmd *send) +{ + __le32 old_filter = send->filter_flags; + int ret; + + send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, + CMD_SYNC, sizeof(*send), send); + + send->filter_flags = old_filter; + + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, + "Error clearing ASSOC_MSK on BSS (%d)\n", ret); + + return ret; +} + +static int iwlagn_disable_pan(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_rxon_cmd *send) +{ + struct iwl_notification_wait disable_wait; + __le32 old_filter = send->filter_flags; + u8 old_dev_type = send->dev_type; + int ret; + static const u8 deactivate_cmd[] = { + REPLY_WIPAN_DEACTIVATION_COMPLETE + }; + + iwl_init_notification_wait(&priv->notif_wait, &disable_wait, + deactivate_cmd, ARRAY_SIZE(deactivate_cmd), + NULL, NULL); + + send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + send->dev_type = RXON_DEV_TYPE_P2P; + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, + CMD_SYNC, sizeof(*send), send); + + send->filter_flags = old_filter; + send->dev_type = old_dev_type; + + if (ret) { + IWL_ERR(priv, "Error disabling PAN (%d)\n", ret); + iwl_remove_notification(&priv->notif_wait, &disable_wait); + } else { + ret = iwl_wait_notification(&priv->notif_wait, + &disable_wait, HZ); + if (ret) + IWL_ERR(priv, "Timed out waiting for PAN disable\n"); + } + + return ret; +} + +static int iwlagn_disconn_pan(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_rxon_cmd *send) +{ + __le32 old_filter = send->filter_flags; + int ret; + + send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, + sizeof(*send), send); + + send->filter_flags = old_filter; + + return ret; +} + +static void iwlagn_update_qos(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret; + + if (!ctx->is_active) + return; + + ctx->qos_data.def_qos_parm.qos_flags = 0; + + if (ctx->qos_data.qos_active) + ctx->qos_data.def_qos_parm.qos_flags |= + QOS_PARAM_FLG_UPDATE_EDCA_MSK; + + if (ctx->ht.enabled) + ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; + + IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", + ctx->qos_data.qos_active, + ctx->qos_data.def_qos_parm.qos_flags); + + ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC, + sizeof(struct iwl_qosparam_cmd), + &ctx->qos_data.def_qos_parm); + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); +} + +static int iwlagn_update_beacon(struct iwl_priv *priv, + struct ieee80211_vif *vif) +{ + lockdep_assert_held(&priv->mutex); + + dev_kfree_skb(priv->beacon_skb); + priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif); + if (!priv->beacon_skb) + return -ENOMEM; + return iwlagn_send_beacon_cmd(priv); +} + +static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret = 0; + struct iwl_rxon_assoc_cmd rxon_assoc; + const struct iwl_rxon_cmd *rxon1 = &ctx->staging; + const struct iwl_rxon_cmd *rxon2 = &ctx->active; + + if ((rxon1->flags == rxon2->flags) && + (rxon1->filter_flags == rxon2->filter_flags) && + (rxon1->cck_basic_rates == rxon2->cck_basic_rates) && + (rxon1->ofdm_ht_single_stream_basic_rates == + rxon2->ofdm_ht_single_stream_basic_rates) && + (rxon1->ofdm_ht_dual_stream_basic_rates == + rxon2->ofdm_ht_dual_stream_basic_rates) && + (rxon1->ofdm_ht_triple_stream_basic_rates == + rxon2->ofdm_ht_triple_stream_basic_rates) && + (rxon1->acquisition_data == rxon2->acquisition_data) && + (rxon1->rx_chain == rxon2->rx_chain) && + (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) { + IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n"); + return 0; + } + + rxon_assoc.flags = ctx->staging.flags; + rxon_assoc.filter_flags = ctx->staging.filter_flags; + rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates; + rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; + rxon_assoc.reserved1 = 0; + rxon_assoc.reserved2 = 0; + rxon_assoc.reserved3 = 0; + rxon_assoc.ofdm_ht_single_stream_basic_rates = + ctx->staging.ofdm_ht_single_stream_basic_rates; + rxon_assoc.ofdm_ht_dual_stream_basic_rates = + ctx->staging.ofdm_ht_dual_stream_basic_rates; + rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; + rxon_assoc.ofdm_ht_triple_stream_basic_rates = + ctx->staging.ofdm_ht_triple_stream_basic_rates; + rxon_assoc.acquisition_data = ctx->staging.acquisition_data; + + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd, + CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc); + return ret; +} + +static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) +{ + u16 new_val; + u16 beacon_factor; + + /* + * If mac80211 hasn't given us a beacon interval, program + * the default into the device (not checking this here + * would cause the adjustment below to return the maximum + * value, which may break PAN.) + */ + if (!beacon_val) + return DEFAULT_BEACON_INTERVAL; + + /* + * If the beacon interval we obtained from the peer + * is too large, we'll have to wake up more often + * (and in IBSS case, we'll beacon too much) + * + * For example, if max_beacon_val is 4096, and the + * requested beacon interval is 7000, we'll have to + * use 3500 to be able to wake up on the beacons. + * + * This could badly influence beacon detection stats. + */ + + beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; + new_val = beacon_val / beacon_factor; + + if (!new_val) + new_val = max_beacon_val; + + return new_val; +} + +static int iwl_send_rxon_timing(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + u64 tsf; + s32 interval_tm, rem; + struct ieee80211_conf *conf = NULL; + u16 beacon_int; + struct ieee80211_vif *vif = ctx->vif; + + conf = &priv->hw->conf; + + lockdep_assert_held(&priv->mutex); + + memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); + + ctx->timing.timestamp = cpu_to_le64(priv->timestamp); + ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); + + beacon_int = vif ? vif->bss_conf.beacon_int : 0; + + /* + * TODO: For IBSS we need to get atim_window from mac80211, + * for now just always use 0 + */ + ctx->timing.atim_window = 0; + + if (ctx->ctxid == IWL_RXON_CTX_PAN && + (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) && + iwl_is_associated(priv, IWL_RXON_CTX_BSS) && + priv->contexts[IWL_RXON_CTX_BSS].vif && + priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) { + ctx->timing.beacon_interval = + priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval; + beacon_int = le16_to_cpu(ctx->timing.beacon_interval); + } else if (ctx->ctxid == IWL_RXON_CTX_BSS && + iwl_is_associated(priv, IWL_RXON_CTX_PAN) && + priv->contexts[IWL_RXON_CTX_PAN].vif && + priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int && + (!iwl_is_associated_ctx(ctx) || !ctx->vif || + !ctx->vif->bss_conf.beacon_int)) { + ctx->timing.beacon_interval = + priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval; + beacon_int = le16_to_cpu(ctx->timing.beacon_interval); + } else { + beacon_int = iwl_adjust_beacon_interval(beacon_int, + IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT); + ctx->timing.beacon_interval = cpu_to_le16(beacon_int); + } + + ctx->beacon_int = beacon_int; + + tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ + interval_tm = beacon_int * TIME_UNIT; + rem = do_div(tsf, interval_tm); + ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; + + IWL_DEBUG_ASSOC(priv, + "beacon interval %d beacon timer %d beacon tim %d\n", + le16_to_cpu(ctx->timing.beacon_interval), + le32_to_cpu(ctx->timing.beacon_init_val), + le16_to_cpu(ctx->timing.atim_window)); + + return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, + CMD_SYNC, sizeof(ctx->timing), &ctx->timing); +} + +static int iwlagn_rxon_disconn(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret; + struct iwl_rxon_cmd *active = (void *)&ctx->active; + + if (ctx->ctxid == IWL_RXON_CTX_BSS) { + ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); + } else { + ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); + if (ret) + return ret; + if (ctx->vif) { + ret = iwl_send_rxon_timing(priv, ctx); + if (ret) { + IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); + return ret; + } + ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging); + } + } + if (ret) + return ret; + + /* + * Un-assoc RXON clears the station table and WEP + * keys, so we have to restore those afterwards. + */ + iwl_clear_ucode_stations(priv, ctx); + /* update -- might need P2P now */ + iwl_update_bcast_station(priv, ctx); + iwl_restore_stations(priv, ctx); + ret = iwl_restore_default_wep_keys(priv, ctx); + if (ret) { + IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); + return ret; + } + + memcpy(active, &ctx->staging, sizeof(*active)); + return 0; +} + +static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) +{ + int ret; + s8 prev_tx_power; + bool defer; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED) + return 0; + + lockdep_assert_held(&priv->mutex); + + if (priv->tx_power_user_lmt == tx_power && !force) + return 0; + + if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) { + IWL_WARN(priv, + "Requested user TXPOWER %d below lower limit %d.\n", + tx_power, + IWLAGN_TX_POWER_TARGET_POWER_MIN); + return -EINVAL; + } + + if (tx_power > priv->tx_power_device_lmt) { + IWL_WARN(priv, + "Requested user TXPOWER %d above upper limit %d.\n", + tx_power, priv->tx_power_device_lmt); + return -EINVAL; + } + + if (!iwl_is_ready_rf(priv)) + return -EIO; + + /* scan complete and commit_rxon use tx_power_next value, + * it always need to be updated for newest request */ + priv->tx_power_next = tx_power; + + /* do not set tx power when scanning or channel changing */ + defer = test_bit(STATUS_SCANNING, &priv->status) || + memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); + if (defer && !force) { + IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); + return 0; + } + + prev_tx_power = priv->tx_power_user_lmt; + priv->tx_power_user_lmt = tx_power; + + ret = iwlagn_send_tx_power(priv); + + /* if fail to set tx_power, restore the orig. tx power */ + if (ret) { + priv->tx_power_user_lmt = prev_tx_power; + priv->tx_power_next = prev_tx_power; + } + return ret; +} + +static int iwlagn_rxon_connect(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int ret; + struct iwl_rxon_cmd *active = (void *)&ctx->active; + + /* RXON timing must be before associated RXON */ + if (ctx->ctxid == IWL_RXON_CTX_BSS) { + ret = iwl_send_rxon_timing(priv, ctx); + if (ret) { + IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); + return ret; + } + } + /* QoS info may be cleared by previous un-assoc RXON */ + iwlagn_update_qos(priv, ctx); + + /* + * We'll run into this code path when beaconing is + * enabled, but then we also need to send the beacon + * to the device. + */ + if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { + ret = iwlagn_update_beacon(priv, ctx->vif); + if (ret) { + IWL_ERR(priv, + "Error sending required beacon (%d)!\n", + ret); + return ret; + } + } + + priv->start_calib = 0; + /* + * Apply the new configuration. + * + * Associated RXON doesn't clear the station table in uCode, + * so we don't need to restore stations etc. after this. + */ + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, + sizeof(struct iwl_rxon_cmd), &ctx->staging); + if (ret) { + IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); + return ret; + } + memcpy(active, &ctx->staging, sizeof(*active)); + + /* IBSS beacon needs to be sent after setting assoc */ + if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) + if (iwlagn_update_beacon(priv, ctx->vif)) + IWL_ERR(priv, "Error sending IBSS beacon\n"); + iwl_init_sensitivity(priv); + + /* + * If we issue a new RXON command which required a tune then + * we must send a new TXPOWER command or we won't be able to + * Tx any frames. + * + * It's expected we set power here if channel is changing. + */ + ret = iwl_set_tx_power(priv, priv->tx_power_next, true); + if (ret) { + IWL_ERR(priv, "Error sending TX power (%d)\n", ret); + return ret; + } + + if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && + priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) + ieee80211_request_smps(ctx->vif, + priv->cfg->ht_params->smps_mode); + + return 0; +} + +int iwlagn_set_pan_params(struct iwl_priv *priv) +{ + struct iwl_wipan_params_cmd cmd; + struct iwl_rxon_context *ctx_bss, *ctx_pan; + int slot0 = 300, slot1 = 0; + int ret; + + if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS)) + return 0; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + lockdep_assert_held(&priv->mutex); + + ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS]; + ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN]; + + /* + * If the PAN context is inactive, then we don't need + * to update the PAN parameters, the last thing we'll + * have done before it goes inactive is making the PAN + * parameters be WLAN-only. + */ + if (!ctx_pan->is_active) + return 0; + + memset(&cmd, 0, sizeof(cmd)); + + /* only 2 slots are currently allowed */ + cmd.num_slots = 2; + + cmd.slots[0].type = 0; /* BSS */ + cmd.slots[1].type = 1; /* PAN */ + + if (priv->hw_roc_setup) { + /* both contexts must be used for this to happen */ + slot1 = IWL_MIN_SLOT_TIME; + slot0 = 3000; + } else if (ctx_bss->vif && ctx_pan->vif) { + int bcnint = ctx_pan->beacon_int; + int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; + + /* should be set, but seems unused?? */ + cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE); + + if (ctx_pan->vif->type == NL80211_IFTYPE_AP && + bcnint && + bcnint != ctx_bss->beacon_int) { + IWL_ERR(priv, + "beacon intervals don't match (%d, %d)\n", + ctx_bss->beacon_int, ctx_pan->beacon_int); + } else + bcnint = max_t(int, bcnint, + ctx_bss->beacon_int); + if (!bcnint) + bcnint = DEFAULT_BEACON_INTERVAL; + slot0 = bcnint / 2; + slot1 = bcnint - slot0; + + if (test_bit(STATUS_SCAN_HW, &priv->status) || + (!ctx_bss->vif->bss_conf.idle && + !ctx_bss->vif->bss_conf.assoc)) { + slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; + slot1 = IWL_MIN_SLOT_TIME; + } else if (!ctx_pan->vif->bss_conf.idle && + !ctx_pan->vif->bss_conf.assoc) { + slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; + slot0 = IWL_MIN_SLOT_TIME; + } + } else if (ctx_pan->vif) { + slot0 = 0; + slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) * + ctx_pan->beacon_int; + slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1); + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME; + slot1 = IWL_MIN_SLOT_TIME; + } + } + + cmd.slots[0].width = cpu_to_le16(slot0); + cmd.slots[1].width = cpu_to_le16(slot1); + + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); + + return ret; +} + +static void _iwl_set_rxon_ht(struct iwl_priv *priv, + struct iwl_ht_config *ht_conf, + struct iwl_rxon_context *ctx) +{ + struct iwl_rxon_cmd *rxon = &ctx->staging; + + if (!ctx->ht.enabled) { + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + return; + } + + /* FIXME: if the definition of ht.protection changed, the "translation" + * will be needed for rxon->flags + */ + rxon->flags |= cpu_to_le32(ctx->ht.protection << + RXON_FLG_HT_OPERATING_MODE_POS); + + /* Set up channel bandwidth: + * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ + /* clear the HT channel mode before set the mode */ + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) { + /* pure ht40 */ + if (ctx->ht.protection == + IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { + rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; + /* + * Note: control channel is opposite of extension + * channel + */ + switch (ctx->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + } + } else { + /* + * Note: control channel is opposite of extension + * channel + */ + switch (ctx->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + default: + /* + * channel location only valid if in Mixed + * mode + */ + IWL_ERR(priv, + "invalid extension channel offset\n"); + break; + } + } + } else { + rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; + } + + iwlagn_set_rxon_chain(priv, ctx); + + IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " + "extension channel offset 0x%x\n", + le32_to_cpu(rxon->flags), ctx->ht.protection, + ctx->ht.extension_chan_offset); +} + +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) +{ + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) + _iwl_set_rxon_ht(priv, ht_conf, ctx); +} + +/** + * iwl_set_rxon_channel - Set the band and channel values in staging RXON + * @ch: requested channel as a pointer to struct ieee80211_channel + + * NOTE: Does not commit to the hardware; it sets appropriate bit fields + * in the staging RXON flag structure based on the ch->band + */ +void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx) +{ + enum ieee80211_band band = ch->band; + u16 channel = ch->hw_value; + + if ((le16_to_cpu(ctx->staging.channel) == channel) && + (priv->band == band)) + return; + + ctx->staging.channel = cpu_to_le16(channel); + if (band == IEEE80211_BAND_5GHZ) + ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; + else + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + + priv->band = band; + + IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band); + +} + +void iwl_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + enum ieee80211_band band, + struct ieee80211_vif *vif) +{ + if (band == IEEE80211_BAND_5GHZ) { + ctx->staging.flags &= + ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK + | RXON_FLG_CCK_MSK); + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + } else { + /* Copied from iwl_post_associate() */ + if (vif && vif->bss_conf.use_short_slot) + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; + ctx->staging.flags &= ~RXON_FLG_CCK_MSK; + } +} + +static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, int hw_decrypt) +{ + struct iwl_rxon_cmd *rxon = &ctx->staging; + + if (hw_decrypt) + rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; + else + rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; + +} + +/* validate RXON structure is valid */ +static int iwl_check_rxon_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_rxon_cmd *rxon = &ctx->staging; + u32 errors = 0; + + if (rxon->flags & RXON_FLG_BAND_24G_MSK) { + if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { + IWL_WARN(priv, "check 2.4G: wrong narrow\n"); + errors |= BIT(0); + } + if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { + IWL_WARN(priv, "check 2.4G: wrong radar\n"); + errors |= BIT(1); + } + } else { + if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { + IWL_WARN(priv, "check 5.2G: not short slot!\n"); + errors |= BIT(2); + } + if (rxon->flags & RXON_FLG_CCK_MSK) { + IWL_WARN(priv, "check 5.2G: CCK!\n"); + errors |= BIT(3); + } + } + if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { + IWL_WARN(priv, "mac/bssid mcast!\n"); + errors |= BIT(4); + } + + /* make sure basic rates 6Mbps and 1Mbps are supported */ + if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 && + (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) { + IWL_WARN(priv, "neither 1 nor 6 are basic\n"); + errors |= BIT(5); + } + + if (le16_to_cpu(rxon->assoc_id) > 2007) { + IWL_WARN(priv, "aid > 2007\n"); + errors |= BIT(6); + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { + IWL_WARN(priv, "CCK and short slot\n"); + errors |= BIT(7); + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { + IWL_WARN(priv, "CCK and auto detect"); + errors |= BIT(8); + } + + if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | + RXON_FLG_TGG_PROTECT_MSK)) == + RXON_FLG_TGG_PROTECT_MSK) { + IWL_WARN(priv, "TGg but no auto-detect\n"); + errors |= BIT(9); + } + + if (rxon->channel == 0) { + IWL_WARN(priv, "zero channel is invalid\n"); + errors |= BIT(10); + } + + WARN(errors, "Invalid RXON (%#x), channel %d", + errors, le16_to_cpu(rxon->channel)); + + return errors ? -EINVAL : 0; +} + +/** + * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @priv: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +static int iwl_full_rxon_required(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + const struct iwl_rxon_cmd *staging = &ctx->staging; + const struct iwl_rxon_cmd *active = &ctx->active; + +#define CHK(cond) \ + if ((cond)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ + return 1; \ + } + +#define CHK_NEQ(c1, c2) \ + if ((c1) != (c2)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " \ + #c1 " != " #c2 " - %d != %d\n", \ + (c1), (c2)); \ + return 1; \ + } + + /* These items are only settable from the full RXON command */ + CHK(!iwl_is_associated_ctx(ctx)); + CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); + CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); + CHK(!ether_addr_equal(staging->wlap_bssid_addr, + active->wlap_bssid_addr)); + CHK_NEQ(staging->dev_type, active->dev_type); + CHK_NEQ(staging->channel, active->channel); + CHK_NEQ(staging->air_propagation, active->air_propagation); + CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, + active->ofdm_ht_single_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, + active->ofdm_ht_dual_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates, + active->ofdm_ht_triple_stream_basic_rates); + CHK_NEQ(staging->assoc_id, active->assoc_id); + + /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can + * be updated with the RXON_ASSOC command -- however only some + * flag transitions are allowed using RXON_ASSOC */ + + /* Check if we are not switching bands */ + CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, + active->flags & RXON_FLG_BAND_24G_MSK); + + /* Check if we are switching association toggle */ + CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, + active->filter_flags & RXON_FILTER_ASSOC_MSK); + +#undef CHK +#undef CHK_NEQ + + return 0; +} + +#ifdef CONFIG_IWLWIFI_DEBUG +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ + struct iwl_rxon_context *ctx = &priv->contexts[ctxid]; + struct iwl_rxon_cmd *rxon = &ctx->staging; + + IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); + iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); + IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n", + le16_to_cpu(rxon->channel)); + IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n", + le32_to_cpu(rxon->flags)); + IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n", + le32_to_cpu(rxon->filter_flags)); + IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type); + IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n", + rxon->ofdm_basic_rates); + IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n", + rxon->cck_basic_rates); + IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr); + IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); + IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", + le16_to_cpu(rxon->assoc_id)); +} +#endif + +static void iwl_calc_basic_rates(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int lowest_present_ofdm = 100; + int lowest_present_cck = 100; + u8 cck = 0; + u8 ofdm = 0; + + if (ctx->vif) { + struct ieee80211_supported_band *sband; + unsigned long basic = ctx->vif->bss_conf.basic_rates; + int i; + + sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band]; + + for_each_set_bit(i, &basic, BITS_PER_LONG) { + int hw = sband->bitrates[i].hw_value; + if (hw >= IWL_FIRST_OFDM_RATE) { + ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); + if (lowest_present_ofdm > hw) + lowest_present_ofdm = hw; + } else { + BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); + + cck |= BIT(hw); + if (lowest_present_cck > hw) + lowest_present_cck = hw; + } + } + } + + /* + * Now we've got the basic rates as bitmaps in the ofdm and cck + * variables. This isn't sufficient though, as there might not + * be all the right rates in the bitmap. E.g. if the only basic + * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps + * and 6 Mbps because the 802.11-2007 standard says in 9.6: + * + * [...] a STA responding to a received frame shall transmit + * its Control Response frame [...] at the highest rate in the + * BSSBasicRateSet parameter that is less than or equal to the + * rate of the immediately previous frame in the frame exchange + * sequence ([...]) and that is of the same modulation class + * ([...]) as the received frame. If no rate contained in the + * BSSBasicRateSet parameter meets these conditions, then the + * control frame sent in response to a received frame shall be + * transmitted at the highest mandatory rate of the PHY that is + * less than or equal to the rate of the received frame, and + * that is of the same modulation class as the received frame. + * + * As a consequence, we need to add all mandatory rates that are + * lower than all of the basic rates to these bitmaps. + */ + + if (IWL_RATE_24M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE; + if (IWL_RATE_12M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE; + /* 6M already there or needed so always add */ + ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE; + + /* + * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. + * Note, however: + * - if no CCK rates are basic, it must be ERP since there must + * be some basic rates at all, so they're OFDM => ERP PHY + * (or we're in 5 GHz, and the cck bitmap will never be used) + * - if 11M is a basic rate, it must be ERP as well, so add 5.5M + * - if 5.5M is basic, 1M and 2M are mandatory + * - if 2M is basic, 1M is mandatory + * - if 1M is basic, that's the only valid ACK rate. + * As a consequence, it's not as complicated as it sounds, just add + * any lower rates to the ACK rate bitmap. + */ + if (IWL_RATE_11M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE; + if (IWL_RATE_5M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE; + if (IWL_RATE_2M_INDEX < lowest_present_ofdm) + ofdm |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE; + /* 1M already there or needed so always add */ + cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE; + + IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n", + cck, ofdm); + + /* "basic_rates" is a misnomer here -- should be called ACK rates */ + ctx->staging.cck_basic_rates = cck; + ctx->staging.ofdm_basic_rates = ofdm; +} + +/** + * iwlagn_commit_rxon - commit staging_rxon to hardware + * + * The RXON command in staging_rxon is committed to the hardware and + * the active_rxon structure is updated with the new data. This + * function correctly transitions out of the RXON_ASSOC_MSK state if + * a HW tune is required based on the RXON structure changes. + * + * The connect/disconnect flow should be as the following: + * + * 1. make sure send RXON command with association bit unset if not connect + * this should include the channel and the band for the candidate + * to be connected to + * 2. Add Station before RXON association with the AP + * 3. RXON_timing has to send before RXON for connection + * 4. full RXON command - associated bit set + * 5. use RXON_ASSOC command to update any flags changes + */ +int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + /* cast away the const for active_rxon in this function */ + struct iwl_rxon_cmd *active = (void *)&ctx->active; + bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); + int ret; + + lockdep_assert_held(&priv->mutex); + + if (!iwl_is_alive(priv)) + return -EBUSY; + + /* This function hardcodes a bunch of dual-mode assumptions */ + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + if (!ctx->is_active) + return 0; + + /* always get timestamp with Rx frame */ + ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; + + /* recalculate basic rates */ + iwl_calc_basic_rates(priv, ctx); + + /* + * force CTS-to-self frames protection if RTS-CTS is not preferred + * one aggregation protection method + */ + if (!priv->hw_params.use_rts_for_aggregation) + ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; + + if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || + !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + iwl_print_rx_config_cmd(priv, ctx->ctxid); + ret = iwl_check_rxon_cmd(priv, ctx); + if (ret) { + IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); + return -EINVAL; + } + + /* + * receive commit_rxon request + * abort any previous channel switch if still in process + */ + if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) && + (priv->switch_channel != ctx->staging.channel)) { + IWL_DEBUG_11H(priv, "abort channel switch on %d\n", + le16_to_cpu(priv->switch_channel)); + iwl_chswitch_done(priv, false); + } + + /* + * If we don't need to send a full RXON, we can use + * iwl_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. + */ + if (!iwl_full_rxon_required(priv, ctx)) { + ret = iwlagn_send_rxon_assoc(priv, ctx); + if (ret) { + IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret); + return ret; + } + + memcpy(active, &ctx->staging, sizeof(*active)); + /* + * We do not commit tx power settings while channel changing, + * do it now if after settings changed. + */ + iwl_set_tx_power(priv, priv->tx_power_next, false); + + /* make sure we are in the right PS state */ + iwl_power_update_mode(priv, true); + + return 0; + } + + iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto); + + IWL_DEBUG_INFO(priv, + "Going to commit RXON\n" + " * with%s RXON_FILTER_ASSOC_MSK\n" + " * channel = %d\n" + " * bssid = %pM\n", + (new_assoc ? "" : "out"), + le16_to_cpu(ctx->staging.channel), + ctx->staging.bssid_addr); + + /* + * Always clear associated first, but with the correct config. + * This is required as for example station addition for the + * AP station must be done after the BSSID is set to correctly + * set up filters in the device. + */ + ret = iwlagn_rxon_disconn(priv, ctx); + if (ret) + return ret; + + ret = iwlagn_set_pan_params(priv); + if (ret) + return ret; + + if (new_assoc) + return iwlagn_rxon_connect(priv, ctx); + + return 0; +} + +void iwlagn_config_ht40(struct ieee80211_conf *conf, + struct iwl_rxon_context *ctx) +{ + if (conf_is_ht40_minus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ctx->ht.is_40mhz = true; + } else if (conf_is_ht40_plus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ctx->ht.is_40mhz = true; + } else { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ctx->ht.is_40mhz = false; + } +} + +int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = conf->channel; + int ret = 0; + + IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed); + + mutex_lock(&priv->mutex); + + if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { + IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); + goto out; + } + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); + goto out; + } + + if (changed & (IEEE80211_CONF_CHANGE_SMPS | + IEEE80211_CONF_CHANGE_CHANNEL)) { + /* mac80211 uses static for non-HT which is what we want */ + priv->current_ht_config.smps = conf->smps_mode; + + /* + * Recalculate chain counts. + * + * If monitor mode is enabled then mac80211 will + * set up the SM PS mode to OFF if an HT channel is + * configured. + */ + for_each_context(priv, ctx) + iwlagn_set_rxon_chain(priv, ctx); + } + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + for_each_context(priv, ctx) { + /* Configure HT40 channels */ + if (ctx->ht.enabled != conf_is_ht(conf)) + ctx->ht.enabled = conf_is_ht(conf); + + if (ctx->ht.enabled) { + /* if HT40 is used, it should not change + * after associated except channel switch */ + if (!ctx->ht.is_40mhz || + !iwl_is_associated_ctx(ctx)) + iwlagn_config_ht40(conf, ctx); + } else + ctx->ht.is_40mhz = false; + + /* + * Default to no protection. Protection mode will + * later be set from BSS config in iwl_ht_conf + */ + ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + if (le16_to_cpu(ctx->staging.channel) != + channel->hw_value) + ctx->staging.flags = 0; + + iwl_set_rxon_channel(priv, channel, ctx); + iwl_set_rxon_ht(priv, &priv->current_ht_config); + + iwl_set_flags_for_band(priv, ctx, channel->band, + ctx->vif); + } + + iwl_update_bcast_stations(priv); + } + + if (changed & (IEEE80211_CONF_CHANGE_PS | + IEEE80211_CONF_CHANGE_IDLE)) { + ret = iwl_power_update_mode(priv, false); + if (ret) + IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", + priv->tx_power_user_lmt, conf->power_level); + + iwl_set_tx_power(priv, conf->power_level, false); + } + + for_each_context(priv, ctx) { + if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) + continue; + iwlagn_commit_rxon(priv, ctx); + } + out: + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwlagn_check_needed_chains(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_bss_conf *bss_conf) +{ + struct ieee80211_vif *vif = ctx->vif; + struct iwl_rxon_context *tmp; + struct ieee80211_sta *sta; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + struct ieee80211_sta_ht_cap *ht_cap; + bool need_multiple; + + lockdep_assert_held(&priv->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!sta) { + /* + * If at all, this can only happen through a race + * when the AP disconnects us while we're still + * setting up the connection, in that case mac80211 + * will soon tell us about that. + */ + need_multiple = false; + rcu_read_unlock(); + break; + } + + ht_cap = &sta->ht_cap; + + need_multiple = true; + + /* + * If the peer advertises no support for receiving 2 and 3 + * stream MCS rates, it can't be transmitting them either. + */ + if (ht_cap->mcs.rx_mask[1] == 0 && + ht_cap->mcs.rx_mask[2] == 0) { + need_multiple = false; + } else if (!(ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_DEFINED)) { + /* If it can't TX MCS at all ... */ + need_multiple = false; + } else if (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_RX_DIFF) { + int maxstreams; + + /* + * But if it can receive them, it might still not + * be able to transmit them, which is what we need + * to check here -- so check the number of streams + * it advertises for TX (if different from RX). + */ + + maxstreams = (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK); + maxstreams >>= + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + maxstreams += 1; + + if (maxstreams <= 1) + need_multiple = false; + } + + rcu_read_unlock(); + break; + case NL80211_IFTYPE_ADHOC: + /* currently */ + need_multiple = false; + break; + default: + /* only AP really */ + need_multiple = true; + break; + } + + ctx->ht_need_multiple_chains = need_multiple; + + if (!need_multiple) { + /* check all contexts */ + for_each_context(priv, tmp) { + if (!tmp->vif) + continue; + if (tmp->ht_need_multiple_chains) { + need_multiple = true; + break; + } + } + } + + ht_conf->single_chain_sufficient = !need_multiple; +} + +static void iwlagn_chain_noise_reset(struct iwl_priv *priv) +{ + struct iwl_chain_noise_data *data = &priv->chain_noise_data; + int ret; + + if (!(priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)) + return; + + if ((data->state == IWL_CHAIN_NOISE_ALIVE) && + iwl_is_any_associated(priv)) { + struct iwl_calib_chain_noise_reset_cmd cmd; + + /* clear data for chain noise calibration algorithm */ + data->chain_noise_a = 0; + data->chain_noise_b = 0; + data->chain_noise_c = 0; + data->chain_signal_a = 0; + data->chain_signal_b = 0; + data->chain_signal_c = 0; + data->beacon_count = 0; + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, + priv->phy_calib_chain_noise_reset_cmd); + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_PHY_CALIBRATION_CMD, + CMD_SYNC, sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, + "Could not send REPLY_PHY_CALIBRATION_CMD\n"); + data->state = IWL_CHAIN_NOISE_ACCUMULATE; + IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n"); + } +} + +void iwlagn_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + int ret; + bool force = false; + + mutex_lock(&priv->mutex); + + if (unlikely(!iwl_is_ready(priv))) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + if (unlikely(!ctx->vif)) { + IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n"); + mutex_unlock(&priv->mutex); + return; + } + + if (changes & BSS_CHANGED_BEACON_INT) + force = true; + + if (changes & BSS_CHANGED_QOS) { + ctx->qos_data.qos_active = bss_conf->qos; + iwlagn_update_qos(priv, ctx); + } + + ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); + if (vif->bss_conf.use_short_preamble) + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (changes & BSS_CHANGED_ASSOC) { + if (bss_conf->assoc) { + priv->timestamp = bss_conf->last_tsf; + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + } else { + /* + * If we disassociate while there are pending + * frames, just wake up the queues and let the + * frames "escape" ... This shouldn't really + * be happening to start with, but we should + * not get stuck in this case either since it + * can happen if userspace gets confused. + */ + iwlagn_lift_passive_no_rx(priv); + + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + + if (ctx->ctxid == IWL_RXON_CTX_BSS) + priv->have_rekey_data = false; + } + + iwlagn_bt_coex_rssi_monitor(priv); + } + + if (ctx->ht.enabled) { + ctx->ht.protection = bss_conf->ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + iwlagn_check_needed_chains(priv, ctx, bss_conf); + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + iwlagn_set_rxon_chain(priv, ctx); + + if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) + ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + + if (bss_conf->use_cts_prot) + ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; + else + ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; + + memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); + + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { + if (vif->bss_conf.enable_beacon) { + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + priv->beacon_ctx = ctx; + } else { + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + priv->beacon_ctx = NULL; + } + } + + /* + * If the ucode decides to do beacon filtering before + * association, it will lose beacons that are needed + * before sending frames out on passive channels. This + * causes association failures on those channels. Enable + * receiving beacons in such cases. + */ + + if (vif->type == NL80211_IFTYPE_STATION) { + if (!bss_conf->assoc) + ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK; + else + ctx->staging.filter_flags &= + ~RXON_FILTER_BCON_AWARE_MSK; + } + + if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) + iwlagn_commit_rxon(priv, ctx); + + if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) { + /* + * The chain noise calibration will enable PM upon + * completion. If calibration has already been run + * then we need to enable power management here. + */ + if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) + iwl_power_update_mode(priv, false); + + /* Enable RX differential gain and sensitivity calibrations */ + iwlagn_chain_noise_reset(priv); + priv->start_calib = 1; + } + + if (changes & BSS_CHANGED_IBSS) { + ret = iwlagn_manage_ibss_station(priv, vif, + bss_conf->ibss_joined); + if (ret) + IWL_ERR(priv, "failed to %s IBSS station %pM\n", + bss_conf->ibss_joined ? "add" : "remove", + bss_conf->bssid); + } + + if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_ADHOC && + priv->beacon_ctx) { + if (iwlagn_update_beacon(priv, vif)) + IWL_ERR(priv, "Error sending IBSS beacon\n"); + } + + mutex_unlock(&priv->mutex); +} + +void iwlagn_post_scan(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + /* + * We do not commit power settings while scan is pending, + * do it now if the settings changed. + */ + iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false); + iwl_set_tx_power(priv, priv->tx_power_next, false); + + /* + * Since setting the RXON may have been deferred while + * performing the scan, fire one off if needed + */ + for_each_context(priv, ctx) + if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) + iwlagn_commit_rxon(priv, ctx); + + iwlagn_set_pan_params(priv); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c new file mode 100644 index 000000000000..7ee7f32b0fab --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -0,0 +1,1169 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#include +#include +#include +#include + +#include "eeprom.h" +#include "dev.h" +#include "agn.h" + +/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after + * sending probe req. This should be set long enough to hear probe responses + * from more than one AP. */ +#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ +#define IWL_ACTIVE_DWELL_TIME_52 (20) + +#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) +#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) + +/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. + * Must be set longer than active dwell time. + * For the most reliable scan, set > AP beacon interval (typically 100msec). */ +#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ +#define IWL_PASSIVE_DWELL_TIME_52 (10) +#define IWL_PASSIVE_DWELL_BASE (100) +#define IWL_CHANNEL_TUNE_TIME 5 +#define MAX_SCAN_CHANNEL 50 + +static int iwl_send_scan_abort(struct iwl_priv *priv) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_ABORT_CMD, + .flags = CMD_SYNC | CMD_WANT_SKB, + }; + __le32 *status; + + /* Exit instantly with error when device is not ready + * to receive scan abort command or it does not perform + * hardware scan currently */ + if (!test_bit(STATUS_READY, &priv->status) || + !test_bit(STATUS_GEO_CONFIGURED, &priv->status) || + !test_bit(STATUS_SCAN_HW, &priv->status) || + test_bit(STATUS_FW_ERROR, &priv->status)) + return -EIO; + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) + return ret; + + status = (void *)cmd.resp_pkt->data; + if (*status != CAN_ABORT_STATUS) { + /* The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before we + * the microcode has notified us that a scan is + * completed. */ + IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", + le32_to_cpu(*status)); + ret = -EIO; + } + + iwl_free_resp(&cmd); + return ret; +} + +static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) +{ + /* check if scan was requested from mac80211 */ + if (priv->scan_request) { + IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); + ieee80211_scan_completed(priv->hw, aborted); + } + + if (priv->scan_type == IWL_SCAN_ROC) + iwl_scan_roc_expired(priv); + + priv->scan_type = IWL_SCAN_NORMAL; + priv->scan_vif = NULL; + priv->scan_request = NULL; +} + +static void iwl_process_scan_complete(struct iwl_priv *priv) +{ + bool aborted; + + lockdep_assert_held(&priv->mutex); + + if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status)) + return; + + IWL_DEBUG_SCAN(priv, "Completed scan.\n"); + + cancel_delayed_work(&priv->scan_check); + + aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); + if (aborted) + IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); + + if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); + goto out_settings; + } + + if (priv->scan_type == IWL_SCAN_ROC) + iwl_scan_roc_expired(priv); + + if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { + int err; + + /* Check if mac80211 requested scan during our internal scan */ + if (priv->scan_request == NULL) + goto out_complete; + + /* If so request a new scan */ + err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, + priv->scan_request->channels[0]->band); + if (err) { + IWL_DEBUG_SCAN(priv, + "failed to initiate pending scan: %d\n", err); + aborted = true; + goto out_complete; + } + + return; + } + +out_complete: + iwl_complete_scan(priv, aborted); + +out_settings: + /* Can we still talk to firmware ? */ + if (!iwl_is_ready_rf(priv)) + return; + + iwlagn_post_scan(priv); +} + +void iwl_force_scan_end(struct iwl_priv *priv) +{ + lockdep_assert_held(&priv->mutex); + + if (!test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); + return; + } + + IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); + clear_bit(STATUS_SCANNING, &priv->status); + clear_bit(STATUS_SCAN_HW, &priv->status); + clear_bit(STATUS_SCAN_ABORTING, &priv->status); + clear_bit(STATUS_SCAN_COMPLETE, &priv->status); + iwl_complete_scan(priv, true); +} + +static void iwl_do_scan_abort(struct iwl_priv *priv) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (!test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); + return; + } + + if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); + return; + } + + ret = iwl_send_scan_abort(priv); + if (ret) { + IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); + iwl_force_scan_end(priv); + } else + IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); +} + +/** + * iwl_scan_cancel - Cancel any currently executing HW scan + */ +int iwl_scan_cancel(struct iwl_priv *priv) +{ + IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); + queue_work(priv->workqueue, &priv->abort_scan); + return 0; +} + +/** + * iwl_scan_cancel_timeout - Cancel any currently executing HW scan + * @ms: amount of time to wait (in milliseconds) for scan to abort + * + */ +void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(ms); + + lockdep_assert_held(&priv->mutex); + + IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); + + iwl_do_scan_abort(priv); + + while (time_before_eq(jiffies, timeout)) { + if (!test_bit(STATUS_SCAN_HW, &priv->status)) + goto finished; + msleep(20); + } + + return; + + finished: + /* + * Now STATUS_SCAN_HW is clear. This means that the + * device finished, but the background work is going + * to execute at best as soon as we release the mutex. + * Since we need to be able to issue a new scan right + * after this function returns, run the complete here. + * The STATUS_SCAN_COMPLETE bit will then be cleared + * and prevent the background work from "completing" + * a possible new scan. + */ + iwl_process_scan_complete(priv); +} + +/* Service response to REPLY_SCAN_CMD (0x80) */ +static int iwl_rx_reply_scan(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanreq_notification *notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); +#endif + return 0; +} + +/* Service SCAN_START_NOTIFICATION (0x82) */ +static int iwl_rx_scan_start_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanstart_notification *notif = (void *)pkt->data; + + priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); + IWL_DEBUG_SCAN(priv, "Scan start: " + "%d [802.11%s] " + "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", + notif->channel, + notif->band ? "bg" : "a", + le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), + notif->status, notif->beacon_timer); + + if (priv->scan_type == IWL_SCAN_ROC && + !priv->hw_roc_start_notified) { + ieee80211_ready_on_channel(priv->hw); + priv->hw_roc_start_notified = true; + } + + return 0; +} + +/* Service SCAN_RESULTS_NOTIFICATION (0x83) */ +static int iwl_rx_scan_results_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanresults_notification *notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(priv, "Scan ch.res: " + "%d [802.11%s] " + "probe status: %u:%u " + "(TSF: 0x%08X:%08X) - %d " + "elapsed=%lu usec\n", + notif->channel, + notif->band ? "bg" : "a", + notif->probe_status, notif->num_probe_not_sent, + le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), + le32_to_cpu(notif->statistics[0]), + le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); +#endif + return 0; +} + +/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ +static int iwl_rx_scan_complete_notif(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", + scan_notif->scanned_channels, + scan_notif->tsf_low, + scan_notif->tsf_high, scan_notif->status); + + IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", + (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", + jiffies_to_msecs(jiffies - priv->scan_start)); + + /* + * When aborting, we run the scan completed background work inline + * and the background work must then do nothing. The SCAN_COMPLETE + * bit helps implement that logic and thus needs to be set before + * queueing the work. Also, since the scan abort waits for SCAN_HW + * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW + * to avoid a race there. + */ + set_bit(STATUS_SCAN_COMPLETE, &priv->status); + clear_bit(STATUS_SCAN_HW, &priv->status); + queue_work(priv->workqueue, &priv->scan_completed); + + if (priv->iw_mode != NL80211_IFTYPE_ADHOC && + iwl_advanced_bt_coexist(priv) && + priv->bt_status != scan_notif->bt_status) { + if (scan_notif->bt_status) { + /* BT on */ + if (!priv->bt_ch_announce) + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + /* + * otherwise, no traffic load information provided + * no changes made + */ + } else { + /* BT off */ + priv->bt_traffic_load = + IWL_BT_COEX_TRAFFIC_LOAD_NONE; + } + priv->bt_status = scan_notif->bt_status; + queue_work(priv->workqueue, + &priv->bt_traffic_change_work); + } + return 0; +} + +void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) +{ + /* scan handlers */ + priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan; + priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif; + priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = + iwl_rx_scan_results_notif; + priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = + iwl_rx_scan_complete_notif; +} + +static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, u8 n_probes) +{ + if (band == IEEE80211_BAND_5GHZ) + return IWL_ACTIVE_DWELL_TIME_52 + + IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); + else + return IWL_ACTIVE_DWELL_TIME_24 + + IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); +} + +static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) +{ + struct iwl_rxon_context *ctx; + + /* + * If we're associated, we clamp the dwell time 98% + * of the smallest beacon interval (minus 2 * channel + * tune time) + */ + for_each_context(priv, ctx) { + u16 value; + + switch (ctx->staging.dev_type) { + case RXON_DEV_TYPE_P2P: + /* no timing constraints */ + continue; + case RXON_DEV_TYPE_ESS: + default: + /* timing constraints if associated */ + if (!iwl_is_associated_ctx(ctx)) + continue; + break; + case RXON_DEV_TYPE_CP: + case RXON_DEV_TYPE_2STA: + /* + * These seem to always have timers for TBTT + * active in uCode even when not associated yet. + */ + break; + } + + value = ctx->beacon_int; + if (!value) + value = IWL_PASSIVE_DWELL_BASE; + value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + dwell_time = min(value, dwell_time); + } + + return dwell_time; +} + +static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band) +{ + u16 passive = (band == IEEE80211_BAND_2GHZ) ? + IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : + IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; + + return iwl_limit_dwell(priv, passive); +} + +/* Return valid, unused, channel for a passive scan to reset the RF */ +static u8 iwl_get_single_channel_number(struct iwl_priv *priv, + enum ieee80211_band band) +{ + struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; + struct iwl_rxon_context *ctx; + int i; + + for (i = 0; i < sband->n_channels; i++) { + bool busy = false; + + for_each_context(priv, ctx) { + busy = sband->channels[i].hw_value == + le16_to_cpu(ctx->staging.channel); + if (busy) + break; + } + + if (busy) + continue; + + if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) + return sband->channels[i].hw_value; + } + + return 0; +} + +static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + struct iwl_scan_channel *scan_ch) +{ + const struct ieee80211_supported_band *sband; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added = 0; + u16 channel = 0; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) { + IWL_ERR(priv, "invalid band\n"); + return added; + } + + active_dwell = iwl_get_active_dwell_time(priv, band, 0); + passive_dwell = iwl_get_passive_dwell_time(priv, band); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + channel = iwl_get_single_channel_number(priv, band); + if (channel) { + scan_ch->channel = cpu_to_le16(channel); + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + added++; + } else + IWL_ERR(priv, "no valid channel found\n"); + return added; +} + +static int iwl_get_channels_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + u8 is_active, u8 n_probes, + struct iwl_scan_channel *scan_ch) +{ + struct ieee80211_channel *chan; + const struct ieee80211_supported_band *sband; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + u16 channel; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) + return 0; + + active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); + passive_dwell = iwl_get_passive_dwell_time(priv, band); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) + continue; + + channel = chan->hw_value; + scan_ch->channel = cpu_to_le16(channel); + + if (!is_active || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + else + scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; + + if (n_probes) + scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); + + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", + channel, le32_to_cpu(scan_ch->type), + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + "ACTIVE" : "PASSIVE", + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + active_dwell : passive_dwell); + + scan_ch++; + added++; + } + + IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); + return added; +} + +/** + * iwl_fill_probe_req - fill in all required fields and IE for probe request + */ + +static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, + const u8 *ies, int ie_len, const u8 *ssid, + u8 ssid_len, int left) +{ + int len = 0; + u8 *pos = NULL; + + /* Make sure there is enough space for the probe request, + * two mandatory IEs and the data */ + left -= 24; + if (left < 0) + return 0; + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + memcpy(frame->da, iwl_bcast_addr, ETH_ALEN); + memcpy(frame->sa, ta, ETH_ALEN); + memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN); + frame->seq_ctrl = 0; + + len += 24; + + /* ...next IE... */ + pos = &frame->u.probe_req.variable[0]; + + /* fill in our SSID IE */ + left -= ssid_len + 2; + if (left < 0) + return 0; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + if (ssid && ssid_len) { + memcpy(pos, ssid, ssid_len); + pos += ssid_len; + } + + len += ssid_len + 2; + + if (WARN_ON(left < ie_len)) + return len; + + if (ies && ie_len) { + memcpy(pos, ies, ie_len); + len += ie_len; + } + + return (u16)len; +} + +static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_CMD, + .len = { sizeof(struct iwl_scan_cmd), }, + .flags = CMD_SYNC, + }; + struct iwl_scan_cmd *scan; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u32 rate_flags = 0; + u16 cmd_len = 0; + u16 rx_chain = 0; + enum ieee80211_band band; + u8 n_probes = 0; + u8 rx_ant = priv->hw_params.valid_rx_ant; + u8 rate; + bool is_active = false; + int chan_mod; + u8 active_chains; + u8 scan_tx_antennas = priv->hw_params.valid_tx_ant; + int ret; + int scan_cmd_size = sizeof(struct iwl_scan_cmd) + + MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + + priv->fw->ucode_capa.max_probe_length; + const u8 *ssid = NULL; + u8 ssid_len = 0; + + if (WARN_ON_ONCE(priv->scan_request && + priv->scan_request->n_channels > MAX_SCAN_CHANNEL)) + return -EINVAL; + + lockdep_assert_held(&priv->mutex); + + if (vif) + ctx = iwl_rxon_ctx_from_vif(vif); + + if (!priv->scan_cmd) { + priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL); + if (!priv->scan_cmd) { + IWL_DEBUG_SCAN(priv, + "fail to allocate memory for scan\n"); + return -ENOMEM; + } + } + scan = priv->scan_cmd; + memset(scan, 0, scan_cmd_size); + + scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; + scan->quiet_time = IWL_ACTIVE_QUIET_TIME; + + if (priv->scan_type != IWL_SCAN_ROC && + iwl_is_any_associated(priv)) { + u16 interval = 0; + u32 extra; + u32 suspend_time = 100; + u32 scan_suspend_time = 100; + + IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); + switch (priv->scan_type) { + case IWL_SCAN_ROC: + WARN_ON(1); + break; + case IWL_SCAN_RADIO_RESET: + interval = 0; + break; + case IWL_SCAN_NORMAL: + interval = vif->bss_conf.beacon_int; + break; + } + + scan->suspend_time = 0; + scan->max_out_time = cpu_to_le32(200 * 1024); + if (!interval) + interval = suspend_time; + + extra = (suspend_time / interval) << 22; + scan_suspend_time = (extra | + ((suspend_time % interval) * 1024)); + scan->suspend_time = cpu_to_le32(scan_suspend_time); + IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", + scan_suspend_time, interval); + } else if (priv->scan_type == IWL_SCAN_ROC) { + scan->suspend_time = 0; + scan->max_out_time = 0; + scan->quiet_time = 0; + scan->quiet_plcp_th = 0; + } + + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); + break; + case IWL_SCAN_NORMAL: + if (priv->scan_request->n_ssids) { + int i, p = 0; + IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); + /* + * The highest priority SSID is inserted to the + * probe request template. + */ + ssid_len = priv->scan_request->ssids[0].ssid_len; + ssid = priv->scan_request->ssids[0].ssid; + + /* + * Invert the order of ssids, the firmware will invert + * it back. + */ + for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) { + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + priv->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + priv->scan_request->ssids[i].ssid, + priv->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } + is_active = true; + } else + IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); + break; + case IWL_SCAN_ROC: + IWL_DEBUG_SCAN(priv, "Start ROC scan.\n"); + break; + } + + scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; + scan->tx_cmd.sta_id = ctx->bcast_sta_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + switch (priv->scan_band) { + case IEEE80211_BAND_2GHZ: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + chan_mod = le32_to_cpu( + priv->contexts[IWL_RXON_CTX_BSS].active.flags & + RXON_FLG_CHANNEL_MODE_MSK) + >> RXON_FLG_CHANNEL_MODE_POS; + if ((priv->scan_request && priv->scan_request->no_cck) || + chan_mod == CHANNEL_MODE_PURE_40) { + rate = IWL_RATE_6M_PLCP; + } else { + rate = IWL_RATE_1M_PLCP; + rate_flags = RATE_MCS_CCK_MSK; + } + /* + * Internal scans are passive, so we can indiscriminately set + * the BT ignore flag on 2.4 GHz since it applies to TX only. + */ + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) + scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; + break; + case IEEE80211_BAND_5GHZ: + rate = IWL_RATE_6M_PLCP; + break; + default: + IWL_WARN(priv, "Invalid scan band\n"); + return -EIO; + } + + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + * + * This was fixed in later versions along with some other + * scan changes, and the threshold behaves as a flag in those + * versions. + */ + if (priv->new_scan_threshold_behaviour) + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_DISABLED; + else + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_NEVER; + + band = priv->scan_band; + + if (band == IEEE80211_BAND_2GHZ && + priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + /* transmit 2.4 GHz probes only on first antenna */ + scan_tx_antennas = first_antenna(scan_tx_antennas); + } + + priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, + priv->scan_tx_ant[band], + scan_tx_antennas); + rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); + scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); + + /* + * In power save mode while associated use one chain, + * otherwise use all chains + */ + if (test_bit(STATUS_POWER_PMI, &priv->status) && + !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { + /* rx_ant has been set to all valid chains previously */ + active_chains = rx_ant & + ((u8)(priv->chain_noise_data.active_chains)); + if (!active_chains) + active_chains = rx_ant; + + IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", + priv->chain_noise_data.active_chains); + + rx_ant = first_antenna(active_chains); + } + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist && + priv->bt_full_concurrent) { + /* operated as 1x1 in full concurrency mode */ + rx_ant = first_antenna(rx_ant); + } + + /* MIMO is not used here, but value is required */ + rx_chain |= + priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; + rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; + scan->rx_chain = cpu_to_le16(rx_chain); + switch (priv->scan_type) { + case IWL_SCAN_NORMAL: + cmd_len = iwl_fill_probe_req( + (struct ieee80211_mgmt *)scan->data, + vif->addr, + priv->scan_request->ie, + priv->scan_request->ie_len, + ssid, ssid_len, + scan_cmd_size - sizeof(*scan)); + break; + case IWL_SCAN_RADIO_RESET: + case IWL_SCAN_ROC: + /* use bcast addr, will not be transmitted but must be valid */ + cmd_len = iwl_fill_probe_req( + (struct ieee80211_mgmt *)scan->data, + iwl_bcast_addr, NULL, 0, + NULL, 0, + scan_cmd_size - sizeof(*scan)); + break; + default: + BUG(); + } + scan->tx_cmd.len = cpu_to_le16(cmd_len); + + scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | + RXON_FILTER_BCON_AWARE_MSK); + + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + scan->channel_count = + iwl_get_single_channel_for_scan(priv, vif, band, + (void *)&scan->data[cmd_len]); + break; + case IWL_SCAN_NORMAL: + scan->channel_count = + iwl_get_channels_for_scan(priv, vif, band, + is_active, n_probes, + (void *)&scan->data[cmd_len]); + break; + case IWL_SCAN_ROC: { + struct iwl_scan_channel *scan_ch; + int n_chan, i; + u16 dwell; + + dwell = iwl_limit_dwell(priv, priv->hw_roc_duration); + n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell); + + scan->channel_count = n_chan; + + scan_ch = (void *)&scan->data[cmd_len]; + + for (i = 0; i < n_chan; i++) { + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->channel = + cpu_to_le16(priv->hw_roc_channel->hw_value); + + if (i == n_chan - 1) + dwell = priv->hw_roc_duration - i * dwell; + + scan_ch->active_dwell = + scan_ch->passive_dwell = cpu_to_le16(dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + scan_ch++; + } + } + + break; + } + + if (scan->channel_count == 0) { + IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); + return -EIO; + } + + cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + + scan->channel_count * sizeof(struct iwl_scan_channel); + cmd.data[0] = scan; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + scan->len = cpu_to_le16(cmd.len[0]); + + /* set scan bit here for PAN params */ + set_bit(STATUS_SCAN_HW, &priv->status); + + ret = iwlagn_set_pan_params(priv); + if (ret) { + clear_bit(STATUS_SCAN_HW, &priv->status); + return ret; + } + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) { + clear_bit(STATUS_SCAN_HW, &priv->status); + iwlagn_set_pan_params(priv); + } + + return ret; +} + +void iwl_init_scan_params(struct iwl_priv *priv) +{ + u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1; + if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) + priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; + if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) + priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; +} + +int __must_check iwl_scan_initiate(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum iwl_scan_type scan_type, + enum ieee80211_band band) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + cancel_delayed_work(&priv->scan_check); + + if (!iwl_is_ready_rf(priv)) { + IWL_WARN(priv, "Request scan called when driver not ready.\n"); + return -EIO; + } + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + IWL_DEBUG_SCAN(priv, + "Multiple concurrent scan requests in parallel.\n"); + return -EBUSY; + } + + if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); + return -EBUSY; + } + + IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", + scan_type == IWL_SCAN_NORMAL ? "" : + scan_type == IWL_SCAN_ROC ? "remain-on-channel " : + "internal short "); + + set_bit(STATUS_SCANNING, &priv->status); + priv->scan_type = scan_type; + priv->scan_start = jiffies; + priv->scan_band = band; + + ret = iwlagn_request_scan(priv, vif); + if (ret) { + clear_bit(STATUS_SCANNING, &priv->status); + priv->scan_type = IWL_SCAN_NORMAL; + return ret; + } + + queue_delayed_work(priv->workqueue, &priv->scan_check, + IWL_SCAN_CHECK_WATCHDOG); + + return 0; +} + + +/* + * internal short scan, this function should only been called while associated. + * It will reset and tune the radio to prevent possible RF related problem + */ +void iwl_internal_short_hw_scan(struct iwl_priv *priv) +{ + queue_work(priv->workqueue, &priv->start_internal_scan); +} + +static void iwl_bg_start_internal_scan(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, start_internal_scan); + + IWL_DEBUG_SCAN(priv, "Start internal scan\n"); + + mutex_lock(&priv->mutex); + + if (priv->scan_type == IWL_SCAN_RADIO_RESET) { + IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); + goto unlock; + } + + if (test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); + goto unlock; + } + + if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) + IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); + unlock: + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_scan_check(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, scan_check.work); + + IWL_DEBUG_SCAN(priv, "Scan check work\n"); + + /* Since we are here firmware does not finish scan and + * most likely is in bad shape, so we don't bother to + * send abort command, just force scan complete to mac80211 */ + mutex_lock(&priv->mutex); + iwl_force_scan_end(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_abort_scan(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); + + IWL_DEBUG_SCAN(priv, "Abort scan work\n"); + + /* We keep scan_check work queued in case when firmware will not + * report back scan completed notification */ + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, 200); + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_scan_completed(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, scan_completed); + + mutex_lock(&priv->mutex); + iwl_process_scan_complete(priv); + mutex_unlock(&priv->mutex); +} + +void iwl_setup_scan_deferred_work(struct iwl_priv *priv) +{ + INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); + INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); + INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); + INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); +} + +void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) +{ + cancel_work_sync(&priv->start_internal_scan); + cancel_work_sync(&priv->abort_scan); + cancel_work_sync(&priv->scan_completed); + + if (cancel_delayed_work_sync(&priv->scan_check)) { + mutex_lock(&priv->mutex); + iwl_force_scan_end(priv); + mutex_unlock(&priv->mutex); + } +} + +void iwl_scan_roc_expired(struct iwl_priv *priv) +{ + /* + * The status bit should be set here, to prevent a race + * where the atomic_read returns 1, but before the execution continues + * iwl_scan_offchannel_skb_status() checks if the status bit is set + */ + set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); + + if (atomic_read(&priv->num_aux_in_flight) == 0) { + ieee80211_remain_on_channel_expired(priv->hw); + priv->hw_roc_channel = NULL; + schedule_delayed_work(&priv->hw_roc_disable_work, + 10 * HZ); + + clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); + } else { + IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n", + atomic_read(&priv->num_aux_in_flight)); + } +} + +void iwl_scan_offchannel_skb(struct iwl_priv *priv) +{ + WARN_ON(!priv->hw_roc_start_notified); + atomic_inc(&priv->num_aux_in_flight); +} + +void iwl_scan_offchannel_skb_status(struct iwl_priv *priv) +{ + if (atomic_dec_return(&priv->num_aux_in_flight) == 0 && + test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) { + IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n"); + iwl_scan_roc_expired(priv); + } +} diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c new file mode 100644 index 000000000000..c6b343fdf5f9 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -0,0 +1,1479 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include "iwl-trans.h" +#include "dev.h" +#include "agn.h" + +const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) +{ + lockdep_assert_held(&priv->sta_lock); + + if (sta_id >= IWLAGN_STATION_COUNT) { + IWL_ERR(priv, "invalid sta_id %u", sta_id); + return -EINVAL; + } + if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) + IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " + "addr %pM\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + + if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_ASSOC(priv, + "STA id %u addr %pM already present in uCode " + "(according to driver)\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } else { + priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } + return 0; +} + +static int iwl_process_add_sta_resp(struct iwl_priv *priv, + struct iwl_addsta_cmd *addsta, + struct iwl_rx_packet *pkt) +{ + struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; + u8 sta_id = addsta->sta.sta_id; + int ret = -EIO; + + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", + pkt->hdr.flags); + return ret; + } + + IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n", + sta_id); + + spin_lock(&priv->sta_lock); + + switch (add_sta_resp->status) { + case ADD_STA_SUCCESS_MSK: + IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); + ret = iwl_sta_ucode_activate(priv, sta_id); + break; + case ADD_STA_NO_ROOM_IN_TABLE: + IWL_ERR(priv, "Adding station %d failed, no room in table.\n", + sta_id); + break; + case ADD_STA_NO_BLOCK_ACK_RESOURCE: + IWL_ERR(priv, "Adding station %d failed, no block ack " + "resource.\n", sta_id); + break; + case ADD_STA_MODIFY_NON_EXIST_STA: + IWL_ERR(priv, "Attempting to modify non-existing station %d\n", + sta_id); + break; + default: + IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", + add_sta_resp->status); + break; + } + + IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", + priv->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", + sta_id, priv->stations[sta_id].sta.sta.addr); + + /* + * XXX: The MAC address in the command buffer is often changed from + * the original sent to the device. That is, the MAC address + * written to the command buffer often is not the same MAC address + * read from the command buffer when the command returns. This + * issue has not yet been resolved and this debugging is left to + * observe the problem. + */ + IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", + priv->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", + addsta->sta.addr); + spin_unlock(&priv->sta_lock); + + return ret; +} + +int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_addsta_cmd *addsta = + (struct iwl_addsta_cmd *) cmd->payload; + + return iwl_process_add_sta_resp(priv, addsta, pkt); +} + +int iwl_send_add_sta(struct iwl_priv *priv, + struct iwl_addsta_cmd *sta, u8 flags) +{ + int ret = 0; + struct iwl_host_cmd cmd = { + .id = REPLY_ADD_STA, + .flags = flags, + .data = { sta, }, + .len = { sizeof(*sta), }, + }; + u8 sta_id __maybe_unused = sta->sta.sta_id; + + IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", + sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); + + if (!(flags & CMD_ASYNC)) { + cmd.flags |= CMD_WANT_SKB; + might_sleep(); + } + + ret = iwl_dvm_send_cmd(priv, &cmd); + + if (ret || (flags & CMD_ASYNC)) + return ret; + /*else the command was successfully sent in SYNC mode, need to free + * the reply page */ + + iwl_free_resp(&cmd); + + if (cmd.handler_status) + IWL_ERR(priv, "%s - error in the CMD response %d", __func__, + cmd.handler_status); + + return cmd.handler_status; +} + +bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta_ht_cap *ht_cap) +{ + if (!ctx->ht.enabled || !ctx->ht.is_40mhz) + return false; + + /* + * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * the bit will not set if it is pure 40MHz case + */ + if (ht_cap && !ht_cap->ht_supported) + return false; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (priv->disable_ht40) + return false; +#endif + + return true; +} + +static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct iwl_rxon_context *ctx, + __le32 *flags, __le32 *mask) +{ + struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; + u8 mimo_ps_mode; + + *mask = STA_FLG_RTS_MIMO_PROT_MSK | + STA_FLG_MIMO_DIS_MSK | + STA_FLG_HT40_EN_MSK | + STA_FLG_MAX_AGG_SIZE_MSK | + STA_FLG_AGG_MPDU_DENSITY_MSK; + *flags = 0; + + if (!sta || !sta_ht_inf->ht_supported) + return; + + mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; + + IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", + sta->addr, + (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? + "static" : + (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? + "dynamic" : "disabled"); + + switch (mimo_ps_mode) { + case WLAN_HT_CAP_SM_PS_STATIC: + *flags |= STA_FLG_MIMO_DIS_MSK; + break; + case WLAN_HT_CAP_SM_PS_DYNAMIC: + *flags |= STA_FLG_RTS_MIMO_PROT_MSK; + break; + case WLAN_HT_CAP_SM_PS_DISABLED: + break; + default: + IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); + break; + } + + *flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); + + *flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); + + if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + *flags |= STA_FLG_HT40_EN_MSK; +} + +int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_sta *sta) +{ + u8 sta_id = iwl_sta_id(sta); + __le32 flags, mask; + struct iwl_addsta_cmd cmd; + + if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.station_flags &= ~mask; + priv->stations[sta_id].sta.station_flags |= flags; + spin_unlock_bh(&priv->sta_lock); + + memset(&cmd, 0, sizeof(cmd)); + cmd.mode = STA_CONTROL_MODIFY_MSK; + cmd.station_flags_msk = mask; + cmd.station_flags = flags; + cmd.sta.sta_id = sta_id; + + return iwl_send_add_sta(priv, &cmd, CMD_SYNC); +} + +static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, + struct ieee80211_sta *sta, + struct iwl_rxon_context *ctx) +{ + __le32 flags, mask; + + iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); + + lockdep_assert_held(&priv->sta_lock); + priv->stations[index].sta.station_flags &= ~mask; + priv->stations[index].sta.station_flags |= flags; +} + +/** + * iwl_prep_station - Prepare station information for addition + * + * should be called with sta_lock held + */ +u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, struct ieee80211_sta *sta) +{ + struct iwl_station_entry *station; + int i; + u8 sta_id = IWL_INVALID_STATION; + + if (is_ap) + sta_id = ctx->ap_sta_id; + else if (is_broadcast_ether_addr(addr)) + sta_id = ctx->bcast_sta_id; + else + for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { + if (ether_addr_equal(priv->stations[i].sta.sta.addr, + addr)) { + sta_id = i; + break; + } + + if (!priv->stations[i].used && + sta_id == IWL_INVALID_STATION) + sta_id = i; + } + + /* + * These two conditions have the same outcome, but keep them + * separate + */ + if (unlikely(sta_id == IWL_INVALID_STATION)) + return sta_id; + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being " + "added.\n", sta_id); + return sta_id; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && + ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " + "adding again.\n", sta_id, addr); + return sta_id; + } + + station = &priv->stations[sta_id]; + station->used = IWL_STA_DRIVER_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", + sta_id, addr); + priv->num_stations++; + + /* Set up the REPLY_ADD_STA command to send to device */ + memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); + memcpy(station->sta.sta.addr, addr, ETH_ALEN); + station->sta.mode = 0; + station->sta.sta.sta_id = sta_id; + station->sta.station_flags = ctx->station_flags; + station->ctxid = ctx->ctxid; + + if (sta) { + struct iwl_station_priv *sta_priv; + + sta_priv = (void *)sta->drv_priv; + sta_priv->ctx = ctx; + } + + /* + * OK to call unconditionally, since local stations (IBSS BSSID + * STA and broadcast STA) pass in a NULL sta, and mac80211 + * doesn't allow HT IBSS. + */ + iwl_set_ht_add_station(priv, sta_id, sta, ctx); + + return sta_id; + +} + +#define STA_WAIT_TIMEOUT (HZ/2) + +/** + * iwl_add_station_common - + */ +int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r) +{ + int ret = 0; + u8 sta_id; + struct iwl_addsta_cmd sta_cmd; + + *sta_id_r = 0; + spin_lock_bh(&priv->sta_lock); + sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare station %pM for addition\n", + addr); + spin_unlock_bh(&priv->sta_lock); + return -EINVAL; + } + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being " + "added.\n", sta_id); + spin_unlock_bh(&priv->sta_lock); + return -EEXIST; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " + "adding again.\n", sta_id, addr); + spin_unlock_bh(&priv->sta_lock); + return -EEXIST; + } + + priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + /* Add station to device's station table */ + ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + if (ret) { + spin_lock_bh(&priv->sta_lock); + IWL_ERR(priv, "Adding station %pM failed.\n", + priv->stations[sta_id].sta.sta.addr); + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_bh(&priv->sta_lock); + } + *sta_id_r = sta_id; + return ret; +} + +/** + * iwl_sta_ucode_deactivate - deactivate ucode status for a station + */ +static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) +{ + lockdep_assert_held(&priv->sta_lock); + + /* Ucode must be active and driver must be non active */ + if ((priv->stations[sta_id].used & + (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != + IWL_STA_UCODE_ACTIVE) + IWL_ERR(priv, "removed non active STA %u\n", sta_id); + + priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; + + memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); + IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); +} + +static int iwl_send_remove_station(struct iwl_priv *priv, + const u8 *addr, int sta_id, + bool temporary) +{ + struct iwl_rx_packet *pkt; + int ret; + struct iwl_rem_sta_cmd rm_sta_cmd; + + struct iwl_host_cmd cmd = { + .id = REPLY_REMOVE_STA, + .len = { sizeof(struct iwl_rem_sta_cmd), }, + .flags = CMD_SYNC, + .data = { &rm_sta_cmd, }, + }; + + memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); + rm_sta_cmd.num_sta = 1; + memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); + + cmd.flags |= CMD_WANT_SKB; + + ret = iwl_dvm_send_cmd(priv, &cmd); + + if (ret) + return ret; + + pkt = cmd.resp_pkt; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", + pkt->hdr.flags); + ret = -EIO; + } + + if (!ret) { + struct iwl_rem_sta_resp *rem_sta_resp = (void *)pkt->data; + switch (rem_sta_resp->status) { + case REM_STA_SUCCESS_MSK: + if (!temporary) { + spin_lock_bh(&priv->sta_lock); + iwl_sta_ucode_deactivate(priv, sta_id); + spin_unlock_bh(&priv->sta_lock); + } + IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); + break; + default: + ret = -EIO; + IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); + break; + } + } + iwl_free_resp(&cmd); + + return ret; +} + +/** + * iwl_remove_station - Remove driver's knowledge of station. + */ +int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr) +{ + u8 tid; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready.\n", + addr); + /* + * It is typical for stations to be removed when we are + * going down. Return success since device will be down + * soon anyway + */ + return 0; + } + + IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", + sta_id, addr); + + if (WARN_ON(sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + spin_lock_bh(&priv->sta_lock); + + if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { + IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", + addr); + goto out_err; + } + + if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", + addr); + goto out_err; + } + + if (priv->stations[sta_id].used & IWL_STA_LOCAL) { + kfree(priv->stations[sta_id].lq); + priv->stations[sta_id].lq = NULL; + } + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + memset(&priv->tid_data[sta_id][tid], 0, + sizeof(priv->tid_data[sta_id][tid])); + + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + + priv->num_stations--; + + if (WARN_ON(priv->num_stations < 0)) + priv->num_stations = 0; + + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_remove_station(priv, addr, sta_id, false); +out_err: + spin_unlock_bh(&priv->sta_lock); + return -EINVAL; +} + +void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr) +{ + u8 tid; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready.\n", + addr); + return; + } + + IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); + + if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) + return; + + spin_lock_bh(&priv->sta_lock); + + WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + memset(&priv->tid_data[sta_id][tid], 0, + sizeof(priv->tid_data[sta_id][tid])); + + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + + priv->num_stations--; + + if (WARN_ON_ONCE(priv->num_stations < 0)) + priv->num_stations = 0; + + spin_unlock_bh(&priv->sta_lock); +} + +static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + u8 sta_id, struct iwl_link_quality_cmd *link_cmd) +{ + int i, r; + u32 rate_flags = 0; + __le32 rate_n_flags; + + lockdep_assert_held(&priv->mutex); + + memset(link_cmd, 0, sizeof(*link_cmd)); + + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (priv->band == IEEE80211_BAND_5GHZ) + r = IWL_RATE_6M_INDEX; + else if (ctx && ctx->vif && ctx->vif->p2p) + r = IWL_RATE_6M_INDEX; + else + r = IWL_RATE_1M_INDEX; + + if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << + RATE_MCS_ANT_POS; + rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + link_cmd->rs_table[i].rate_n_flags = rate_n_flags; + + link_cmd->general_params.single_stream_ant_msk = + first_antenna(priv->hw_params.valid_tx_ant); + + link_cmd->general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant & + ~first_antenna(priv->hw_params.valid_tx_ant); + if (!link_cmd->general_params.dual_stream_ant_msk) { + link_cmd->general_params.dual_stream_ant_msk = ANT_AB; + } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { + link_cmd->general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant; + } + + link_cmd->agg_params.agg_dis_start_th = + LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + link_cmd->sta_id = sta_id; +} + +/** + * iwl_clear_ucode_stations - clear ucode station table bits + * + * This function clears all the bits in the driver indicating + * which stations are active in the ucode. Call when something + * other than explicit station management would cause this in + * the ucode, e.g. unassociated RXON. + */ +void iwl_clear_ucode_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int i; + bool cleared = false; + + IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); + + spin_lock_bh(&priv->sta_lock); + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if (ctx && ctx->ctxid != priv->stations[i].ctxid) + continue; + + if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_INFO(priv, + "Clearing ucode active for station %d\n", i); + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + cleared = true; + } + } + spin_unlock_bh(&priv->sta_lock); + + if (!cleared) + IWL_DEBUG_INFO(priv, + "No active stations found to be cleared\n"); +} + +/** + * iwl_restore_stations() - Restore driver known stations to device + * + * All stations considered active by driver, but not present in ucode, is + * restored. + * + * Function sleeps. + */ +void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + struct iwl_addsta_cmd sta_cmd; + struct iwl_link_quality_cmd lq; + int i; + bool found = false; + int ret; + bool send_lq; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Not ready yet, not restoring any stations.\n"); + return; + } + + IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); + spin_lock_bh(&priv->sta_lock); + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if (ctx->ctxid != priv->stations[i].ctxid) + continue; + if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && + !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].sta.mode = 0; + priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; + found = true; + } + } + + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { + memcpy(&sta_cmd, &priv->stations[i].sta, + sizeof(struct iwl_addsta_cmd)); + send_lq = false; + if (priv->stations[i].lq) { + if (priv->wowlan) + iwl_sta_fill_lq(priv, ctx, i, &lq); + else + memcpy(&lq, priv->stations[i].lq, + sizeof(struct iwl_link_quality_cmd)); + send_lq = true; + } + spin_unlock_bh(&priv->sta_lock); + ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + if (ret) { + spin_lock_bh(&priv->sta_lock); + IWL_ERR(priv, "Adding station %pM failed.\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].used &= + ~IWL_STA_DRIVER_ACTIVE; + priv->stations[i].used &= + ~IWL_STA_UCODE_INPROGRESS; + continue; + } + /* + * Rate scaling has already been initialized, send + * current LQ command + */ + if (send_lq) + iwl_send_lq_cmd(priv, ctx, &lq, + CMD_SYNC, true); + spin_lock_bh(&priv->sta_lock); + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + } + } + + spin_unlock_bh(&priv->sta_lock); + if (!found) + IWL_DEBUG_INFO(priv, "Restoring all known stations .... " + "no stations to be restored.\n"); + else + IWL_DEBUG_INFO(priv, "Restoring all known stations .... " + "complete.\n"); +} + +int iwl_get_free_ucode_key_offset(struct iwl_priv *priv) +{ + int i; + + for (i = 0; i < priv->sta_key_max_num; i++) + if (!test_and_set_bit(i, &priv->ucode_key_table)) + return i; + + return WEP_INVALID_OFFSET; +} + +void iwl_dealloc_bcast_stations(struct iwl_priv *priv) +{ + int i; + + spin_lock_bh(&priv->sta_lock); + for (i = 0; i < IWLAGN_STATION_COUNT; i++) { + if (!(priv->stations[i].used & IWL_STA_BCAST)) + continue; + + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + priv->num_stations--; + if (WARN_ON(priv->num_stations < 0)) + priv->num_stations = 0; + kfree(priv->stations[i].lq); + priv->stations[i].lq = NULL; + } + spin_unlock_bh(&priv->sta_lock); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +static void iwl_dump_lq_cmd(struct iwl_priv *priv, + struct iwl_link_quality_cmd *lq) +{ + int i; + IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); + IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", + lq->general_params.single_stream_ant_msk, + lq->general_params.dual_stream_ant_msk); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", + i, lq->rs_table[i].rate_n_flags); +} +#else +static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, + struct iwl_link_quality_cmd *lq) +{ +} +#endif + +/** + * is_lq_table_valid() - Test one aspect of LQ cmd for validity + * + * It sometimes happens when a HT rate has been in use and we + * loose connectivity with AP then mac80211 will first tell us that the + * current channel is not HT anymore before removing the station. In such a + * scenario the RXON flags will be updated to indicate we are not + * communicating HT anymore, but the LQ command may still contain HT rates. + * Test for this to prevent driver from sending LQ command between the time + * RXON flags are updated and when LQ command is updated. + */ +static bool is_lq_table_valid(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq) +{ + int i; + + if (ctx->ht.enabled) + return true; + + IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", + ctx->active.channel); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & + RATE_MCS_HT_MSK) { + IWL_DEBUG_INFO(priv, + "index %d of LQ expects HT channel\n", + i); + return false; + } + } + return true; +} + +/** + * iwl_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ +int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq, u8 flags, bool init) +{ + int ret = 0; + struct iwl_host_cmd cmd = { + .id = REPLY_TX_LINK_QUALITY_CMD, + .len = { sizeof(struct iwl_link_quality_cmd), }, + .flags = flags, + .data = { lq, }, + }; + + if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + + spin_lock_bh(&priv->sta_lock); + if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { + spin_unlock_bh(&priv->sta_lock); + return -EINVAL; + } + spin_unlock_bh(&priv->sta_lock); + + iwl_dump_lq_cmd(priv, lq); + if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) + return -EINVAL; + + if (is_lq_table_valid(priv, ctx, lq)) + ret = iwl_dvm_send_cmd(priv, &cmd); + else + ret = -EINVAL; + + if (cmd.flags & CMD_ASYNC) + return ret; + + if (init) { + IWL_DEBUG_INFO(priv, "init LQ command complete, " + "clearing sta addition status for sta %d\n", + lq->sta_id); + spin_lock_bh(&priv->sta_lock); + priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_bh(&priv->sta_lock); + } + return ret; +} + + +static struct iwl_link_quality_cmd * +iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + u8 sta_id) +{ + struct iwl_link_quality_cmd *link_cmd; + + link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); + if (!link_cmd) { + IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); + return NULL; + } + + iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); + + return link_cmd; +} + +/* + * iwlagn_add_bssid_station - Add the special IBSS BSSID station + * + * Function sleeps. + */ +int iwlagn_add_bssid_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr, u8 *sta_id_r) +{ + int ret; + u8 sta_id; + struct iwl_link_quality_cmd *link_cmd; + + if (sta_id_r) + *sta_id_r = IWL_INVALID_STATION; + + ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM\n", addr); + return ret; + } + + if (sta_id_r) + *sta_id_r = sta_id; + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].used |= IWL_STA_LOCAL; + spin_unlock_bh(&priv->sta_lock); + + /* Set up default rate scaling table in device's station table */ + link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for station %pM.\n", + addr); + return -ENOMEM; + } + + ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true); + if (ret) + IWL_ERR(priv, "Link quality command failed (%d)\n", ret); + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_bh(&priv->sta_lock); + + return 0; +} + +/* + * static WEP keys + * + * For each context, the device has a table of 4 static WEP keys + * (one for each key index) that is updated with the following + * commands. + */ + +static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + bool send_if_empty) +{ + int i, not_empty = 0; + u8 buff[sizeof(struct iwl_wep_cmd) + + sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; + struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; + size_t cmd_size = sizeof(struct iwl_wep_cmd); + struct iwl_host_cmd cmd = { + .id = ctx->wep_key_cmd, + .data = { wep_cmd, }, + .flags = CMD_SYNC, + }; + + might_sleep(); + + memset(wep_cmd, 0, cmd_size + + (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); + + for (i = 0; i < WEP_KEYS_MAX ; i++) { + wep_cmd->key[i].key_index = i; + if (ctx->wep_keys[i].key_size) { + wep_cmd->key[i].key_offset = i; + not_empty = 1; + } else { + wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; + } + + wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; + memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, + ctx->wep_keys[i].key_size); + } + + wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; + wep_cmd->num_keys = WEP_KEYS_MAX; + + cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; + + cmd.len[0] = cmd_size; + + if (not_empty || send_if_empty) + return iwl_dvm_send_cmd(priv, &cmd); + else + return 0; +} + +int iwl_restore_default_wep_keys(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + lockdep_assert_held(&priv->mutex); + + return iwl_send_static_wepkey_cmd(priv, ctx, false); +} + +int iwl_remove_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", + keyconf->keyidx); + + memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_WEP(priv, + "Not sending REPLY_WEPKEY command due to RFKILL.\n"); + /* but keys in device are clear anyway so return success */ + return 0; + } + ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); + IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", + keyconf->keyidx, ret); + + return ret; +} + +int iwl_set_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (keyconf->keylen != WEP_KEY_LEN_128 && + keyconf->keylen != WEP_KEY_LEN_64) { + IWL_DEBUG_WEP(priv, + "Bad WEP key length %d\n", keyconf->keylen); + return -EINVAL; + } + + keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; + + ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; + memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, + keyconf->keylen); + + ret = iwl_send_static_wepkey_cmd(priv, ctx, false); + IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", + keyconf->keylen, keyconf->keyidx, ret); + + return ret; +} + +/* + * dynamic (per-station) keys + * + * The dynamic keys are a little more complicated. The device has + * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. + * These are linked to stations by a table that contains an index + * into the key table for each station/key index/{mcast,unicast}, + * i.e. it's basically an array of pointers like this: + * key_offset_t key_mapping[NUM_STATIONS][4][2]; + * (it really works differently, but you can think of it as such) + * + * The key uploading and linking happens in the same command, the + * add station command with STA_MODIFY_KEY_MASK. + */ + +static u8 iwlagn_key_sta_id(struct iwl_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + if (sta) + return iwl_sta_id(sta); + + /* + * The device expects GTKs for station interfaces to be + * installed as GTKs for the AP station. If we have no + * station ID, then use the ap_sta_id in that case. + */ + if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) + return vif_priv->ctx->ap_sta_id; + + return IWL_INVALID_STATION; +} + +static int iwlagn_send_sta_key(struct iwl_priv *priv, + struct ieee80211_key_conf *keyconf, + u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, + u32 cmd_flags) +{ + __le16 key_flags; + struct iwl_addsta_cmd sta_cmd; + int i; + + spin_lock_bh(&priv->sta_lock); + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags |= STA_KEY_FLG_MAP_KEY_MSK; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + key_flags |= STA_KEY_FLG_CCMP; + memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); + break; + case WLAN_CIPHER_SUITE_TKIP: + key_flags |= STA_KEY_FLG_TKIP; + sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; + for (i = 0; i < 5; i++) + sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); + memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); + break; + case WLAN_CIPHER_SUITE_WEP104: + key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + key_flags |= STA_KEY_FLG_WEP; + memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); + break; + default: + WARN_ON(1); + return -EINVAL; + } + + if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + key_flags |= STA_KEY_MULTICAST_MSK; + + /* key pointer (offset) */ + sta_cmd.key.key_offset = keyconf->hw_key_idx; + + sta_cmd.key.key_flags = key_flags; + sta_cmd.mode = STA_CONTROL_MODIFY_MSK; + sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; + + return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); +} + +void iwl_update_tkip_key(struct iwl_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) +{ + u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); + + if (sta_id == IWL_INVALID_STATION) + return; + + if (iwl_scan_cancel(priv)) { + /* cancel scan failed, just live w/ bad key and rely + briefly on SW decryption */ + return; + } + + iwlagn_send_sta_key(priv, keyconf, sta_id, + iv32, phase1key, CMD_ASYNC); +} + +int iwl_remove_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta) +{ + struct iwl_addsta_cmd sta_cmd; + u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); + __le16 key_flags; + + /* if station isn't there, neither is the key */ + if (sta_id == IWL_INVALID_STATION) + return -ENOENT; + + spin_lock_bh(&priv->sta_lock); + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); + if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) + sta_id = IWL_INVALID_STATION; + spin_unlock_bh(&priv->sta_lock); + + if (sta_id == IWL_INVALID_STATION) + return 0; + + lockdep_assert_held(&priv->mutex); + + ctx->key_mapping_keys--; + + IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", + keyconf->keyidx, sta_id); + + if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) + IWL_ERR(priv, "offset %d not used in uCode key table.\n", + keyconf->hw_key_idx); + + key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | + STA_KEY_FLG_INVALID; + + if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + key_flags |= STA_KEY_MULTICAST_MSK; + + sta_cmd.key.key_flags = key_flags; + sta_cmd.key.key_offset = WEP_INVALID_OFFSET; + sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; + sta_cmd.mode = STA_CONTROL_MODIFY_MSK; + + return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +int iwl_set_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta) +{ + struct ieee80211_key_seq seq; + u16 p1k[5]; + int ret; + u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); + const u8 *addr; + + if (sta_id == IWL_INVALID_STATION) + return -EINVAL; + + lockdep_assert_held(&priv->mutex); + + keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); + if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) + return -ENOSPC; + + ctx->key_mapping_keys++; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (sta) + addr = sta->addr; + else /* station mode case only */ + addr = ctx->active.bssid_addr; + + /* pre-fill phase 1 key into device cache */ + ieee80211_get_key_rx_seq(keyconf, 0, &seq); + ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); + ret = iwlagn_send_sta_key(priv, keyconf, sta_id, + seq.tkip.iv32, p1k, CMD_SYNC); + break; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + ret = iwlagn_send_sta_key(priv, keyconf, sta_id, + 0, NULL, CMD_SYNC); + break; + default: + IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); + ret = -EINVAL; + } + + if (ret) { + ctx->key_mapping_keys--; + clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); + } + + IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, + sta ? sta->addr : NULL, ret); + + return ret; +} + +/** + * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. + * + * This adds the broadcast station into the driver's station table + * and marks it driver active, so that it will be restored to the + * device at the next best time. + */ +int iwlagn_alloc_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_link_quality_cmd *link_cmd; + u8 sta_id; + + spin_lock_bh(&priv->sta_lock); + sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare broadcast station\n"); + spin_unlock_bh(&priv->sta_lock); + + return -EINVAL; + } + + priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used |= IWL_STA_BCAST; + spin_unlock_bh(&priv->sta_lock); + + link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_bh(&priv->sta_lock); + + return 0; +} + +/** + * iwl_update_bcast_station - update broadcast station's LQ command + * + * Only used by iwlagn. Placed here to have all bcast station management + * code together. + */ +int iwl_update_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_link_quality_cmd *link_cmd; + u8 sta_id = ctx->bcast_sta_id; + + link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); + if (!link_cmd) { + IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_bh(&priv->sta_lock); + if (priv->stations[sta_id].lq) + kfree(priv->stations[sta_id].lq); + else + IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_bh(&priv->sta_lock); + + return 0; +} + +int iwl_update_bcast_stations(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int ret = 0; + + for_each_context(priv, ctx) { + ret = iwl_update_bcast_station(priv, ctx); + if (ret) + break; + } + + return ret; +} + +/** + * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table + */ +int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) +{ + struct iwl_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + /* Remove "disable" flag, to enable Tx for this TID */ + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; + priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid, u16 ssn) +{ + int sta_id; + struct iwl_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + sta_id = iwl_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) + return -ENXIO; + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.station_flags_msk = 0; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; + priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; + priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid) +{ + int sta_id; + struct iwl_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + sta_id = iwl_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_bh(&priv->sta_lock); + priv->stations[sta_id].sta.station_flags_msk = 0; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; + priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + spin_unlock_bh(&priv->sta_lock); + + return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + + + +void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) +{ + struct iwl_addsta_cmd cmd = { + .mode = STA_CONTROL_MODIFY_MSK, + .station_flags = STA_FLG_PWR_SAVE_MSK, + .station_flags_msk = STA_FLG_PWR_SAVE_MSK, + .sta.sta_id = sta_id, + .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, + .sleep_tx_count = cpu_to_le16(cnt), + }; + + iwl_send_add_sta(priv, &cmd, CMD_ASYNC); +} diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c new file mode 100644 index 000000000000..59cfe9a8eea5 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c @@ -0,0 +1,1113 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iwl-debug.h" +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-fh.h" +#include "iwl-prph.h" +#include "dev.h" +#include "agn.h" +#include "testmode.h" + + +/* Periphery registers absolute lower bound. This is used in order to + * differentiate registery access through HBUS_TARG_PRPH_* and + * HBUS_TARG_MEM_* accesses. + */ +#define IWL_TM_ABS_PRPH_START (0xA00000) + +/* The TLVs used in the gnl message policy between the kernel module and + * user space application. iwl_testmode_gnl_msg_policy is to be carried + * through the NL80211_CMD_TESTMODE channel regulated by nl80211. + * See testmode.h + */ +static +struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { + [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, }, + [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, }, + [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, }, + [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, }, + [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, }, + [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, }, + [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, + + [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, }, + [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, + [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, + [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, }, +}; + +/* + * See the struct iwl_rx_packet in commands.h for the format of the + * received events from the device + */ +static inline int get_event_length(struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + if (pkt) + return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + else + return 0; +} + + +/* + * This function multicasts the spontaneous messages from the device to the + * user space. It is invoked whenever there is a received messages + * from the device. This function is called within the ISR of the rx handlers + * in iwlagn driver. + * + * The parsing of the message content is left to the user space application, + * The message content is treated as unattacked raw data and is encapsulated + * with IWL_TM_ATTR_UCODE_RX_PKT multicasting to the user space. + * + * @priv: the instance of iwlwifi device + * @rxb: pointer to rx data content received by the ISR + * + * See the message policies and TLVs in iwl_testmode_gnl_msg_policy[]. + * For the messages multicasting to the user application, the mandatory + * TLV fields are : + * IWL_TM_ATTR_COMMAND must be IWL_TM_CMD_DEV2APP_UCODE_RX_PKT + * IWL_TM_ATTR_UCODE_RX_PKT for carrying the message content + */ + +static void iwl_testmode_ucode_rx_pkt(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb) +{ + struct ieee80211_hw *hw = priv->hw; + struct sk_buff *skb; + void *data; + int length; + + data = (void *)rxb_addr(rxb); + length = get_event_length(rxb); + + if (!data || length == 0) + return; + + skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length, + GFP_ATOMIC); + if (skb == NULL) { + IWL_ERR(priv, + "Run out of memory for messages to user space ?\n"); + return; + } + if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || + /* the length doesn't include len_n_flags field, so add it manually */ + nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length + sizeof(__le32), data)) + goto nla_put_failure; + cfg80211_testmode_event(skb, GFP_ATOMIC); + return; + +nla_put_failure: + kfree_skb(skb); + IWL_ERR(priv, "Ouch, overran buffer, check allocation!\n"); +} + +void iwl_testmode_init(struct iwl_priv *priv) +{ + priv->pre_rx_handler = NULL; + priv->testmode_trace.trace_enabled = false; + priv->testmode_mem.read_in_progress = false; +} + +static void iwl_mem_cleanup(struct iwl_priv *priv) +{ + if (priv->testmode_mem.read_in_progress) { + kfree(priv->testmode_mem.buff_addr); + priv->testmode_mem.buff_addr = NULL; + priv->testmode_mem.buff_size = 0; + priv->testmode_mem.num_chunks = 0; + priv->testmode_mem.read_in_progress = false; + } +} + +static void iwl_trace_cleanup(struct iwl_priv *priv) +{ + if (priv->testmode_trace.trace_enabled) { + if (priv->testmode_trace.cpu_addr && + priv->testmode_trace.dma_addr) + dma_free_coherent(priv->trans->dev, + priv->testmode_trace.total_size, + priv->testmode_trace.cpu_addr, + priv->testmode_trace.dma_addr); + priv->testmode_trace.trace_enabled = false; + priv->testmode_trace.cpu_addr = NULL; + priv->testmode_trace.trace_addr = NULL; + priv->testmode_trace.dma_addr = 0; + priv->testmode_trace.buff_size = 0; + priv->testmode_trace.total_size = 0; + } +} + + +void iwl_testmode_cleanup(struct iwl_priv *priv) +{ + iwl_trace_cleanup(priv); + iwl_mem_cleanup(priv); +} + + +/* + * This function handles the user application commands to the ucode. + * + * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_CMD_ID and + * IWL_TM_ATTR_UCODE_CMD_DATA and calls to the handler to send the + * host command to the ucode. + * + * If any mandatory field is missing, -ENOMSG is replied to the user space + * application; otherwise, waits for the host command to be sent and checks + * the return code. In case or error, it is returned, otherwise a reply is + * allocated and the reply RX packet + * is returned. + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_host_cmd cmd; + struct iwl_rx_packet *pkt; + struct sk_buff *skb; + void *reply_buf; + u32 reply_len; + int ret; + bool cmd_want_skb; + + memset(&cmd, 0, sizeof(struct iwl_host_cmd)); + + if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] || + !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) { + IWL_ERR(priv, "Missing ucode command mandatory fields\n"); + return -ENOMSG; + } + + cmd.flags = CMD_ON_DEMAND | CMD_SYNC; + cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]); + if (cmd_want_skb) + cmd.flags |= CMD_WANT_SKB; + + cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]); + cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); + cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + IWL_DEBUG_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x," + " len %d\n", cmd.id, cmd.flags, cmd.len[0]); + + ret = iwl_dvm_send_cmd(priv, &cmd); + if (ret) { + IWL_ERR(priv, "Failed to send hcmd\n"); + return ret; + } + if (!cmd_want_skb) + return ret; + + /* Handling return of SKB to the user */ + pkt = cmd.resp_pkt; + if (!pkt) { + IWL_ERR(priv, "HCMD received a null response packet\n"); + return ret; + } + + reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20); + reply_buf = kmalloc(reply_len, GFP_KERNEL); + if (!skb || !reply_buf) { + kfree_skb(skb); + kfree(reply_buf); + return -ENOMEM; + } + + /* The reply is in a page, that we cannot send to user space. */ + memcpy(reply_buf, &(pkt->hdr), reply_len); + iwl_free_resp(&cmd); + + if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || + nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) + goto nla_put_failure; + return cfg80211_testmode_reply(skb); + +nla_put_failure: + IWL_DEBUG_INFO(priv, "Failed creating NL attributes\n"); + return -ENOMSG; +} + + +/* + * This function handles the user application commands for register access. + * + * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the + * handlers respectively. + * + * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the + * mandatory fields(IWL_TM_ATTR_REG_OFFSET,IWL_TM_ATTR_REG_VALUE32, + * IWL_TM_ATTR_REG_VALUE8) are missing; Otherwise 0 is replied indicating + * the success of the command execution. + * + * If IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_READ32, the register read + * value is returned with IWL_TM_ATTR_REG_VALUE32. + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + u32 ofs, val32, cmd; + u8 val8; + struct sk_buff *skb; + int status = 0; + + if (!tb[IWL_TM_ATTR_REG_OFFSET]) { + IWL_ERR(priv, "Missing register offset\n"); + return -ENOMSG; + } + ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]); + IWL_INFO(priv, "testmode register access command offset 0x%x\n", ofs); + + /* Allow access only to FH/CSR/HBUS in direct mode. + Since we don't have the upper bounds for the CSR and HBUS segments, + we will use only the upper bound of FH for sanity check. */ + cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); + if ((cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 || + cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 || + cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8) && + (ofs >= FH_MEM_UPPER_BOUND)) { + IWL_ERR(priv, "offset out of segment (0x0 - 0x%x)\n", + FH_MEM_UPPER_BOUND); + return -EINVAL; + } + + switch (cmd) { + case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + val32 = iwl_read_direct32(priv->trans, ofs); + IWL_INFO(priv, "32bit value to read 0x%x\n", val32); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + return -ENOMEM; + } + if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(priv, "Error sending msg : %d\n", status); + break; + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + if (!tb[IWL_TM_ATTR_REG_VALUE32]) { + IWL_ERR(priv, "Missing value to write\n"); + return -ENOMSG; + } else { + val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); + IWL_INFO(priv, "32bit value to write 0x%x\n", val32); + iwl_write_direct32(priv->trans, ofs, val32); + } + break; + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + if (!tb[IWL_TM_ATTR_REG_VALUE8]) { + IWL_ERR(priv, "Missing value to write\n"); + return -ENOMSG; + } else { + val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]); + IWL_INFO(priv, "8bit value to write 0x%x\n", val8); + iwl_write8(priv->trans, ofs, val8); + } + break; + default: + IWL_ERR(priv, "Unknown testmode register command ID\n"); + return -ENOSYS; + } + + return status; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + + +static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv) +{ + struct iwl_notification_wait calib_wait; + static const u8 calib_complete[] = { + CALIBRATION_COMPLETE_NOTIFICATION + }; + int ret; + + iwl_init_notification_wait(&priv->notif_wait, &calib_wait, + calib_complete, ARRAY_SIZE(calib_complete), + NULL, NULL); + ret = iwl_init_alive_start(priv); + if (ret) { + IWL_ERR(priv, "Fail init calibration: %d\n", ret); + goto cfg_init_calib_error; + } + + ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, 2 * HZ); + if (ret) + IWL_ERR(priv, "Error detecting" + " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret); + return ret; + +cfg_init_calib_error: + iwl_remove_notification(&priv->notif_wait, &calib_wait); + return ret; +} + +/* + * This function handles the user application commands for driver. + * + * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the + * handlers respectively. + * + * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned + * value of the actual command execution is replied to the user application. + * + * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP + * is used for carry the message while IWL_TM_ATTR_COMMAND must set to + * IWL_TM_CMD_DEV2APP_SYNC_RSP. + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct iwl_trans *trans = priv->trans; + struct sk_buff *skb; + unsigned char *rsp_data_ptr = NULL; + int status = 0, rsp_data_len = 0; + u32 devid, inst_size = 0, data_size = 0; + const struct fw_img *img; + + switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { + case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: + rsp_data_ptr = (unsigned char *)priv->cfg->name; + rsp_data_len = strlen(priv->cfg->name); + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, + rsp_data_len + 20); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + return -ENOMEM; + } + if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, + IWL_TM_CMD_DEV2APP_SYNC_RSP) || + nla_put(skb, IWL_TM_ATTR_SYNC_RSP, + rsp_data_len, rsp_data_ptr)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(priv, "Error sending msg : %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: + status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); + if (status) + IWL_ERR(priv, "Error loading init ucode: %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: + iwl_testmode_cfg_init_calib(priv); + priv->ucode_loaded = false; + iwl_trans_stop_device(trans); + break; + + case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: + status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); + if (status) { + IWL_ERR(priv, + "Error loading runtime ucode: %d\n", status); + break; + } + status = iwl_alive_start(priv); + if (status) + IWL_ERR(priv, + "Error starting the device: %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: + iwl_scan_cancel_timeout(priv, 200); + priv->ucode_loaded = false; + iwl_trans_stop_device(trans); + status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); + if (status) { + IWL_ERR(priv, + "Error loading WOWLAN ucode: %d\n", status); + break; + } + status = iwl_alive_start(priv); + if (status) + IWL_ERR(priv, + "Error starting the device: %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_GET_EEPROM: + if (priv->eeprom) { + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, + priv->cfg->base_params->eeprom_size + 20); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + return -ENOMEM; + } + if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, + IWL_TM_CMD_DEV2APP_EEPROM_RSP) || + nla_put(skb, IWL_TM_ATTR_EEPROM, + priv->cfg->base_params->eeprom_size, + priv->eeprom)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(priv, "Error sending msg : %d\n", + status); + } else + return -EFAULT; + break; + + case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: + if (!tb[IWL_TM_ATTR_FIXRATE]) { + IWL_ERR(priv, "Missing fixrate setting\n"); + return -ENOMSG; + } + priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]); + break; + + case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: + IWL_INFO(priv, "uCode version raw: 0x%x\n", + priv->fw->ucode_ver); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + return -ENOMEM; + } + if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, + priv->fw->ucode_ver)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(priv, "Error sending msg : %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: + devid = priv->trans->hw_id; + IWL_INFO(priv, "hw version: 0x%x\n", devid); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + return -ENOMEM; + } + if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(priv, "Error sending msg : %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_GET_FW_INFO: + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + return -ENOMEM; + } + if (!priv->ucode_loaded) { + IWL_ERR(priv, "No uCode has not been loaded\n"); + return -EINVAL; + } else { + img = &priv->fw->img[priv->cur_ucode]; + inst_size = img->sec[IWL_UCODE_SECTION_INST].len; + data_size = img->sec[IWL_UCODE_SECTION_DATA].len; + } + if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) || + nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) || + nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(priv, "Error sending msg : %d\n", status); + break; + + default: + IWL_ERR(priv, "Unknown testmode driver command ID\n"); + return -ENOSYS; + } + return status; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + + +/* + * This function handles the user application commands for uCode trace + * + * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the + * handlers respectively. + * + * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned + * value of the actual command execution is replied to the user application. + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + struct sk_buff *skb; + int status = 0; + struct device *dev = priv->trans->dev; + + switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { + case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: + if (priv->testmode_trace.trace_enabled) + return -EBUSY; + + if (!tb[IWL_TM_ATTR_TRACE_SIZE]) + priv->testmode_trace.buff_size = TRACE_BUFF_SIZE_DEF; + else + priv->testmode_trace.buff_size = + nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]); + if (!priv->testmode_trace.buff_size) + return -EINVAL; + if (priv->testmode_trace.buff_size < TRACE_BUFF_SIZE_MIN || + priv->testmode_trace.buff_size > TRACE_BUFF_SIZE_MAX) + return -EINVAL; + + priv->testmode_trace.total_size = + priv->testmode_trace.buff_size + TRACE_BUFF_PADD; + priv->testmode_trace.cpu_addr = + dma_alloc_coherent(dev, + priv->testmode_trace.total_size, + &priv->testmode_trace.dma_addr, + GFP_KERNEL); + if (!priv->testmode_trace.cpu_addr) + return -ENOMEM; + priv->testmode_trace.trace_enabled = true; + priv->testmode_trace.trace_addr = (u8 *)PTR_ALIGN( + priv->testmode_trace.cpu_addr, 0x100); + memset(priv->testmode_trace.trace_addr, 0x03B, + priv->testmode_trace.buff_size); + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, + sizeof(priv->testmode_trace.dma_addr) + 20); + if (!skb) { + IWL_ERR(priv, "Memory allocation fail\n"); + iwl_trace_cleanup(priv); + return -ENOMEM; + } + if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR, + sizeof(priv->testmode_trace.dma_addr), + (u64 *)&priv->testmode_trace.dma_addr)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) { + IWL_ERR(priv, "Error sending msg : %d\n", status); + } + priv->testmode_trace.num_chunks = + DIV_ROUND_UP(priv->testmode_trace.buff_size, + DUMP_CHUNK_SIZE); + break; + + case IWL_TM_CMD_APP2DEV_END_TRACE: + iwl_trace_cleanup(priv); + break; + default: + IWL_ERR(priv, "Unknown testmode mem command ID\n"); + return -ENOSYS; + } + return status; + +nla_put_failure: + kfree_skb(skb); + if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) == + IWL_TM_CMD_APP2DEV_BEGIN_TRACE) + iwl_trace_cleanup(priv); + return -EMSGSIZE; +} + +static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int idx, length; + + if (priv->testmode_trace.trace_enabled && + priv->testmode_trace.trace_addr) { + idx = cb->args[4]; + if (idx >= priv->testmode_trace.num_chunks) + return -ENOENT; + length = DUMP_CHUNK_SIZE; + if (((idx + 1) == priv->testmode_trace.num_chunks) && + (priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE)) + length = priv->testmode_trace.buff_size % + DUMP_CHUNK_SIZE; + + if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length, + priv->testmode_trace.trace_addr + + (DUMP_CHUNK_SIZE * idx))) + goto nla_put_failure; + idx++; + cb->args[4] = idx; + return 0; + } else + return -EFAULT; + + nla_put_failure: + return -ENOBUFS; +} + +/* + * This function handles the user application switch ucode ownership. + * + * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and + * decide who the current owner of the uCode + * + * If the current owner is OWNERSHIP_TM, then the only host command + * can deliver to uCode is from testmode, all the other host commands + * will dropped. + * + * default driver is the owner of uCode in normal operational mode + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + u8 owner; + + if (!tb[IWL_TM_ATTR_UCODE_OWNER]) { + IWL_ERR(priv, "Missing ucode owner\n"); + return -ENOMSG; + } + + owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]); + if (owner == IWL_OWNERSHIP_DRIVER) { + priv->ucode_owner = owner; + priv->pre_rx_handler = NULL; + } else if (owner == IWL_OWNERSHIP_TM) { + priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; + priv->ucode_owner = owner; + } else { + IWL_ERR(priv, "Invalid owner\n"); + return -EINVAL; + } + return 0; +} + +static int iwl_testmode_indirect_read(struct iwl_priv *priv, u32 addr, u32 size) +{ + struct iwl_trans *trans = priv->trans; + unsigned long flags; + int i; + + if (size & 0x3) + return -EINVAL; + priv->testmode_mem.buff_size = size; + priv->testmode_mem.buff_addr = + kmalloc(priv->testmode_mem.buff_size, GFP_KERNEL); + if (priv->testmode_mem.buff_addr == NULL) + return -ENOMEM; + + /* Hard-coded periphery absolute address */ + if (IWL_TM_ABS_PRPH_START <= addr && + addr < IWL_TM_ABS_PRPH_START + PRPH_END) { + spin_lock_irqsave(&trans->reg_lock, flags); + iwl_grab_nic_access(trans); + iwl_write32(trans, HBUS_TARG_PRPH_RADDR, + addr | (3 << 24)); + for (i = 0; i < size; i += 4) + *(u32 *)(priv->testmode_mem.buff_addr + i) = + iwl_read32(trans, HBUS_TARG_PRPH_RDAT); + iwl_release_nic_access(trans); + spin_unlock_irqrestore(&trans->reg_lock, flags); + } else { /* target memory (SRAM) */ + _iwl_read_targ_mem_words(trans, addr, + priv->testmode_mem.buff_addr, + priv->testmode_mem.buff_size / 4); + } + + priv->testmode_mem.num_chunks = + DIV_ROUND_UP(priv->testmode_mem.buff_size, DUMP_CHUNK_SIZE); + priv->testmode_mem.read_in_progress = true; + return 0; + +} + +static int iwl_testmode_indirect_write(struct iwl_priv *priv, u32 addr, + u32 size, unsigned char *buf) +{ + struct iwl_trans *trans = priv->trans; + u32 val, i; + unsigned long flags; + + if (IWL_TM_ABS_PRPH_START <= addr && + addr < IWL_TM_ABS_PRPH_START + PRPH_END) { + /* Periphery writes can be 1-3 bytes long, or DWORDs */ + if (size < 4) { + memcpy(&val, buf, size); + spin_lock_irqsave(&trans->reg_lock, flags); + iwl_grab_nic_access(trans); + iwl_write32(trans, HBUS_TARG_PRPH_WADDR, + (addr & 0x0000FFFF) | + ((size - 1) << 24)); + iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); + iwl_release_nic_access(trans); + /* needed after consecutive writes w/o read */ + mmiowb(); + spin_unlock_irqrestore(&trans->reg_lock, flags); + } else { + if (size % 4) + return -EINVAL; + for (i = 0; i < size; i += 4) + iwl_write_prph(trans, addr+i, + *(u32 *)(buf+i)); + } + } else if (iwlagn_hw_valid_rtc_data_addr(addr) || + (IWLAGN_RTC_INST_LOWER_BOUND <= addr && + addr < IWLAGN_RTC_INST_UPPER_BOUND)) { + _iwl_write_targ_mem_words(trans, addr, buf, size/4); + } else + return -EINVAL; + return 0; +} + +/* + * This function handles the user application commands for SRAM data dump + * + * It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and + * IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading + * + * Several error will be retured, -EBUSY if the SRAM data retrieved by + * previous command has not been delivered to userspace, or -ENOMSG if + * the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE) + * are missing, or -ENOMEM if the buffer allocation fails. + * + * Otherwise 0 is replied indicating the success of the SRAM reading. + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_indirect_mem(struct ieee80211_hw *hw, + struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + u32 addr, size, cmd; + unsigned char *buf; + + /* Both read and write should be blocked, for atomicity */ + if (priv->testmode_mem.read_in_progress) + return -EBUSY; + + cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); + if (!tb[IWL_TM_ATTR_MEM_ADDR]) { + IWL_ERR(priv, "Error finding memory offset address\n"); + return -ENOMSG; + } + addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]); + if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) { + IWL_ERR(priv, "Error finding size for memory reading\n"); + return -ENOMSG; + } + size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]); + + if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) + return iwl_testmode_indirect_read(priv, addr, size); + else { + if (!tb[IWL_TM_ATTR_BUFFER_DUMP]) + return -EINVAL; + buf = (unsigned char *) nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]); + return iwl_testmode_indirect_write(priv, addr, size, buf); + } +} + +static int iwl_testmode_buffer_dump(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int idx, length; + + if (priv->testmode_mem.read_in_progress) { + idx = cb->args[4]; + if (idx >= priv->testmode_mem.num_chunks) { + iwl_mem_cleanup(priv); + return -ENOENT; + } + length = DUMP_CHUNK_SIZE; + if (((idx + 1) == priv->testmode_mem.num_chunks) && + (priv->testmode_mem.buff_size % DUMP_CHUNK_SIZE)) + length = priv->testmode_mem.buff_size % + DUMP_CHUNK_SIZE; + + if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length, + priv->testmode_mem.buff_addr + + (DUMP_CHUNK_SIZE * idx))) + goto nla_put_failure; + idx++; + cb->args[4] = idx; + return 0; + } else + return -EFAULT; + + nla_put_failure: + return -ENOBUFS; +} + +static int iwl_testmode_notifications(struct ieee80211_hw *hw, + struct nlattr **tb) +{ + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + bool enable; + + enable = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]); + if (enable) + priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; + else + priv->pre_rx_handler = NULL; + return 0; +} + + +/* The testmode gnl message handler that takes the gnl message from the + * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then + * invoke the corresponding handlers. + * + * This function is invoked when there is user space application sending + * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated + * by nl80211. + * + * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before + * dispatching it to the corresponding handler. + * + * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application; + * -ENOSYS is replied to the user application if the command is unknown; + * Otherwise, the command is dispatched to the respective handler. + * + * @hw: ieee80211_hw object that represents the device + * @data: pointer to user space message + * @len: length in byte of @data + */ +int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) +{ + struct nlattr *tb[IWL_TM_ATTR_MAX]; + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int result; + + result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, + iwl_testmode_gnl_msg_policy); + if (result != 0) { + IWL_ERR(priv, "Error parsing the gnl message : %d\n", result); + return result; + } + + /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ + if (!tb[IWL_TM_ATTR_COMMAND]) { + IWL_ERR(priv, "Missing testmode command type\n"); + return -ENOMSG; + } + /* in case multiple accesses to the device happens */ + mutex_lock(&priv->mutex); + + switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { + case IWL_TM_CMD_APP2DEV_UCODE: + IWL_DEBUG_INFO(priv, "testmode cmd to uCode\n"); + result = iwl_testmode_ucode(hw, tb); + break; + case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + IWL_DEBUG_INFO(priv, "testmode cmd to register\n"); + result = iwl_testmode_reg(hw, tb); + break; + case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: + case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: + case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: + case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: + case IWL_TM_CMD_APP2DEV_GET_EEPROM: + case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: + case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: + case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: + case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: + case IWL_TM_CMD_APP2DEV_GET_FW_INFO: + IWL_DEBUG_INFO(priv, "testmode cmd to driver\n"); + result = iwl_testmode_driver(hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: + case IWL_TM_CMD_APP2DEV_END_TRACE: + case IWL_TM_CMD_APP2DEV_READ_TRACE: + IWL_DEBUG_INFO(priv, "testmode uCode trace cmd to driver\n"); + result = iwl_testmode_trace(hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_OWNERSHIP: + IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n"); + result = iwl_testmode_ownership(hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: + IWL_DEBUG_INFO(priv, "testmode indirect memory cmd " + "to driver\n"); + result = iwl_testmode_indirect_mem(hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: + IWL_DEBUG_INFO(priv, "testmode notifications cmd " + "to driver\n"); + result = iwl_testmode_notifications(hw, tb); + break; + + default: + IWL_ERR(priv, "Unknown testmode command\n"); + result = -ENOSYS; + break; + } + + mutex_unlock(&priv->mutex); + return result; +} + +int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, + struct netlink_callback *cb, + void *data, int len) +{ + struct nlattr *tb[IWL_TM_ATTR_MAX]; + struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); + int result; + u32 cmd; + + if (cb->args[3]) { + /* offset by 1 since commands start at 0 */ + cmd = cb->args[3] - 1; + } else { + result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, + iwl_testmode_gnl_msg_policy); + if (result) { + IWL_ERR(priv, + "Error parsing the gnl message : %d\n", result); + return result; + } + + /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ + if (!tb[IWL_TM_ATTR_COMMAND]) { + IWL_ERR(priv, "Missing testmode command type\n"); + return -ENOMSG; + } + cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); + cb->args[3] = cmd + 1; + } + + /* in case multiple accesses to the device happens */ + mutex_lock(&priv->mutex); + switch (cmd) { + case IWL_TM_CMD_APP2DEV_READ_TRACE: + IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n"); + result = iwl_testmode_trace_dump(hw, skb, cb); + break; + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: + IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n"); + result = iwl_testmode_buffer_dump(hw, skb, cb); + break; + default: + result = -EINVAL; + break; + } + + mutex_unlock(&priv->mutex); + return result; +} diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.h b/drivers/net/wireless/iwlwifi/dvm/testmode.h new file mode 100644 index 000000000000..6ba211b09426 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/testmode.h @@ -0,0 +1,309 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __IWL_TESTMODE_H__ +#define __IWL_TESTMODE_H__ + +#include + + +/* + * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and + * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX). + * The command ID is carried with IWL_TM_ATTR_COMMAND. + * + * @IWL_TM_CMD_APP2DEV_UCODE: + * commands from user application to the uCode, + * the actual uCode host command ID is carried with + * IWL_TM_ATTR_UCODE_CMD_ID + * + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + * commands from user applicaiton to access register + * + * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name + * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image + * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration + * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image + * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data + * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS + * commands fom user space for pure driver level operations + * + * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE: + * @IWL_TM_CMD_APP2DEV_END_TRACE: + * @IWL_TM_CMD_APP2DEV_READ_TRACE: + * commands fom user space for uCode trace operations + * + * @IWL_TM_CMD_DEV2APP_SYNC_RSP: + * commands from kernel space to carry the synchronous response + * to user application + * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT: + * commands from kernel space to multicast the spontaneous messages + * to user application, or reply of host commands + * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: + * commands from kernel space to carry the eeprom response + * to user application + * + * @IWL_TM_CMD_APP2DEV_OWNERSHIP: + * commands from user application to own change the ownership of the uCode + * if application has the ownership, the only host command from + * testmode will deliver to uCode. Default owner is driver + * + * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image + * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version + * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device + * @IWL_TM_CMD_APP2DEV_GET_FW_INFO: + * retrieve information of existing loaded uCode image + * + * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: + * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: + * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: + * Commands to read/write data from periphery or SRAM memory ranges. + * Fore reading, a READ command is sent from the userspace and the data + * is returned when the user calls a DUMP command. + * For writing, only a WRITE command is used. + * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS: + * Command to enable/disable notifications (currently RX packets) from the + * driver to userspace. + */ +enum iwl_tm_cmd_t { + IWL_TM_CMD_APP2DEV_UCODE = 1, + IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, + IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, + IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, + IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, + IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, + IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, + IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8, + IWL_TM_CMD_APP2DEV_GET_EEPROM = 9, + IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10, + IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11, + IWL_TM_CMD_APP2DEV_END_TRACE = 12, + IWL_TM_CMD_APP2DEV_READ_TRACE = 13, + IWL_TM_CMD_DEV2APP_SYNC_RSP = 14, + IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, + IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, + IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, + RESERVED_18 = 18, + RESERVED_19 = 19, + RESERVED_20 = 20, + RESERVED_21 = 21, + IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, + IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, + IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, + IWL_TM_CMD_APP2DEV_GET_FW_INFO = 25, + IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26, + IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27, + IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28, + IWL_TM_CMD_APP2DEV_NOTIFICATIONS = 29, + IWL_TM_CMD_MAX = 30, +}; + +/* + * Atrribute filed in testmode command + * See enum iwl_tm_cmd_t. + * + * @IWL_TM_ATTR_NOT_APPLICABLE: + * The attribute is not applicable or invalid + * @IWL_TM_ATTR_COMMAND: + * From user space to kernel space: + * the command either destines to ucode, driver, or register; + * From kernel space to user space: + * the command either carries synchronous response, + * or the spontaneous message multicast from the device; + * + * @IWL_TM_ATTR_UCODE_CMD_ID: + * @IWL_TM_ATTR_UCODE_CMD_DATA: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE, + * The mandatory fields are : + * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID; + * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload + * to the ucode + * + * @IWL_TM_ATTR_REG_OFFSET: + * @IWL_TM_ATTR_REG_VALUE8: + * @IWL_TM_ATTR_REG_VALUE32: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX, + * The mandatory fields are: + * IWL_TM_ATTR_REG_OFFSET for the offset of the target register; + * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value + * + * @IWL_TM_ATTR_SYNC_RSP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP, + * The mandatory fields are: + * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user + * application command + * + * @IWL_TM_ATTR_UCODE_RX_PKT: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT, + * The mandatory fields are: + * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user + * application + * + * @IWL_TM_ATTR_EEPROM: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM, + * The mandatory fields are: + * IWL_TM_ATTR_EEPROM for the data content responging to the user + * application + * + * @IWL_TM_ATTR_TRACE_ADDR: + * @IWL_TM_ATTR_TRACE_SIZE: + * @IWL_TM_ATTR_TRACE_DUMP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE, + * The mandatory fields are: + * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address + * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size + * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump + * + * @IWL_TM_ATTR_FIXRATE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ, + * The mandatory fields are: + * IWL_TM_ATTR_FIXRATE for the fixed rate + * + * @IWL_TM_ATTR_UCODE_OWNER: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, + * The mandatory fields are: + * IWL_TM_ATTR_UCODE_OWNER for the new owner + * + * @IWL_TM_ATTR_MEM_ADDR: + * @IWL_TM_ATTR_BUFFER_SIZE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ + * or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE. + * The mandatory fields are: + * IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write + * IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write. + * + * @IWL_TM_ATTR_BUFFER_DUMP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP, + * IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read. + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE, + * this attribute contains the data to write. + * + * @IWL_TM_ATTR_FW_VERSION: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, + * IWL_TM_ATTR_FW_VERSION for the uCode version + * + * @IWL_TM_ATTR_DEVICE_ID: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, + * IWL_TM_ATTR_DEVICE_ID for the device ID information + * + * @IWL_TM_ATTR_FW_TYPE: + * @IWL_TM_ATTR_FW_INST_SIZE: + * @IWL_TM_ATTR_FW_DATA_SIZE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO, + * The mandatory fields are: + * IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...) + * IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section + * IWL_TM_ATTR_FW_DATA_SIZE for the size of data section + * + * @IWL_TM_ATTR_UCODE_CMD_SKB: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag + * indicates that the user wants to receive the response of the command + * in a reply SKB. If it's not present, the response is not returned. + * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this + * flag enables (if present) or disables (if not) the forwarding + * to userspace. + */ +enum iwl_tm_attr_t { + IWL_TM_ATTR_NOT_APPLICABLE = 0, + IWL_TM_ATTR_COMMAND = 1, + IWL_TM_ATTR_UCODE_CMD_ID = 2, + IWL_TM_ATTR_UCODE_CMD_DATA = 3, + IWL_TM_ATTR_REG_OFFSET = 4, + IWL_TM_ATTR_REG_VALUE8 = 5, + IWL_TM_ATTR_REG_VALUE32 = 6, + IWL_TM_ATTR_SYNC_RSP = 7, + IWL_TM_ATTR_UCODE_RX_PKT = 8, + IWL_TM_ATTR_EEPROM = 9, + IWL_TM_ATTR_TRACE_ADDR = 10, + IWL_TM_ATTR_TRACE_SIZE = 11, + IWL_TM_ATTR_TRACE_DUMP = 12, + IWL_TM_ATTR_FIXRATE = 13, + IWL_TM_ATTR_UCODE_OWNER = 14, + IWL_TM_ATTR_MEM_ADDR = 15, + IWL_TM_ATTR_BUFFER_SIZE = 16, + IWL_TM_ATTR_BUFFER_DUMP = 17, + IWL_TM_ATTR_FW_VERSION = 18, + IWL_TM_ATTR_DEVICE_ID = 19, + IWL_TM_ATTR_FW_TYPE = 20, + IWL_TM_ATTR_FW_INST_SIZE = 21, + IWL_TM_ATTR_FW_DATA_SIZE = 22, + IWL_TM_ATTR_UCODE_CMD_SKB = 23, + IWL_TM_ATTR_ENABLE_NOTIFICATION = 24, + IWL_TM_ATTR_MAX = 25, +}; + +/* uCode trace buffer */ +#define TRACE_BUFF_SIZE_MAX 0x200000 +#define TRACE_BUFF_SIZE_MIN 0x20000 +#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN +#define TRACE_BUFF_PADD 0x2000 + +/* Maximum data size of each dump it packet */ +#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) + +/* Address offset of data segment in SRAM */ +#define SRAM_DATA_SEG_OFFSET 0x800000 + +#endif diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c new file mode 100644 index 000000000000..55418899bc4e --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -0,0 +1,694 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + + +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-modparams.h" +#include "iwl-debug.h" +#include "agn.h" +#include "eeprom.h" +#include "dev.h" +#include "commands.h" +#include "tt.h" + +/* default Thermal Throttling transaction table + * Current state | Throttling Down | Throttling Up + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, + {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, + {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; + +/* Advance Thermal Throttling default restriction table */ +static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { + {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, + {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } +}; + +bool iwl_tt_is_low_power_state(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (tt->state >= IWL_TI_1) + return true; + return false; +} + +u8 iwl_tt_current_power_mode(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + return tt->tt_power_mode; +} + +bool iwl_ht_enabled(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return true; + restriction = tt->restriction + tt->state; + return restriction->is_ht; +} + +static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) +{ + s32 temp = priv->temperature; /* degrees CELSIUS except specified */ + bool within_margin = false; + + if (!priv->thermal_throttle.advanced_tt) + within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= + CT_KILL_THRESHOLD_LEGACY) ? true : false; + else + within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= + CT_KILL_THRESHOLD) ? true : false; + return within_margin; +} + +bool iwl_check_for_ct_kill(struct iwl_priv *priv) +{ + bool is_ct_kill = false; + + if (iwl_within_ct_kill_margin(priv)) { + iwl_tt_enter_ct_kill(priv); + is_ct_kill = true; + } + return is_ct_kill; +} + +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->tx_stream; +} + +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->rx_stream; +} + +#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ +#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ + +/* + * toggle the bit to wake up uCode and check the temperature + * if the temperature is below CT, uCode will stay awake and send card + * state notification with CT_KILL bit clear to inform Thermal Throttling + * Management to change state. Otherwise, uCode will go back to sleep + * without doing anything, driver should continue the 5 seconds timer + * to wake up uCode for temperature check until temperature drop below CT + */ +static void iwl_tt_check_exit_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + unsigned long flags; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (tt->state == IWL_TI_CT_KILL) { + if (priv->thermal_throttle.ct_kill_toggle) { + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = false; + } else { + iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = true; + } + iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); + spin_lock_irqsave(&priv->trans->reg_lock, flags); + if (likely(iwl_grab_nic_access(priv->trans))) + iwl_release_nic_access(priv->trans); + spin_unlock_irqrestore(&priv->trans->reg_lock, flags); + + /* Reschedule the ct_kill timer to occur in + * CT_KILL_EXIT_DURATION seconds to ensure we get a + * thermal update */ + IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, + jiffies + CT_KILL_EXIT_DURATION * HZ); + } +} + +static void iwl_perform_ct_kill_task(struct iwl_priv *priv, + bool stop) +{ + if (stop) { + IWL_DEBUG_TEMP(priv, "Stop all queues\n"); + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + IWL_DEBUG_TEMP(priv, + "Schedule 5 seconds CT_KILL Timer\n"); + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, + jiffies + CT_KILL_EXIT_DURATION * HZ); + } else { + IWL_DEBUG_TEMP(priv, "Wake all queues\n"); + if (priv->mac80211_registered) + ieee80211_wake_queues(priv->hw); + } +} + +static void iwl_tt_ready_for_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* temperature timer expired, ready to go into CT_KILL state */ + if (tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " + "temperature timer expired\n"); + tt->state = IWL_TI_CT_KILL; + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } +} + +static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) +{ + IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); + /* make request to retrieve statistics information */ + iwl_send_statistics_request(priv, CMD_SYNC, false); + /* Reschedule the ct_kill wait timer */ + mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, + jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); +} + +#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) + +/* + * Legacy thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + */ +static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + enum iwl_tt_state old_state; + +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_TEMP(priv, + "Temperature increase %d degree Celsius\n", + (temp - tt->tt_previous_temp)); + } +#endif + old_state = tt->state; + /* in Celsius */ + if (temp >= IWL_MINIMAL_POWER_THRESHOLD) + tt->state = IWL_TI_CT_KILL; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) + tt->state = IWL_TI_2; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) + tt->state = IWL_TI_1; + else + tt->state = IWL_TI_0; + +#ifdef CONFIG_IWLWIFI_DEBUG + tt->tt_previous_temp = temp; +#endif + /* stop ct_kill_waiting_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + if (tt->state != old_state) { + switch (tt->state) { + case IWL_TI_0: + /* + * When the system is ready to go back to IWL_TI_0 + * we only have to call iwl_power_update_mode() to + * do so. + */ + break; + case IWL_TI_1: + tt->tt_power_mode = IWL_POWER_INDEX_3; + break; + case IWL_TI_2: + tt->tt_power_mode = IWL_POWER_INDEX_4; + break; + default: + tt->tt_power_mode = IWL_POWER_INDEX_5; + break; + } + mutex_lock(&priv->mutex); + if (old_state == IWL_TI_CT_KILL) + clear_bit(STATUS_CT_KILL, &priv->status); + if (tt->state != IWL_TI_CT_KILL && + iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + if (old_state == IWL_TI_CT_KILL) + set_bit(STATUS_CT_KILL, &priv->status); + tt->state = old_state; + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + } else { + if (tt->state == IWL_TI_CT_KILL) { + if (force) { + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } else { + iwl_prepare_ct_kill_task(priv); + tt->state = old_state; + } + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) + iwl_perform_ct_kill_task(priv, false); + IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", + tt->state); + IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", + tt->tt_power_mode); + } + mutex_unlock(&priv->mutex); + } +} + +/* + * Advance thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + * Actions include relaxing the power down sleep thresholds and + * decreasing the number of TX streams + * 3) Avoid throughput performance impact as much as possible + * + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int i; + bool changed = false; + enum iwl_tt_state old_state; + struct iwl_tt_trans *transaction; + + old_state = tt->state; + for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { + /* based on the current TT state, + * find the curresponding transaction table + * each table has (IWL_TI_STATE_MAX - 1) entries + * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) + * will advance to the correct table. + * then based on the current temperature + * find the next state need to transaction to + * go through all the possible (IWL_TI_STATE_MAX - 1) entries + * in the current table to see if transaction is needed + */ + transaction = tt->transaction + + ((old_state * (IWL_TI_STATE_MAX - 1)) + i); + if (temp >= transaction->tt_low && + temp <= transaction->tt_high) { +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_TEMP(priv, + "Temperature increase %d " + "degree Celsius\n", + (temp - tt->tt_previous_temp)); + } + tt->tt_previous_temp = temp; +#endif + if (old_state != + transaction->next_state) { + changed = true; + tt->state = + transaction->next_state; + } + break; + } + } + /* stop ct_kill_waiting_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + if (changed) { + if (tt->state >= IWL_TI_1) { + /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ + tt->tt_power_mode = IWL_POWER_INDEX_5; + + if (!iwl_ht_enabled(priv)) { + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) { + struct iwl_rxon_cmd *rxon; + + rxon = &ctx->staging; + + /* disable HT */ + rxon->flags &= ~( + RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + } + } else { + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + } else { + /* + * restore system power setting -- it will be + * recalculated automatically. + */ + + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + mutex_lock(&priv->mutex); + if (old_state == IWL_TI_CT_KILL) + clear_bit(STATUS_CT_KILL, &priv->status); + if (tt->state != IWL_TI_CT_KILL && + iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + if (old_state == IWL_TI_CT_KILL) + set_bit(STATUS_CT_KILL, &priv->status); + tt->state = old_state; + } else { + IWL_DEBUG_TEMP(priv, + "Thermal Throttling to new state: %u\n", + tt->state); + if (old_state != IWL_TI_CT_KILL && + tt->state == IWL_TI_CT_KILL) { + if (force) { + IWL_DEBUG_TEMP(priv, + "Enter IWL_TI_CT_KILL\n"); + set_bit(STATUS_CT_KILL, &priv->status); + iwl_perform_ct_kill_task(priv, true); + } else { + iwl_prepare_ct_kill_task(priv); + tt->state = old_state; + } + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, false); + } + } + mutex_unlock(&priv->mutex); + } +} + +/* Card State Notification indicated reach critical temperature + * if PSP not enable, no Thermal Throttling function will be performed + * just set the GP1 bit to acknowledge the event + * otherwise, go into IWL_TI_CT_KILL state + * since Card State Notification will not provide any temperature reading + * for Legacy mode + * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() + * for advance mode + * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state + */ +static void iwl_bg_ct_enter(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + if (tt->state != IWL_TI_CT_KILL) { + IWL_ERR(priv, "Device reached critical temperature " + "- ucode going to sleep!\n"); + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_MINIMAL_POWER_THRESHOLD, + true); + else + iwl_advance_tt_handler(priv, + CT_KILL_THRESHOLD + 1, true); + } +} + +/* Card State Notification indicated out of critical temperature + * since Card State Notification will not provide any temperature reading + * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature + * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state + */ +static void iwl_bg_ct_exit(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + /* stop ct_kill_exit_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + + if (tt->state == IWL_TI_CT_KILL) { + IWL_ERR(priv, + "Device temperature below critical" + "- ucode awake!\n"); + /* + * exit from CT_KILL state + * reset the current temperature reading + */ + priv->temperature = 0; + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_REDUCED_PERFORMANCE_THRESHOLD_2, + true); + else + iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, + true); + } +} + +void iwl_tt_enter_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); + queue_work(priv->workqueue, &priv->ct_enter); +} + +void iwl_tt_exit_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); + queue_work(priv->workqueue, &priv->ct_exit); +} + +static void iwl_bg_tt_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); + s32 temp = priv->temperature; /* degrees CELSIUS except specified */ + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, temp, false); + else + iwl_advance_tt_handler(priv, temp, false); +} + +void iwl_tt_handler(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); + queue_work(priv->workqueue, &priv->tt_work); +} + +/* Thermal throttling initialization + * For advance thermal throttling: + * Initialize Thermal Index and temperature threshold table + * Initialize thermal throttling restriction table + */ +void iwl_tt_initialize(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); + struct iwl_tt_trans *transaction; + + IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); + + memset(tt, 0, sizeof(struct iwl_tt_mgmt)); + + tt->state = IWL_TI_0; + init_timer(&priv->thermal_throttle.ct_kill_exit_tm); + priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; + priv->thermal_throttle.ct_kill_exit_tm.function = + iwl_tt_check_exit_ct_kill; + init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); + priv->thermal_throttle.ct_kill_waiting_tm.data = + (unsigned long)priv; + priv->thermal_throttle.ct_kill_waiting_tm.function = + iwl_tt_ready_for_ct_kill; + /* setup deferred ct kill work */ + INIT_WORK(&priv->tt_work, iwl_bg_tt_work); + INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); + INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); + + if (priv->cfg->base_params->adv_thermal_throttle) { + IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); + tt->restriction = kcalloc(IWL_TI_STATE_MAX, + sizeof(struct iwl_tt_restriction), + GFP_KERNEL); + tt->transaction = kcalloc(IWL_TI_STATE_MAX * + (IWL_TI_STATE_MAX - 1), + sizeof(struct iwl_tt_trans), + GFP_KERNEL); + if (!tt->restriction || !tt->transaction) { + IWL_ERR(priv, "Fallback to Legacy Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } else { + transaction = tt->transaction + + (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_0[0], size); + transaction = tt->transaction + + (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_1[0], size); + transaction = tt->transaction + + (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_2[0], size); + transaction = tt->transaction + + (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_3[0], size); + size = sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX; + memcpy(tt->restriction, + &restriction_range[0], size); + priv->thermal_throttle.advanced_tt = true; + } + } else { + IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + } +} + +/* cleanup thermal throttling management related memory and timer */ +void iwl_tt_exit(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + /* stop ct_kill_exit_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + /* stop ct_kill_waiting_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + cancel_work_sync(&priv->tt_work); + cancel_work_sync(&priv->ct_enter); + cancel_work_sync(&priv->ct_exit); + + if (priv->thermal_throttle.advanced_tt) { + /* free advance thermal throttling memory */ + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } +} diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.h b/drivers/net/wireless/iwlwifi/dvm/tt.h new file mode 100644 index 000000000000..44c7c8f30a2d --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/tt.h @@ -0,0 +1,128 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#ifndef __iwl_tt_setting_h__ +#define __iwl_tt_setting_h__ + +#include "commands.h" + +#define IWL_ABSOLUTE_ZERO 0 +#define IWL_ABSOLUTE_MAX 0xFFFFFFFF +#define IWL_TT_INCREASE_MARGIN 5 +#define IWL_TT_CT_KILL_MARGIN 3 + +enum iwl_antenna_ok { + IWL_ANT_OK_NONE, + IWL_ANT_OK_SINGLE, + IWL_ANT_OK_MULTI, +}; + +/* Thermal Throttling State Machine states */ +enum iwl_tt_state { + IWL_TI_0, /* normal temperature, system power state */ + IWL_TI_1, /* high temperature detect, low power state */ + IWL_TI_2, /* higher temperature detected, lower power state */ + IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ + IWL_TI_STATE_MAX +}; + +/** + * struct iwl_tt_restriction - Thermal Throttling restriction table + * @tx_stream: number of tx stream allowed + * @is_ht: ht enable/disable + * @rx_stream: number of rx stream allowed + * + * This table is used by advance thermal throttling management + * based on the current thermal throttling state, and determines + * the number of tx/rx streams and the status of HT operation. + */ +struct iwl_tt_restriction { + enum iwl_antenna_ok tx_stream; + enum iwl_antenna_ok rx_stream; + bool is_ht; +}; + +/** + * struct iwl_tt_trans - Thermal Throttling transaction table + * @next_state: next thermal throttling mode + * @tt_low: low temperature threshold to change state + * @tt_high: high temperature threshold to change state + * + * This is used by the advanced thermal throttling algorithm + * to determine the next thermal state to go based on the + * current temperature. + */ +struct iwl_tt_trans { + enum iwl_tt_state next_state; + u32 tt_low; + u32 tt_high; +}; + +/** + * struct iwl_tt_mgnt - Thermal Throttling Management structure + * @advanced_tt: advanced thermal throttle required + * @state: current Thermal Throttling state + * @tt_power_mode: Thermal Throttling power mode index + * being used to set power level when + * when thermal throttling state != IWL_TI_0 + * the tt_power_mode should set to different + * power mode based on the current tt state + * @tt_previous_temperature: last measured temperature + * @iwl_tt_restriction: ptr to restriction tbl, used by advance + * thermal throttling to determine how many tx/rx streams + * should be used in tt state; and can HT be enabled or not + * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling + * state transaction + * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature + * @ct_kill_exit_tm: timer to exit thermal kill + */ +struct iwl_tt_mgmt { + enum iwl_tt_state state; + bool advanced_tt; + u8 tt_power_mode; + bool ct_kill_toggle; +#ifdef CONFIG_IWLWIFI_DEBUG + s32 tt_previous_temp; +#endif + struct iwl_tt_restriction *restriction; + struct iwl_tt_trans *transaction; + struct timer_list ct_kill_exit_tm; + struct timer_list ct_kill_waiting_tm; +}; + +u8 iwl_tt_current_power_mode(struct iwl_priv *priv); +bool iwl_tt_is_low_power_state(struct iwl_priv *priv); +bool iwl_ht_enabled(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); +void iwl_tt_enter_ct_kill(struct iwl_priv *priv); +void iwl_tt_exit_ct_kill(struct iwl_priv *priv); +void iwl_tt_handler(struct iwl_priv *priv); +void iwl_tt_initialize(struct iwl_priv *priv); +void iwl_tt_exit(struct iwl_priv *priv); + +#endif /* __iwl_tt_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c new file mode 100644 index 000000000000..52f2cae080c3 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -0,0 +1,1385 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include "iwl-io.h" +#include "iwl-trans.h" +#include "iwl-agn-hw.h" +#include "dev.h" +#include "agn.h" + +static const u8 tid_to_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, +}; + +static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + __le16 fc, __le32 *tx_flags) +{ + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS || + info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT || + info->flags & IEEE80211_TX_CTL_AMPDU) + *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK; +} + +/* + * handle build REPLY_TX command notification. + */ +static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, + struct sk_buff *skb, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 sta_id) +{ + __le16 fc = hdr->frame_control; + __le32 tx_flags = tx_cmd->tx_flags; + + tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + tx_flags |= TX_CMD_FLG_ACK_MSK; + else + tx_flags &= ~TX_CMD_FLG_ACK_MSK; + + if (ieee80211_is_probe_resp(fc)) + tx_flags |= TX_CMD_FLG_TSF_MSK; + else if (ieee80211_is_back_req(fc)) + tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; + else if (info->band == IEEE80211_BAND_2GHZ && + priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist && + (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || + ieee80211_is_reassoc_req(fc) || + skb->protocol == cpu_to_be16(ETH_P_PAE))) + tx_flags |= TX_CMD_FLG_IGNORE_BT; + + + tx_cmd->sta_id = sta_id; + if (ieee80211_has_morefrags(fc)) + tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tx_cmd->tid_tspec = qc[0] & 0xf; + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } else { + tx_cmd->tid_tspec = IWL_TID_NON_QOS; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + else + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } + + iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); + + tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); + else + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); + } else { + tx_cmd->timeout.pm_frame_timeout = 0; + } + + tx_cmd->driver_txop = 0; + tx_cmd->tx_flags = tx_flags; + tx_cmd->next_frame_len = 0; +} + +static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + __le16 fc) +{ + u32 rate_flags; + int rate_idx; + u8 rts_retry_limit; + u8 data_retry_limit; + u8 rate_plcp; + + if (priv->wowlan) { + rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT; + data_retry_limit = IWLAGN_LOW_RETRY_LIMIT; + } else { + /* Set retry limit on RTS packets */ + rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT; + + /* Set retry limit on DATA packets and Probe Responses*/ + if (ieee80211_is_probe_resp(fc)) { + data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT; + rts_retry_limit = + min(data_retry_limit, rts_retry_limit); + } else if (ieee80211_is_back_req(fc)) + data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT; + else + data_retry_limit = IWLAGN_DEFAULT_TX_RETRY; + } + + tx_cmd->data_retry_limit = data_retry_limit; + tx_cmd->rts_retry_limit = rts_retry_limit; + + /* DATA packets will use the uCode station table for rate/antenna + * selection */ + if (ieee80211_is_data(fc)) { + tx_cmd->initial_rate_index = 0; + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + if (priv->tm_fixed_rate) { + /* + * rate overwrite by testmode + * we not only send lq command to change rate + * we also re-enforce per data pkt base. + */ + tx_cmd->tx_flags &= ~TX_CMD_FLG_STA_RATE_MSK; + memcpy(&tx_cmd->rate_n_flags, &priv->tm_fixed_rate, + sizeof(tx_cmd->rate_n_flags)); + } +#endif + return; + } else if (ieee80211_is_back_req(fc)) + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; + + /** + * If the current TX rate stored in mac80211 has the MCS bit set, it's + * not really a TX rate. Thus, we use the lowest supported rate for + * this band. Also use the lowest supported rate if the stored rate + * index is invalid. + */ + rate_idx = info->control.rates[0].idx; + if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || + (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) + rate_idx = rate_lowest_index(&priv->bands[info->band], + info->control.sta); + /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ + if (info->band == IEEE80211_BAND_5GHZ) + rate_idx += IWL_FIRST_OFDM_RATE; + /* Get PLCP rate for tx_cmd->rate_n_flags */ + rate_plcp = iwl_rates[rate_idx].plcp; + /* Zero out flags for this packet */ + rate_flags = 0; + + /* Set CCK flag as needed */ + if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + + /* Set up antennas */ + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist && + priv->bt_full_concurrent) { + /* operated as 1x1 in full concurrency mode */ + priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, + first_antenna(priv->hw_params.valid_tx_ant)); + } else + priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, + priv->hw_params.valid_tx_ant); + rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); + + /* Set the rate in the TX cmd */ + tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); +} + +static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + struct iwl_tx_cmd *tx_cmd, + struct sk_buff *skb_frag) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; + IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_TKIP: + tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); + IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | + (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); + + memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); + + IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " + "with key %d\n", keyconf->keyidx); + break; + + default: + IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); + break; + } +} + +/** + * iwl_sta_id_or_broadcast - return sta_id or broadcast sta + * @context: the current context + * @sta: mac80211 station + * + * In certain circumstances mac80211 passes a station pointer + * that may be %NULL, for example during TX or key setup. In + * that case, we need to use the broadcast station, so this + * inline wraps that pattern. + */ +static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, + struct ieee80211_sta *sta) +{ + int sta_id; + + if (!sta) + return context->bcast_sta_id; + + sta_id = iwl_sta_id(sta); + + /* + * mac80211 should not be passing a partially + * initialised station! + */ + WARN_ON(sta_id == IWL_INVALID_STATION); + + return sta_id; +} + +/* + * start REPLY_TX command process + */ +int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_station_priv *sta_priv = NULL; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_device_cmd *dev_cmd = NULL; + struct iwl_tx_cmd *tx_cmd; + __le16 fc; + u8 hdr_len; + u16 len, seq_number = 0; + u8 sta_id, tid = IWL_MAX_TID_COUNT; + bool is_agg = false; + int txq_id; + + if (info->control.vif) + ctx = iwl_rxon_ctx_from_vif(info->control.vif); + + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); + goto drop_unlock_priv; + } + + fc = hdr->frame_control; + +#ifdef CONFIG_IWLWIFI_DEBUG + if (ieee80211_is_auth(fc)) + IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); + else if (ieee80211_is_assoc_req(fc)) + IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); + else if (ieee80211_is_reassoc_req(fc)) + IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); +#endif + + if (unlikely(ieee80211_is_probe_resp(fc))) { + struct iwl_wipan_noa_data *noa_data = + rcu_dereference(priv->noa_data); + + if (noa_data && + pskb_expand_head(skb, 0, noa_data->length, + GFP_ATOMIC) == 0) { + memcpy(skb_put(skb, noa_data->length), + noa_data->data, noa_data->length); + hdr = (struct ieee80211_hdr *)skb->data; + } + } + + hdr_len = ieee80211_hdrlen(fc); + + /* For management frames use broadcast id to do not break aggregation */ + if (!ieee80211_is_data(fc)) + sta_id = ctx->bcast_sta_id; + else { + /* Find index into station table for destination station */ + sta_id = iwl_sta_id_or_broadcast(ctx, info->control.sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", + hdr->addr1); + goto drop_unlock_priv; + } + } + + IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); + + if (info->control.sta) + sta_priv = (void *)info->control.sta->drv_priv; + + if (sta_priv && sta_priv->asleep && + (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { + /* + * This sends an asynchronous command to the device, + * but we can rely on it being processed before the + * next frame is processed -- and the next frame to + * this station is the one that will consume this + * counter. + * For now set the counter to just 1 since we do not + * support uAPSD yet. + * + * FIXME: If we get two non-bufferable frames one + * after the other, we might only send out one of + * them because this is racy. + */ + iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); + } + + if (info->flags & IEEE80211_TX_CTL_AMPDU) + is_agg = true; + + dev_cmd = kmem_cache_alloc(iwl_tx_cmd_pool, GFP_ATOMIC); + + if (unlikely(!dev_cmd)) + goto drop_unlock_priv; + + memset(dev_cmd, 0, sizeof(*dev_cmd)); + tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; + + /* Total # bytes to be transmitted */ + len = (u16)skb->len; + tx_cmd->len = cpu_to_le16(len); + + if (info->control.hw_key) + iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb); + + /* TODO need this for burst mode later on */ + iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); + + iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); + + memset(&info->status, 0, sizeof(info->status)); + + info->driver_data[0] = ctx; + info->driver_data[1] = dev_cmd; + + spin_lock(&priv->sta_lock); + + if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { + u8 *qc = NULL; + struct iwl_tid_data *tid_data; + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + goto drop_unlock_sta; + tid_data = &priv->tid_data[sta_id][tid]; + + /* aggregation is on for this */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + tid_data->agg.state != IWL_AGG_ON) { + IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:" + " Tx flags = 0x%08x, agg.state = %d", + info->flags, tid_data->agg.state); + IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", + sta_id, tid, SEQ_TO_SN(tid_data->seq_number)); + goto drop_unlock_sta; + } + + /* We can receive packets from the stack in IWL_AGG_{ON,OFF} + * only. Check this here. + */ + if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && + tid_data->agg.state != IWL_AGG_OFF, + "Tx while agg.state = %d", tid_data->agg.state)) + goto drop_unlock_sta; + + seq_number = tid_data->seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + seq_number += 0x10; + } + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + if (is_agg) + txq_id = priv->tid_data[sta_id][tid].agg.txq_id; + else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + /* + * Send this frame after DTIM -- there's a special queue + * reserved for this for contexts that support AP mode. + */ + txq_id = ctx->mcast_queue; + + /* + * The microcode will clear the more data + * bit in the last frame it transmits. + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + txq_id = IWL_AUX_QUEUE; + else + txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)]; + + WARN_ON_ONCE(!is_agg && txq_id != info->hw_queue); + WARN_ON_ONCE(is_agg && + priv->queue_to_mac80211[txq_id] != info->hw_queue); + + if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) + goto drop_unlock_sta; + + if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) && + !ieee80211_has_morefrags(fc)) + priv->tid_data[sta_id][tid].seq_number = seq_number; + + spin_unlock(&priv->sta_lock); + + /* + * Avoid atomic ops if it isn't an associated client. + * Also, if this is a packet for aggregation, don't + * increase the counter because the ucode will stop + * aggregation queues when their respective station + * goes to sleep. + */ + if (sta_priv && sta_priv->client && !is_agg) + atomic_inc(&sta_priv->pending_frames); + + if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + iwl_scan_offchannel_skb(priv); + + return 0; + +drop_unlock_sta: + if (dev_cmd) + kmem_cache_free(iwl_tx_cmd_pool, dev_cmd); + spin_unlock(&priv->sta_lock); +drop_unlock_priv: + return -1; +} + +static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) +{ + int q; + + for (q = IWLAGN_FIRST_AMPDU_QUEUE; + q < priv->cfg->base_params->num_of_queues; q++) { + if (!test_and_set_bit(q, priv->agg_q_alloc)) { + priv->queue_to_mac80211[q] = mq; + return q; + } + } + + return -ENOSPC; +} + +static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q) +{ + clear_bit(q, priv->agg_q_alloc); + priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE; +} + +int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_tid_data *tid_data; + int sta_id, txq_id; + enum iwl_agg_state agg_state; + + sta_id = iwl_sta_id(sta); + + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_bh(&priv->sta_lock); + + tid_data = &priv->tid_data[sta_id][tid]; + txq_id = priv->tid_data[sta_id][tid].agg.txq_id; + + switch (priv->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* + * This can happen if the peer stops aggregation + * again before we've had a chance to drain the + * queue we selected previously, i.e. before the + * session was really started completely. + */ + IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); + goto turn_off; + case IWL_AGG_STARTING: + /* + * This can happen when the session is stopped before + * we receive ADDBA response + */ + IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); + goto turn_off; + case IWL_AGG_ON: + break; + default: + IWL_WARN(priv, "Stopping AGG while state not ON " + "or starting for %d on %d (%d)\n", sta_id, tid, + priv->tid_data[sta_id][tid].agg.state); + spin_unlock_bh(&priv->sta_lock); + return 0; + } + + tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); + + /* There are still packets for this RA / TID in the HW */ + if (!test_bit(txq_id, priv->agg_q_alloc)) { + IWL_DEBUG_TX_QUEUES(priv, + "stopping AGG on STA/TID %d/%d but hwq %d not used\n", + sta_id, tid, txq_id); + } else if (tid_data->agg.ssn != tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " + "next_recl = %d\n", + tid_data->agg.ssn, + tid_data->next_reclaimed); + priv->tid_data[sta_id][tid].agg.state = + IWL_EMPTYING_HW_QUEUE_DELBA; + spin_unlock_bh(&priv->sta_lock); + return 0; + } + + IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", + tid_data->agg.ssn); +turn_off: + agg_state = priv->tid_data[sta_id][tid].agg.state; + priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF; + + spin_unlock_bh(&priv->sta_lock); + + if (test_bit(txq_id, priv->agg_q_alloc)) { + /* + * If the transport didn't know that we wanted to start + * agreggation, don't tell it that we want to stop them. + * This can happen when we don't get the addBA response on + * time, or we hadn't time to drain the AC queues. + */ + if (agg_state == IWL_AGG_ON) + iwl_trans_tx_agg_disable(priv->trans, txq_id); + else + IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", + agg_state); + iwlagn_dealloc_agg_txq(priv, txq_id); + } + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + + return 0; +} + +int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + struct iwl_tid_data *tid_data; + int sta_id, txq_id, ret; + + IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", + sta->addr, tid); + + sta_id = iwl_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Start AGG on invalid station\n"); + return -ENXIO; + } + if (unlikely(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { + IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); + return -ENXIO; + } + + txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]); + if (txq_id < 0) { + IWL_DEBUG_TX_QUEUES(priv, + "No free aggregation queue for %pM/%d\n", + sta->addr, tid); + return txq_id; + } + + ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); + if (ret) + return ret; + + spin_lock_bh(&priv->sta_lock); + tid_data = &priv->tid_data[sta_id][tid]; + tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); + tid_data->agg.txq_id = txq_id; + + *ssn = tid_data->agg.ssn; + + if (*ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", + tid_data->agg.ssn); + tid_data->agg.state = IWL_AGG_STARTING; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + } else { + IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " + "next_reclaimed = %d\n", + tid_data->agg.ssn, + tid_data->next_reclaimed); + tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; + } + spin_unlock_bh(&priv->sta_lock); + + return ret; +} + +int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size) +{ + struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + int q, fifo; + u16 ssn; + + buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); + + spin_lock_bh(&priv->sta_lock); + ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; + q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id; + priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON; + spin_unlock_bh(&priv->sta_lock); + + fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; + + iwl_trans_tx_agg_setup(priv->trans, q, fifo, + sta_priv->sta_id, tid, + buf_size, ssn); + + /* + * If the limit is 0, then it wasn't initialised yet, + * use the default. We can do that since we take the + * minimum below, and we don't want to go above our + * default due to hardware restrictions. + */ + if (sta_priv->max_agg_bufsize == 0) + sta_priv->max_agg_bufsize = + LINK_QUAL_AGG_FRAME_LIMIT_DEF; + + /* + * Even though in theory the peer could have different + * aggregation reorder buffer sizes for different sessions, + * our ucode doesn't allow for that and has a global limit + * for each station. Therefore, use the minimum of all the + * aggregation sessions and our default value. + */ + sta_priv->max_agg_bufsize = + min(sta_priv->max_agg_bufsize, buf_size); + + if (priv->hw_params.use_rts_for_aggregation) { + /* + * switch to RTS/CTS if it is the prefer protection + * method for HT traffic + */ + + sta_priv->lq_sta.lq.general_params.flags |= + LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; + } + priv->agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + + sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = + sta_priv->max_agg_bufsize; + + IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); + + return iwl_send_lq_cmd(priv, ctx, + &sta_priv->lq_sta.lq, CMD_ASYNC, false); +} + +static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) +{ + struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; + enum iwl_rxon_context_id ctx; + struct ieee80211_vif *vif; + u8 *addr; + + lockdep_assert_held(&priv->sta_lock); + + addr = priv->stations[sta_id].sta.sta.addr; + ctx = priv->stations[sta_id].ctxid; + vif = priv->contexts[ctx].vif; + + switch (priv->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_DELBA: + /* There are no packets for this RA / TID in the HW any more */ + if (tid_data->agg.ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can continue DELBA flow ssn = next_recl =" + " %d", tid_data->next_reclaimed); + iwl_trans_tx_agg_disable(priv->trans, + tid_data->agg.txq_id); + iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); + tid_data->agg.state = IWL_AGG_OFF; + ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); + } + break; + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* There are no packets for this RA / TID in the HW any more */ + if (tid_data->agg.ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can continue ADDBA flow ssn = next_recl =" + " %d", tid_data->next_reclaimed); + tid_data->agg.state = IWL_AGG_STARTING; + ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); + } + break; + default: + break; + } +} + +static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr1) +{ + struct ieee80211_sta *sta; + struct iwl_station_priv *sta_priv; + + rcu_read_lock(); + sta = ieee80211_find_sta(ctx->vif, addr1); + if (sta) { + sta_priv = (void *)sta->drv_priv; + /* avoid atomic ops if this isn't a client */ + if (sta_priv->client && + atomic_dec_return(&sta_priv->pending_frames) == 0) + ieee80211_sta_block_awake(priv->hw, sta, false); + } + rcu_read_unlock(); +} + +/** + * translate ucode response to mac80211 tx status control values + */ +static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + if (rate_n_flags & RATE_MCS_HT_MSK) + r->flags |= IEEE80211_TX_RC_MCS; + if (rate_n_flags & RATE_MCS_GF_MSK) + r->flags |= IEEE80211_TX_RC_GREEN_FIELD; + if (rate_n_flags & RATE_MCS_HT40_MSK) + r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (rate_n_flags & RATE_MCS_DUP_MSK) + r->flags |= IEEE80211_TX_RC_DUP_DATA; + if (rate_n_flags & RATE_MCS_SGI_MSK) + r->flags |= IEEE80211_TX_RC_SHORT_GI; + r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +const char *iwl_get_tx_fail_reason(u32 status) +{ +#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x +#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x + + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + return "SUCCESS"; + TX_STATUS_POSTPONE(DELAY); + TX_STATUS_POSTPONE(FEW_BYTES); + TX_STATUS_POSTPONE(BT_PRIO); + TX_STATUS_POSTPONE(QUIET_PERIOD); + TX_STATUS_POSTPONE(CALC_TTAK); + TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); + TX_STATUS_FAIL(SHORT_LIMIT); + TX_STATUS_FAIL(LONG_LIMIT); + TX_STATUS_FAIL(FIFO_UNDERRUN); + TX_STATUS_FAIL(DRAIN_FLOW); + TX_STATUS_FAIL(RFKILL_FLUSH); + TX_STATUS_FAIL(LIFE_EXPIRE); + TX_STATUS_FAIL(DEST_PS); + TX_STATUS_FAIL(HOST_ABORTED); + TX_STATUS_FAIL(BT_RETRY); + TX_STATUS_FAIL(STA_INVALID); + TX_STATUS_FAIL(FRAG_DROPPED); + TX_STATUS_FAIL(TID_DISABLE); + TX_STATUS_FAIL(FIFO_FLUSHED); + TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); + TX_STATUS_FAIL(PASSIVE_NO_RX); + TX_STATUS_FAIL(NO_BEACON_ON_RADAR); + } + + return "UNKNOWN"; + +#undef TX_STATUS_FAIL +#undef TX_STATUS_POSTPONE +} +#endif /* CONFIG_IWLWIFI_DEBUG */ + +static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) +{ + status &= AGG_TX_STATUS_MSK; + + switch (status) { + case AGG_TX_STATE_UNDERRUN_MSK: + priv->reply_agg_tx_stats.underrun++; + break; + case AGG_TX_STATE_BT_PRIO_MSK: + priv->reply_agg_tx_stats.bt_prio++; + break; + case AGG_TX_STATE_FEW_BYTES_MSK: + priv->reply_agg_tx_stats.few_bytes++; + break; + case AGG_TX_STATE_ABORT_MSK: + priv->reply_agg_tx_stats.abort++; + break; + case AGG_TX_STATE_LAST_SENT_TTL_MSK: + priv->reply_agg_tx_stats.last_sent_ttl++; + break; + case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK: + priv->reply_agg_tx_stats.last_sent_try++; + break; + case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK: + priv->reply_agg_tx_stats.last_sent_bt_kill++; + break; + case AGG_TX_STATE_SCD_QUERY_MSK: + priv->reply_agg_tx_stats.scd_query++; + break; + case AGG_TX_STATE_TEST_BAD_CRC32_MSK: + priv->reply_agg_tx_stats.bad_crc32++; + break; + case AGG_TX_STATE_RESPONSE_MSK: + priv->reply_agg_tx_stats.response++; + break; + case AGG_TX_STATE_DUMP_TX_MSK: + priv->reply_agg_tx_stats.dump_tx++; + break; + case AGG_TX_STATE_DELAY_TX_MSK: + priv->reply_agg_tx_stats.delay_tx++; + break; + default: + priv->reply_agg_tx_stats.unknown++; + break; + } +} + +static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, + struct iwlagn_tx_resp *tx_resp) +{ + struct agg_tx_status *frame_status = &tx_resp->status; + int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> + IWLAGN_TX_RES_TID_POS; + int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> + IWLAGN_TX_RES_RA_POS; + struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; + u32 status = le16_to_cpu(tx_resp->status.status); + int i; + + WARN_ON(tid == IWL_TID_NON_QOS); + + if (agg->wait_for_ba) + IWL_DEBUG_TX_REPLY(priv, + "got tx response w/o block-ack\n"); + + agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); + agg->wait_for_ba = (tx_resp->frame_count > 1); + + /* + * If the BT kill count is non-zero, we'll get this + * notification again. + */ + if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && + priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); + } + + if (tx_resp->frame_count == 1) + return; + + /* Construct bit-map of pending frames within Tx window */ + for (i = 0; i < tx_resp->frame_count; i++) { + u16 fstatus = le16_to_cpu(frame_status[i].status); + + if (status & AGG_TX_STATUS_MSK) + iwlagn_count_agg_tx_err_status(priv, fstatus); + + if (status & (AGG_TX_STATE_FEW_BYTES_MSK | + AGG_TX_STATE_ABORT_MSK)) + continue; + + IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), " + "try-count (0x%08x)\n", + iwl_get_agg_tx_fail_reason(fstatus), + fstatus & AGG_TX_STATUS_MSK, + fstatus & AGG_TX_TRY_MSK); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUG +#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x + +const char *iwl_get_agg_tx_fail_reason(u16 status) +{ + status &= AGG_TX_STATUS_MSK; + switch (status) { + case AGG_TX_STATE_TRANSMITTED: + return "SUCCESS"; + AGG_TX_STATE_FAIL(UNDERRUN_MSK); + AGG_TX_STATE_FAIL(BT_PRIO_MSK); + AGG_TX_STATE_FAIL(FEW_BYTES_MSK); + AGG_TX_STATE_FAIL(ABORT_MSK); + AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK); + AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK); + AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK); + AGG_TX_STATE_FAIL(SCD_QUERY_MSK); + AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK); + AGG_TX_STATE_FAIL(RESPONSE_MSK); + AGG_TX_STATE_FAIL(DUMP_TX_MSK); + AGG_TX_STATE_FAIL(DELAY_TX_MSK); + } + + return "UNKNOWN"; +} +#endif /* CONFIG_IWLWIFI_DEBUG */ + +static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) +{ + return le32_to_cpup((__le32 *)&tx_resp->status + + tx_resp->frame_count) & MAX_SN; +} + +static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_POSTPONE_DELAY: + priv->reply_tx_stats.pp_delay++; + break; + case TX_STATUS_POSTPONE_FEW_BYTES: + priv->reply_tx_stats.pp_few_bytes++; + break; + case TX_STATUS_POSTPONE_BT_PRIO: + priv->reply_tx_stats.pp_bt_prio++; + break; + case TX_STATUS_POSTPONE_QUIET_PERIOD: + priv->reply_tx_stats.pp_quiet_period++; + break; + case TX_STATUS_POSTPONE_CALC_TTAK: + priv->reply_tx_stats.pp_calc_ttak++; + break; + case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: + priv->reply_tx_stats.int_crossed_retry++; + break; + case TX_STATUS_FAIL_SHORT_LIMIT: + priv->reply_tx_stats.short_limit++; + break; + case TX_STATUS_FAIL_LONG_LIMIT: + priv->reply_tx_stats.long_limit++; + break; + case TX_STATUS_FAIL_FIFO_UNDERRUN: + priv->reply_tx_stats.fifo_underrun++; + break; + case TX_STATUS_FAIL_DRAIN_FLOW: + priv->reply_tx_stats.drain_flow++; + break; + case TX_STATUS_FAIL_RFKILL_FLUSH: + priv->reply_tx_stats.rfkill_flush++; + break; + case TX_STATUS_FAIL_LIFE_EXPIRE: + priv->reply_tx_stats.life_expire++; + break; + case TX_STATUS_FAIL_DEST_PS: + priv->reply_tx_stats.dest_ps++; + break; + case TX_STATUS_FAIL_HOST_ABORTED: + priv->reply_tx_stats.host_abort++; + break; + case TX_STATUS_FAIL_BT_RETRY: + priv->reply_tx_stats.bt_retry++; + break; + case TX_STATUS_FAIL_STA_INVALID: + priv->reply_tx_stats.sta_invalid++; + break; + case TX_STATUS_FAIL_FRAG_DROPPED: + priv->reply_tx_stats.frag_drop++; + break; + case TX_STATUS_FAIL_TID_DISABLE: + priv->reply_tx_stats.tid_disable++; + break; + case TX_STATUS_FAIL_FIFO_FLUSHED: + priv->reply_tx_stats.fifo_flush++; + break; + case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL: + priv->reply_tx_stats.insuff_cf_poll++; + break; + case TX_STATUS_FAIL_PASSIVE_NO_RX: + priv->reply_tx_stats.fail_hw_drop++; + break; + case TX_STATUS_FAIL_NO_BEACON_ON_RADAR: + priv->reply_tx_stats.sta_color_mismatch++; + break; + default: + priv->reply_tx_stats.unknown++; + break; + } +} + +static void iwlagn_set_tx_status(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + struct iwlagn_tx_resp *tx_resp, + bool is_agg) +{ + u16 status = le16_to_cpu(tx_resp->status.status); + + info->status.rates[0].count = tx_resp->failure_frame + 1; + if (is_agg) + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + info->flags |= iwl_tx_status_to_mac80211(status); + iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), + info); + if (!iwl_is_tx_success(status)) + iwlagn_count_tx_err_status(priv, status); +} + +static void iwl_check_abort_status(struct iwl_priv *priv, + u8 frame_count, u32 status) +{ + if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { + IWL_ERR(priv, "Tx flush command to flush out all frames\n"); + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) + queue_work(priv->workqueue, &priv->tx_flush); + } +} + +static int iwl_reclaim(struct iwl_priv *priv, int sta_id, int tid, + int txq_id, int ssn, struct sk_buff_head *skbs) +{ + if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE && + tid != IWL_TID_NON_QOS && + txq_id != priv->tid_data[sta_id][tid].agg.txq_id)) { + /* + * FIXME: this is a uCode bug which need to be addressed, + * log the information and return for now. + * Since it is can possibly happen very often and in order + * not to fill the syslog, don't use IWL_ERR or IWL_WARN + */ + IWL_DEBUG_TX_QUEUES(priv, + "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n", + txq_id, sta_id, tid, + priv->tid_data[sta_id][tid].agg.txq_id); + return 1; + } + + iwl_trans_reclaim(priv->trans, txq_id, ssn, skbs); + return 0; +} + +int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); + struct iwlagn_tx_resp *tx_resp = (void *)pkt->data; + struct ieee80211_hdr *hdr; + u32 status = le16_to_cpu(tx_resp->status.status); + u16 ssn = iwlagn_get_scd_ssn(tx_resp); + int tid; + int sta_id; + int freed; + struct ieee80211_tx_info *info; + struct sk_buff_head skbs; + struct sk_buff *skb; + struct iwl_rxon_context *ctx; + bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); + bool is_offchannel_skb; + + tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> + IWLAGN_TX_RES_TID_POS; + sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> + IWLAGN_TX_RES_RA_POS; + + spin_lock(&priv->sta_lock); + + if (is_agg) + iwl_rx_reply_tx_agg(priv, tx_resp); + + __skb_queue_head_init(&skbs); + + is_offchannel_skb = false; + + if (tx_resp->frame_count == 1) { + u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); + next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); + + if (is_agg) { + /* If this is an aggregation queue, we can rely on the + * ssn since the wifi sequence number corresponds to + * the index in the TFD ring (%256). + * The seq_ctl is the sequence control of the packet + * to which this Tx response relates. But if there is a + * hole in the bitmap of the BA we received, this Tx + * response may allow to reclaim the hole and all the + * subsequent packets that were already acked. + * In that case, seq_ctl != ssn, and the next packet + * to be reclaimed will be ssn and not seq_ctl. + */ + next_reclaimed = ssn; + } + + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + + /*we can free until ssn % q.n_bd not inclusive */ + WARN_ON(iwl_reclaim(priv, sta_id, tid, txq_id, ssn, &skbs)); + iwlagn_check_ratid_empty(priv, sta_id, tid); + freed = 0; + + /* process frames */ + skb_queue_walk(&skbs, skb) { + hdr = (struct ieee80211_hdr *)skb->data; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + priv->last_seq_ctl = tx_resp->seq_ctl; + + info = IEEE80211_SKB_CB(skb); + ctx = info->driver_data[0]; + kmem_cache_free(iwl_tx_cmd_pool, + (info->driver_data[1])); + + memset(&info->status, 0, sizeof(info->status)); + + if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && + iwl_is_associated_ctx(ctx) && ctx->vif && + ctx->vif->type == NL80211_IFTYPE_STATION) { + /* block and stop all queues */ + priv->passive_no_rx = true; + IWL_DEBUG_TX_QUEUES(priv, "stop all queues: " + "passive channel"); + ieee80211_stop_queues(priv->hw); + + IWL_DEBUG_TX_REPLY(priv, + "TXQ %d status %s (0x%08x) " + "rate_n_flags 0x%x retries %d\n", + txq_id, + iwl_get_tx_fail_reason(status), + status, + le32_to_cpu(tx_resp->rate_n_flags), + tx_resp->failure_frame); + + IWL_DEBUG_TX_REPLY(priv, + "FrameCnt = %d, idx=%d\n", + tx_resp->frame_count, cmd_index); + } + + /* check if BAR is needed */ + if (is_agg && !iwl_is_tx_success(status)) + info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), + tx_resp, is_agg); + if (!is_agg) + iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); + + is_offchannel_skb = + (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); + freed++; + } + + WARN_ON(!is_agg && freed != 1); + + /* + * An offchannel frame can be send only on the AUX queue, where + * there is no aggregation (and reordering) so it only is single + * skb is expected to be processed. + */ + WARN_ON(is_offchannel_skb && freed != 1); + } + + iwl_check_abort_status(priv, tx_resp->frame_count, status); + spin_unlock(&priv->sta_lock); + + while (!skb_queue_empty(&skbs)) { + skb = __skb_dequeue(&skbs); + ieee80211_tx_status(priv->hw, skb); + } + + if (is_offchannel_skb) + iwl_scan_offchannel_skb_status(priv); + + return 0; +} + +/** + * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA + * + * Handles block-acknowledge notification from device, which reports success + * of frames sent via aggregation. + */ +int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; + struct iwl_ht_agg *agg; + struct sk_buff_head reclaimed_skbs; + struct ieee80211_tx_info *info; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + int sta_id; + int tid; + int freed; + + /* "flow" corresponds to Tx queue */ + u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); + + /* "ssn" is start of block-ack Tx window, corresponds to index + * (in Tx queue's circular buffer) of first TFD/frame in window */ + u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); + + if (scd_flow >= priv->cfg->base_params->num_of_queues) { + IWL_ERR(priv, + "BUG_ON scd_flow is bigger than number of queues\n"); + return 0; + } + + sta_id = ba_resp->sta_id; + tid = ba_resp->tid; + agg = &priv->tid_data[sta_id][tid].agg; + + spin_lock(&priv->sta_lock); + + if (unlikely(!agg->wait_for_ba)) { + if (unlikely(ba_resp->bitmap)) + IWL_ERR(priv, "Received BA when not expected\n"); + spin_unlock(&priv->sta_lock); + return 0; + } + + __skb_queue_head_init(&reclaimed_skbs); + + /* Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack window (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). */ + if (iwl_reclaim(priv, sta_id, tid, scd_flow, + ba_resp_scd_ssn, &reclaimed_skbs)) { + spin_unlock(&priv->sta_lock); + return 0; + } + + IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " + "sta_id = %d\n", + agg->wait_for_ba, + (u8 *) &ba_resp->sta_addr_lo32, + ba_resp->sta_id); + IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " + "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", + ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), + (unsigned long long)le64_to_cpu(ba_resp->bitmap), + scd_flow, ba_resp_scd_ssn, ba_resp->txed, + ba_resp->txed_2_done); + + /* Mark that the expected block-ack response arrived */ + agg->wait_for_ba = false; + + /* Sanity check values reported by uCode */ + if (ba_resp->txed_2_done > ba_resp->txed) { + IWL_DEBUG_TX_REPLY(priv, + "bogus sent(%d) and ack(%d) count\n", + ba_resp->txed, ba_resp->txed_2_done); + /* + * set txed_2_done = txed, + * so it won't impact rate scale + */ + ba_resp->txed = ba_resp->txed_2_done; + } + + priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; + + iwlagn_check_ratid_empty(priv, sta_id, tid); + freed = 0; + + skb_queue_walk(&reclaimed_skbs, skb) { + hdr = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_data_qos(hdr->frame_control)) + freed++; + else + WARN_ON_ONCE(1); + + info = IEEE80211_SKB_CB(skb); + kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1])); + + if (freed == 1) { + /* this is the first skb we deliver in this batch */ + /* put the rate scaling data there */ + info = IEEE80211_SKB_CB(skb); + memset(&info->status, 0, sizeof(info->status)); + info->flags |= IEEE80211_TX_STAT_ACK; + info->flags |= IEEE80211_TX_STAT_AMPDU; + info->status.ampdu_ack_len = ba_resp->txed_2_done; + info->status.ampdu_len = ba_resp->txed; + iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, + info); + } + } + + spin_unlock(&priv->sta_lock); + + while (!skb_queue_empty(&reclaimed_skbs)) { + skb = __skb_dequeue(&reclaimed_skbs); + ieee80211_tx_status(priv->hw, skb); + } + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c new file mode 100644 index 000000000000..5a2e186c7ded --- /dev/null +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -0,0 +1,533 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include + +#include "iwl-io.h" +#include "iwl-agn-hw.h" +#include "iwl-trans.h" +#include "iwl-fh.h" +#include "iwl-op-mode.h" + +#include "dev.h" +#include "agn.h" +#include "calib.h" + +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static inline const struct fw_img * +iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) +{ + if (ucode_type >= IWL_UCODE_TYPE_MAX) + return NULL; + + return &priv->fw->img[ucode_type]; +} + +/* + * Calibration + */ +static int iwl_set_Xtal_calib(struct iwl_priv *priv) +{ + struct iwl_calib_xtal_freq_cmd cmd; + __le16 *xtal_calib = + (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_XTAL); + + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); + cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); + cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]); + return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); +} + +static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) +{ + struct iwl_calib_temperature_offset_cmd cmd; + __le16 *offset_calib = + (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); + memcpy(&cmd.radio_sensor_offset, offset_calib, sizeof(*offset_calib)); + if (!(cmd.radio_sensor_offset)) + cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; + + IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n", + le16_to_cpu(cmd.radio_sensor_offset)); + return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); +} + +static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv) +{ + struct iwl_calib_temperature_offset_v2_cmd cmd; + __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv, + EEPROM_KELVIN_TEMPERATURE); + __le16 *offset_calib_low = + (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); + struct iwl_eeprom_calib_hdr *hdr; + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); + hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, + EEPROM_CALIB_ALL); + memcpy(&cmd.radio_sensor_offset_high, offset_calib_high, + sizeof(*offset_calib_high)); + memcpy(&cmd.radio_sensor_offset_low, offset_calib_low, + sizeof(*offset_calib_low)); + if (!(cmd.radio_sensor_offset_low)) { + IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); + cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; + cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; + } + memcpy(&cmd.burntVoltageRef, &hdr->voltage, + sizeof(hdr->voltage)); + + IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", + le16_to_cpu(cmd.radio_sensor_offset_high)); + IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", + le16_to_cpu(cmd.radio_sensor_offset_low)); + IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", + le16_to_cpu(cmd.burntVoltageRef)); + + return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); +} + +static int iwl_send_calib_cfg(struct iwl_priv *priv) +{ + struct iwl_calib_cfg_cmd calib_cfg_cmd; + struct iwl_host_cmd cmd = { + .id = CALIBRATION_CFG_CMD, + .len = { sizeof(struct iwl_calib_cfg_cmd), }, + .data = { &calib_cfg_cmd, }, + }; + + memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); + calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL; + calib_cfg_cmd.ucd_calib_cfg.flags = + IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK; + + return iwl_dvm_send_cmd(priv, &cmd); +} + +int iwl_init_alive_start(struct iwl_priv *priv) +{ + int ret; + + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + /* + * Tell uCode we are ready to perform calibration + * need to perform this before any calibration + * no need to close the envlope since we are going + * to load the runtime uCode later. + */ + ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, + BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); + if (ret) + return ret; + + } + + ret = iwl_send_calib_cfg(priv); + if (ret) + return ret; + + /** + * temperature offset calibration is only needed for runtime ucode, + * so prepare the value now. + */ + if (priv->cfg->need_temp_offset_calib) { + if (priv->cfg->temp_offset_v2) + return iwl_set_temperature_offset_calib_v2(priv); + else + return iwl_set_temperature_offset_calib(priv); + } + + return 0; +} + +static int iwl_send_wimax_coex(struct iwl_priv *priv) +{ + struct iwl_wimax_coex_cmd coex_cmd; + + /* coexistence is disabled */ + memset(&coex_cmd, 0, sizeof(coex_cmd)); + + return iwl_dvm_send_cmd_pdu(priv, + COEX_PRIORITY_TABLE_CMD, CMD_SYNC, + sizeof(coex_cmd), &coex_cmd); +} + +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { + ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | + (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), + 0, 0, 0, 0, 0, 0, 0 +}; + +void iwl_send_prio_tbl(struct iwl_priv *priv) +{ + struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd; + + memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, + sizeof(iwl_bt_prio_tbl)); + if (iwl_dvm_send_cmd_pdu(priv, + REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC, + sizeof(prio_tbl_cmd), &prio_tbl_cmd)) + IWL_ERR(priv, "failed to send BT prio tbl command\n"); +} + +int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) +{ + struct iwl_bt_coex_prot_env_cmd env_cmd; + int ret; + + env_cmd.action = action; + env_cmd.type = type; + ret = iwl_dvm_send_cmd_pdu(priv, + REPLY_BT_COEX_PROT_ENV, CMD_SYNC, + sizeof(env_cmd), &env_cmd); + if (ret) + IWL_ERR(priv, "failed to send BT env command\n"); + return ret; +} + + +static int iwl_alive_notify(struct iwl_priv *priv) +{ + int ret; + + iwl_trans_fw_alive(priv->trans); + + priv->passive_no_rx = false; + priv->transport_queue_stop = 0; + + ret = iwl_send_wimax_coex(priv); + if (ret) + return ret; + + if (!priv->cfg->no_xtal_calib) { + ret = iwl_set_Xtal_calib(priv); + if (ret) + return ret; + } + + return iwl_send_calib_results(priv); +} + + +/** + * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host, + * using sample data 100 bytes apart. If these sample points are good, + * it's a pretty good bet that everything between them is good, too. + */ +static int iwl_verify_sec_sparse(struct iwl_priv *priv, + const struct fw_desc *fw_desc) +{ + __le32 *image = (__le32 *)fw_desc->v_addr; + u32 len = fw_desc->len; + u32 val; + u32 i; + + IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); + + for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IWL_DL_IO is set */ + iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR, + i + fw_desc->offset); + val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) + return -EIO; + } + + return 0; +} + +static void iwl_print_mismatch_sec(struct iwl_priv *priv, + const struct fw_desc *fw_desc) +{ + __le32 *image = (__le32 *)fw_desc->v_addr; + u32 len = fw_desc->len; + u32 val; + u32 offs; + int errors = 0; + + IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); + + iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR, + fw_desc->offset); + + for (offs = 0; + offs < len && errors < 20; + offs += sizeof(u32), image++) { + /* read data comes through single port, auto-incr addr */ + val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { + IWL_ERR(priv, "uCode INST section at " + "offset 0x%x, is 0x%x, s/b 0x%x\n", + offs, val, le32_to_cpu(*image)); + errors++; + } + } +} + +/** + * iwl_verify_ucode - determine which instruction image is in SRAM, + * and verify its contents + */ +static int iwl_verify_ucode(struct iwl_priv *priv, + enum iwl_ucode_type ucode_type) +{ + const struct fw_img *img = iwl_get_ucode_image(priv, ucode_type); + + if (!img) { + IWL_ERR(priv, "Invalid ucode requested (%d)\n", ucode_type); + return -EINVAL; + } + + if (!iwl_verify_sec_sparse(priv, &img->sec[IWL_UCODE_SECTION_INST])) { + IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n"); + return 0; + } + + IWL_ERR(priv, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n"); + + iwl_print_mismatch_sec(priv, &img->sec[IWL_UCODE_SECTION_INST]); + return -EIO; +} + +struct iwl_alive_data { + bool valid; + u8 subtype; +}; + +static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_priv *priv = + container_of(notif_wait, struct iwl_priv, notif_wait); + struct iwl_alive_data *alive_data = data; + struct iwl_alive_resp *palive; + + palive = (void *)pkt->data; + + IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision " + "0x%01X 0x%01X\n", + palive->is_valid, palive->ver_type, + palive->ver_subtype); + + priv->device_pointers.error_event_table = + le32_to_cpu(palive->error_event_table_ptr); + priv->device_pointers.log_event_table = + le32_to_cpu(palive->log_event_table_ptr); + + alive_data->subtype = palive->ver_subtype; + alive_data->valid = palive->is_valid == UCODE_VALID_OK; + + return true; +} + +#define UCODE_ALIVE_TIMEOUT HZ +#define UCODE_CALIB_TIMEOUT (2*HZ) + +int iwl_load_ucode_wait_alive(struct iwl_priv *priv, + enum iwl_ucode_type ucode_type) +{ + struct iwl_notification_wait alive_wait; + struct iwl_alive_data alive_data; + const struct fw_img *fw; + int ret; + enum iwl_ucode_type old_type; + static const u8 alive_cmd[] = { REPLY_ALIVE }; + + old_type = priv->cur_ucode; + priv->cur_ucode = ucode_type; + fw = iwl_get_ucode_image(priv, ucode_type); + + priv->ucode_loaded = false; + + if (!fw) + return -EINVAL; + + iwl_init_notification_wait(&priv->notif_wait, &alive_wait, + alive_cmd, ARRAY_SIZE(alive_cmd), + iwl_alive_fn, &alive_data); + + ret = iwl_trans_start_fw(priv->trans, fw); + if (ret) { + priv->cur_ucode = old_type; + iwl_remove_notification(&priv->notif_wait, &alive_wait); + return ret; + } + + /* + * Some things may run in the background now, but we + * just wait for the ALIVE notification here. + */ + ret = iwl_wait_notification(&priv->notif_wait, &alive_wait, + UCODE_ALIVE_TIMEOUT); + if (ret) { + priv->cur_ucode = old_type; + return ret; + } + + if (!alive_data.valid) { + IWL_ERR(priv, "Loaded ucode is not valid!\n"); + priv->cur_ucode = old_type; + return -EIO; + } + + /* + * This step takes a long time (60-80ms!!) and + * WoWLAN image should be loaded quickly, so + * skip it for WoWLAN. + */ + if (ucode_type != IWL_UCODE_WOWLAN) { + ret = iwl_verify_ucode(priv, ucode_type); + if (ret) { + priv->cur_ucode = old_type; + return ret; + } + + /* delay a bit to give rfkill time to run */ + msleep(5); + } + + ret = iwl_alive_notify(priv); + if (ret) { + IWL_WARN(priv, + "Could not complete ALIVE transition: %d\n", ret); + priv->cur_ucode = old_type; + return ret; + } + + priv->ucode_loaded = true; + + return 0; +} + +static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_priv *priv = data; + struct iwl_calib_hdr *hdr; + int len; + + if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { + WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); + return true; + } + + hdr = (struct iwl_calib_hdr *)pkt->data; + len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + + /* reduce the size by the length field itself */ + len -= sizeof(__le32); + + if (iwl_calib_set(priv, hdr, len)) + IWL_ERR(priv, "Failed to record calibration data %d\n", + hdr->op_code); + + return false; +} + +int iwl_run_init_ucode(struct iwl_priv *priv) +{ + struct iwl_notification_wait calib_wait; + static const u8 calib_complete[] = { + CALIBRATION_RES_NOTIFICATION, + CALIBRATION_COMPLETE_NOTIFICATION + }; + int ret; + + lockdep_assert_held(&priv->mutex); + + /* No init ucode required? Curious, but maybe ok */ + if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) + return 0; + + if (priv->init_ucode_run) + return 0; + + iwl_init_notification_wait(&priv->notif_wait, &calib_wait, + calib_complete, ARRAY_SIZE(calib_complete), + iwlagn_wait_calib, priv); + + /* Will also start the device */ + ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); + if (ret) + goto error; + + ret = iwl_init_alive_start(priv); + if (ret) + goto error; + + /* + * Some things may run in the background now, but we + * just wait for the calibration complete notification. + */ + ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, + UCODE_CALIB_TIMEOUT); + if (!ret) + priv->init_ucode_run = true; + + goto out; + + error: + iwl_remove_notification(&priv->notif_wait, &calib_wait); + out: + /* Whatever happened, stop the device */ + iwl_trans_stop_device(priv->trans); + priv->ucode_loaded = false; + + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c deleted file mode 100644 index 95f27f1a423b..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c +++ /dev/null @@ -1,1112 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#include -#include - -#include "iwl-dev.h" -#include "iwl-agn-calib.h" -#include "iwl-trans.h" -#include "iwl-agn.h" - -/***************************************************************************** - * INIT calibrations framework - *****************************************************************************/ - -/* Opaque calibration results */ -struct iwl_calib_result { - struct list_head list; - size_t cmd_len; - struct iwl_calib_hdr hdr; - /* data follows */ -}; - -struct statistics_general_data { - u32 beacon_silence_rssi_a; - u32 beacon_silence_rssi_b; - u32 beacon_silence_rssi_c; - u32 beacon_energy_a; - u32 beacon_energy_b; - u32 beacon_energy_c; -}; - -int iwl_send_calib_results(struct iwl_priv *priv) -{ - struct iwl_host_cmd hcmd = { - .id = REPLY_PHY_CALIBRATION_CMD, - .flags = CMD_SYNC, - }; - struct iwl_calib_result *res; - - list_for_each_entry(res, &priv->calib_results, list) { - int ret; - - hcmd.len[0] = res->cmd_len; - hcmd.data[0] = &res->hdr; - hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - ret = iwl_dvm_send_cmd(priv, &hcmd); - if (ret) { - IWL_ERR(priv, "Error %d on calib cmd %d\n", - ret, res->hdr.op_code); - return ret; - } - } - - return 0; -} - -int iwl_calib_set(struct iwl_priv *priv, - const struct iwl_calib_hdr *cmd, int len) -{ - struct iwl_calib_result *res, *tmp; - - res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr), - GFP_ATOMIC); - if (!res) - return -ENOMEM; - memcpy(&res->hdr, cmd, len); - res->cmd_len = len; - - list_for_each_entry(tmp, &priv->calib_results, list) { - if (tmp->hdr.op_code == res->hdr.op_code) { - list_replace(&tmp->list, &res->list); - kfree(tmp); - return 0; - } - } - - /* wasn't in list already */ - list_add_tail(&res->list, &priv->calib_results); - - return 0; -} - -void iwl_calib_free_results(struct iwl_priv *priv) -{ - struct iwl_calib_result *res, *tmp; - - list_for_each_entry_safe(res, tmp, &priv->calib_results, list) { - list_del(&res->list); - kfree(res); - } -} - -/***************************************************************************** - * RUNTIME calibrations framework - *****************************************************************************/ - -/* "false alarms" are signals that our DSP tries to lock onto, - * but then determines that they are either noise, or transmissions - * from a distant wireless network (also "noise", really) that get - * "stepped on" by stronger transmissions within our own network. - * This algorithm attempts to set a sensitivity level that is high - * enough to receive all of our own network traffic, but not so - * high that our DSP gets too busy trying to lock onto non-network - * activity/noise. */ -static int iwl_sens_energy_cck(struct iwl_priv *priv, - u32 norm_fa, - u32 rx_enable_time, - struct statistics_general_data *rx_info) -{ - u32 max_nrg_cck = 0; - int i = 0; - u8 max_silence_rssi = 0; - u32 silence_ref = 0; - u8 silence_rssi_a = 0; - u8 silence_rssi_b = 0; - u8 silence_rssi_c = 0; - u32 val; - - /* "false_alarms" values below are cross-multiplications to assess the - * numbers of false alarms within the measured period of actual Rx - * (Rx is off when we're txing), vs the min/max expected false alarms - * (some should be expected if rx is sensitive enough) in a - * hypothetical listening period of 200 time units (TU), 204.8 msec: - * - * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time - * - * */ - u32 false_alarms = norm_fa * 200 * 1024; - u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; - u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; - struct iwl_sensitivity_data *data = NULL; - const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; - - data = &(priv->sensitivity_data); - - data->nrg_auto_corr_silence_diff = 0; - - /* Find max silence rssi among all 3 receivers. - * This is background noise, which may include transmissions from other - * networks, measured during silence before our network's beacon */ - silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & - ALL_BAND_FILTER) >> 8); - silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & - ALL_BAND_FILTER) >> 8); - silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & - ALL_BAND_FILTER) >> 8); - - val = max(silence_rssi_b, silence_rssi_c); - max_silence_rssi = max(silence_rssi_a, (u8) val); - - /* Store silence rssi in 20-beacon history table */ - data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; - data->nrg_silence_idx++; - if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) - data->nrg_silence_idx = 0; - - /* Find max silence rssi across 20 beacon history */ - for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { - val = data->nrg_silence_rssi[i]; - silence_ref = max(silence_ref, val); - } - IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", - silence_rssi_a, silence_rssi_b, silence_rssi_c, - silence_ref); - - /* Find max rx energy (min value!) among all 3 receivers, - * measured during beacon frame. - * Save it in 10-beacon history table. */ - i = data->nrg_energy_idx; - val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); - data->nrg_value[i] = min(rx_info->beacon_energy_a, val); - - data->nrg_energy_idx++; - if (data->nrg_energy_idx >= 10) - data->nrg_energy_idx = 0; - - /* Find min rx energy (max value) across 10 beacon history. - * This is the minimum signal level that we want to receive well. - * Add backoff (margin so we don't miss slightly lower energy frames). - * This establishes an upper bound (min value) for energy threshold. */ - max_nrg_cck = data->nrg_value[0]; - for (i = 1; i < 10; i++) - max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); - max_nrg_cck += 6; - - IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", - rx_info->beacon_energy_a, rx_info->beacon_energy_b, - rx_info->beacon_energy_c, max_nrg_cck - 6); - - /* Count number of consecutive beacons with fewer-than-desired - * false alarms. */ - if (false_alarms < min_false_alarms) - data->num_in_cck_no_fa++; - else - data->num_in_cck_no_fa = 0; - IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", - data->num_in_cck_no_fa); - - /* If we got too many false alarms this time, reduce sensitivity */ - if ((false_alarms > max_false_alarms) && - (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { - IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", - false_alarms, max_false_alarms); - IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); - data->nrg_curr_state = IWL_FA_TOO_MANY; - /* Store for "fewer than desired" on later beacon */ - data->nrg_silence_ref = silence_ref; - - /* increase energy threshold (reduce nrg value) - * to decrease sensitivity */ - data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; - /* Else if we got fewer than desired, increase sensitivity */ - } else if (false_alarms < min_false_alarms) { - data->nrg_curr_state = IWL_FA_TOO_FEW; - - /* Compare silence level with silence level for most recent - * healthy number or too many false alarms */ - data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - - (s32)silence_ref; - - IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u, silence diff %d\n", - false_alarms, min_false_alarms, - data->nrg_auto_corr_silence_diff); - - /* Increase value to increase sensitivity, but only if: - * 1a) previous beacon did *not* have *too many* false alarms - * 1b) AND there's a significant difference in Rx levels - * from a previous beacon with too many, or healthy # FAs - * OR 2) We've seen a lot of beacons (100) with too few - * false alarms */ - if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && - ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || - (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { - - IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); - /* Increase nrg value to increase sensitivity */ - val = data->nrg_th_cck + NRG_STEP_CCK; - data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); - } else { - IWL_DEBUG_CALIB(priv, "... but not changing sensitivity\n"); - } - - /* Else we got a healthy number of false alarms, keep status quo */ - } else { - IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); - data->nrg_curr_state = IWL_FA_GOOD_RANGE; - - /* Store for use in "fewer than desired" with later beacon */ - data->nrg_silence_ref = silence_ref; - - /* If previous beacon had too many false alarms, - * give it some extra margin by reducing sensitivity again - * (but don't go below measured energy of desired Rx) */ - if (IWL_FA_TOO_MANY == data->nrg_prev_state) { - IWL_DEBUG_CALIB(priv, "... increasing margin\n"); - if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) - data->nrg_th_cck -= NRG_MARGIN; - else - data->nrg_th_cck = max_nrg_cck; - } - } - - /* Make sure the energy threshold does not go above the measured - * energy of the desired Rx signals (reduced by backoff margin), - * or else we might start missing Rx frames. - * Lower value is higher energy, so we use max()! - */ - data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); - IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); - - data->nrg_prev_state = data->nrg_curr_state; - - /* Auto-correlation CCK algorithm */ - if (false_alarms > min_false_alarms) { - - /* increase auto_corr values to decrease sensitivity - * so the DSP won't be disturbed by the noise - */ - if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) - data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; - else { - val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; - data->auto_corr_cck = - min((u32)ranges->auto_corr_max_cck, val); - } - val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; - data->auto_corr_cck_mrc = - min((u32)ranges->auto_corr_max_cck_mrc, val); - } else if ((false_alarms < min_false_alarms) && - ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || - (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { - - /* Decrease auto_corr values to increase sensitivity */ - val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; - data->auto_corr_cck = - max((u32)ranges->auto_corr_min_cck, val); - val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; - data->auto_corr_cck_mrc = - max((u32)ranges->auto_corr_min_cck_mrc, val); - } - - return 0; -} - - -static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv, - u32 norm_fa, - u32 rx_enable_time) -{ - u32 val; - u32 false_alarms = norm_fa * 200 * 1024; - u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; - u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; - struct iwl_sensitivity_data *data = NULL; - const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; - - data = &(priv->sensitivity_data); - - /* If we got too many false alarms this time, reduce sensitivity */ - if (false_alarms > max_false_alarms) { - - IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", - false_alarms, max_false_alarms); - - val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm = - min((u32)ranges->auto_corr_max_ofdm, val); - - val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc = - min((u32)ranges->auto_corr_max_ofdm_mrc, val); - - val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_x1 = - min((u32)ranges->auto_corr_max_ofdm_x1, val); - - val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc_x1 = - min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); - } - - /* Else if we got fewer than desired, increase sensitivity */ - else if (false_alarms < min_false_alarms) { - - IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", - false_alarms, min_false_alarms); - - val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm = - max((u32)ranges->auto_corr_min_ofdm, val); - - val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc = - max((u32)ranges->auto_corr_min_ofdm_mrc, val); - - val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_x1 = - max((u32)ranges->auto_corr_min_ofdm_x1, val); - - val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; - data->auto_corr_ofdm_mrc_x1 = - max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); - } else { - IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", - min_false_alarms, false_alarms, max_false_alarms); - } - return 0; -} - -static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, - struct iwl_sensitivity_data *data, - __le16 *tbl) -{ - tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm); - tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm_mrc); - tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm_x1); - tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); - - tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = - cpu_to_le16((u16)data->auto_corr_cck); - tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16((u16)data->auto_corr_cck_mrc); - - tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = - cpu_to_le16((u16)data->nrg_th_cck); - tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = - cpu_to_le16((u16)data->nrg_th_ofdm); - - tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = - cpu_to_le16(data->barker_corr_th_min); - tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = - cpu_to_le16(data->barker_corr_th_min_mrc); - tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = - cpu_to_le16(data->nrg_th_cca); - - IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", - data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, - data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, - data->nrg_th_ofdm); - - IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", - data->auto_corr_cck, data->auto_corr_cck_mrc, - data->nrg_th_cck); -} - -/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ -static int iwl_sensitivity_write(struct iwl_priv *priv) -{ - struct iwl_sensitivity_cmd cmd; - struct iwl_sensitivity_data *data = NULL; - struct iwl_host_cmd cmd_out = { - .id = SENSITIVITY_CMD, - .len = { sizeof(struct iwl_sensitivity_cmd), }, - .flags = CMD_ASYNC, - .data = { &cmd, }, - }; - - data = &(priv->sensitivity_data); - - memset(&cmd, 0, sizeof(cmd)); - - iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); - - /* Update uCode's "work" table, and copy it to DSP */ - cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; - - /* Don't send command to uCode if nothing has changed */ - if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), - sizeof(u16)*HD_TABLE_SIZE)) { - IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); - return 0; - } - - /* Copy table for comparison next time */ - memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), - sizeof(u16)*HD_TABLE_SIZE); - - return iwl_dvm_send_cmd(priv, &cmd_out); -} - -/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ -static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) -{ - struct iwl_enhance_sensitivity_cmd cmd; - struct iwl_sensitivity_data *data = NULL; - struct iwl_host_cmd cmd_out = { - .id = SENSITIVITY_CMD, - .len = { sizeof(struct iwl_enhance_sensitivity_cmd), }, - .flags = CMD_ASYNC, - .data = { &cmd, }, - }; - - data = &(priv->sensitivity_data); - - memset(&cmd, 0, sizeof(cmd)); - - iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); - - if (priv->cfg->base_params->hd_v2) { - cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = - HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; - cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = - HD_INA_NON_SQUARE_DET_CCK_DATA_V2; - cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = - HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2; - } else { - cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = - HD_INA_NON_SQUARE_DET_OFDM_DATA_V1; - cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = - HD_INA_NON_SQUARE_DET_CCK_DATA_V1; - cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] = - HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] = - HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1; - cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] = - HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1; - cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] = - HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1; - } - - /* Update uCode's "work" table, and copy it to DSP */ - cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; - - /* Don't send command to uCode if nothing has changed */ - if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]), - sizeof(u16)*HD_TABLE_SIZE) && - !memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX], - &(priv->enhance_sensitivity_tbl[0]), - sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) { - IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); - return 0; - } - - /* Copy table for comparison next time */ - memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]), - sizeof(u16)*HD_TABLE_SIZE); - memcpy(&(priv->enhance_sensitivity_tbl[0]), - &(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]), - sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES); - - return iwl_dvm_send_cmd(priv, &cmd_out); -} - -void iwl_init_sensitivity(struct iwl_priv *priv) -{ - int ret = 0; - int i; - struct iwl_sensitivity_data *data = NULL; - const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; - - if (priv->calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) - return; - - IWL_DEBUG_CALIB(priv, "Start iwl_init_sensitivity\n"); - - /* Clear driver's sensitivity algo data */ - data = &(priv->sensitivity_data); - - if (ranges == NULL) - return; - - memset(data, 0, sizeof(struct iwl_sensitivity_data)); - - data->num_in_cck_no_fa = 0; - data->nrg_curr_state = IWL_FA_TOO_MANY; - data->nrg_prev_state = IWL_FA_TOO_MANY; - data->nrg_silence_ref = 0; - data->nrg_silence_idx = 0; - data->nrg_energy_idx = 0; - - for (i = 0; i < 10; i++) - data->nrg_value[i] = 0; - - for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) - data->nrg_silence_rssi[i] = 0; - - data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; - data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; - data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; - data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; - data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; - data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; - data->nrg_th_cck = ranges->nrg_th_cck; - data->nrg_th_ofdm = ranges->nrg_th_ofdm; - data->barker_corr_th_min = ranges->barker_corr_th_min; - data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; - data->nrg_th_cca = ranges->nrg_th_cca; - - data->last_bad_plcp_cnt_ofdm = 0; - data->last_fa_cnt_ofdm = 0; - data->last_bad_plcp_cnt_cck = 0; - data->last_fa_cnt_cck = 0; - - if (priv->fw->enhance_sensitivity_table) - ret |= iwl_enhance_sensitivity_write(priv); - else - ret |= iwl_sensitivity_write(priv); - IWL_DEBUG_CALIB(priv, "<calib_disabled & IWL_SENSITIVITY_CALIB_DISABLED) - return; - - data = &(priv->sensitivity_data); - - if (!iwl_is_any_associated(priv)) { - IWL_DEBUG_CALIB(priv, "<< - not associated\n"); - return; - } - - spin_lock_bh(&priv->statistics.lock); - rx_info = &priv->statistics.rx_non_phy; - ofdm = &priv->statistics.rx_ofdm; - cck = &priv->statistics.rx_cck; - if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { - IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); - spin_unlock_bh(&priv->statistics.lock); - return; - } - - /* Extract Statistics: */ - rx_enable_time = le32_to_cpu(rx_info->channel_load); - fa_cck = le32_to_cpu(cck->false_alarm_cnt); - fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); - bad_plcp_cck = le32_to_cpu(cck->plcp_err); - bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); - - statis.beacon_silence_rssi_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a); - statis.beacon_silence_rssi_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b); - statis.beacon_silence_rssi_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c); - statis.beacon_energy_a = - le32_to_cpu(rx_info->beacon_energy_a); - statis.beacon_energy_b = - le32_to_cpu(rx_info->beacon_energy_b); - statis.beacon_energy_c = - le32_to_cpu(rx_info->beacon_energy_c); - - spin_unlock_bh(&priv->statistics.lock); - - IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); - - if (!rx_enable_time) { - IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); - return; - } - - /* These statistics increase monotonically, and do not reset - * at each beacon. Calculate difference from last value, or just - * use the new statistics value if it has reset or wrapped around. */ - if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) - data->last_bad_plcp_cnt_cck = bad_plcp_cck; - else { - bad_plcp_cck -= data->last_bad_plcp_cnt_cck; - data->last_bad_plcp_cnt_cck += bad_plcp_cck; - } - - if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) - data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; - else { - bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; - data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; - } - - if (data->last_fa_cnt_ofdm > fa_ofdm) - data->last_fa_cnt_ofdm = fa_ofdm; - else { - fa_ofdm -= data->last_fa_cnt_ofdm; - data->last_fa_cnt_ofdm += fa_ofdm; - } - - if (data->last_fa_cnt_cck > fa_cck) - data->last_fa_cnt_cck = fa_cck; - else { - fa_cck -= data->last_fa_cnt_cck; - data->last_fa_cnt_cck += fa_cck; - } - - /* Total aborted signal locks */ - norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; - norm_fa_cck = fa_cck + bad_plcp_cck; - - IWL_DEBUG_CALIB(priv, "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, - bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); - - iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); - iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); - if (priv->fw->enhance_sensitivity_table) - iwl_enhance_sensitivity_write(priv); - else - iwl_sensitivity_write(priv); -} - -static inline u8 find_first_chain(u8 mask) -{ - if (mask & ANT_A) - return CHAIN_A; - if (mask & ANT_B) - return CHAIN_B; - return CHAIN_C; -} - -/** - * Run disconnected antenna algorithm to find out which antennas are - * disconnected. - */ -static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, - struct iwl_chain_noise_data *data) -{ - u32 active_chains = 0; - u32 max_average_sig; - u16 max_average_sig_antenna_i; - u8 num_tx_chains; - u8 first_chain; - u16 i = 0; - - average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; - average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; - average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; - - if (average_sig[0] >= average_sig[1]) { - max_average_sig = average_sig[0]; - max_average_sig_antenna_i = 0; - active_chains = (1 << max_average_sig_antenna_i); - } else { - max_average_sig = average_sig[1]; - max_average_sig_antenna_i = 1; - active_chains = (1 << max_average_sig_antenna_i); - } - - if (average_sig[2] >= max_average_sig) { - max_average_sig = average_sig[2]; - max_average_sig_antenna_i = 2; - active_chains = (1 << max_average_sig_antenna_i); - } - - IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", - average_sig[0], average_sig[1], average_sig[2]); - IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", - max_average_sig, max_average_sig_antenna_i); - - /* Compare signal strengths for all 3 receivers. */ - for (i = 0; i < NUM_RX_CHAINS; i++) { - if (i != max_average_sig_antenna_i) { - s32 rssi_delta = (max_average_sig - average_sig[i]); - - /* If signal is very weak, compared with - * strongest, mark it as disconnected. */ - if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) - data->disconn_array[i] = 1; - else - active_chains |= (1 << i); - IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " - "disconn_array[i] = %d\n", - i, rssi_delta, data->disconn_array[i]); - } - } - - /* - * The above algorithm sometimes fails when the ucode - * reports 0 for all chains. It's not clear why that - * happens to start with, but it is then causing trouble - * because this can make us enable more chains than the - * hardware really has. - * - * To be safe, simply mask out any chains that we know - * are not on the device. - */ - active_chains &= priv->hw_params.valid_rx_ant; - - num_tx_chains = 0; - for (i = 0; i < NUM_RX_CHAINS; i++) { - /* loops on all the bits of - * priv->hw_setting.valid_tx_ant */ - u8 ant_msk = (1 << i); - if (!(priv->hw_params.valid_tx_ant & ant_msk)) - continue; - - num_tx_chains++; - if (data->disconn_array[i] == 0) - /* there is a Tx antenna connected */ - break; - if (num_tx_chains == priv->hw_params.tx_chains_num && - data->disconn_array[i]) { - /* - * If all chains are disconnected - * connect the first valid tx chain - */ - first_chain = - find_first_chain(priv->hw_params.valid_tx_ant); - data->disconn_array[first_chain] = 0; - active_chains |= BIT(first_chain); - IWL_DEBUG_CALIB(priv, - "All Tx chains are disconnected W/A - declare %d as connected\n", - first_chain); - break; - } - } - - if (active_chains != priv->hw_params.valid_rx_ant && - active_chains != priv->chain_noise_data.active_chains) - IWL_DEBUG_CALIB(priv, - "Detected that not all antennas are connected! " - "Connected: %#x, valid: %#x.\n", - active_chains, - priv->hw_params.valid_rx_ant); - - /* Save for use within RXON, TX, SCAN commands, etc. */ - data->active_chains = active_chains; - IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", - active_chains); -} - -static void iwlagn_gain_computation(struct iwl_priv *priv, - u32 average_noise[NUM_RX_CHAINS], - u8 default_chain) -{ - int i; - s32 delta_g; - struct iwl_chain_noise_data *data = &priv->chain_noise_data; - - /* - * Find Gain Code for the chains based on "default chain" - */ - for (i = default_chain + 1; i < NUM_RX_CHAINS; i++) { - if ((data->disconn_array[i])) { - data->delta_gain_code[i] = 0; - continue; - } - - delta_g = (priv->cfg->base_params->chain_noise_scale * - ((s32)average_noise[default_chain] - - (s32)average_noise[i])) / 1500; - - /* bound gain by 2 bits value max, 3rd bit is sign */ - data->delta_gain_code[i] = - min(abs(delta_g), - (long) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); - - if (delta_g < 0) - /* - * set negative sign ... - * note to Intel developers: This is uCode API format, - * not the format of any internal device registers. - * Do not change this format for e.g. 6050 or similar - * devices. Change format only if more resolution - * (i.e. more than 2 bits magnitude) is needed. - */ - data->delta_gain_code[i] |= (1 << 2); - } - - IWL_DEBUG_CALIB(priv, "Delta gains: ANT_B = %d ANT_C = %d\n", - data->delta_gain_code[1], data->delta_gain_code[2]); - - if (!data->radio_write) { - struct iwl_calib_chain_noise_gain_cmd cmd; - - memset(&cmd, 0, sizeof(cmd)); - - iwl_set_calib_hdr(&cmd.hdr, - priv->phy_calib_chain_noise_gain_cmd); - cmd.delta_gain_1 = data->delta_gain_code[1]; - cmd.delta_gain_2 = data->delta_gain_code[2]; - iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, - CMD_ASYNC, sizeof(cmd), &cmd); - - data->radio_write = 1; - data->state = IWL_CHAIN_NOISE_CALIBRATED; - } -} - -/* - * Accumulate 16 beacons of signal and noise statistics for each of - * 3 receivers/antennas/rx-chains, then figure out: - * 1) Which antennas are connected. - * 2) Differential rx gain settings to balance the 3 receivers. - */ -void iwl_chain_noise_calibration(struct iwl_priv *priv) -{ - struct iwl_chain_noise_data *data = NULL; - - u32 chain_noise_a; - u32 chain_noise_b; - u32 chain_noise_c; - u32 chain_sig_a; - u32 chain_sig_b; - u32 chain_sig_c; - u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; - u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; - u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; - u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; - u16 i = 0; - u16 rxon_chnum = INITIALIZATION_VALUE; - u16 stat_chnum = INITIALIZATION_VALUE; - u8 rxon_band24; - u8 stat_band24; - struct statistics_rx_non_phy *rx_info; - - /* - * MULTI-FIXME: - * When we support multiple interfaces on different channels, - * this must be modified/fixed. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED) - return; - - data = &(priv->chain_noise_data); - - /* - * Accumulate just the first "chain_noise_num_beacons" after - * the first association, then we're done forever. - */ - if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { - if (data->state == IWL_CHAIN_NOISE_ALIVE) - IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); - return; - } - - spin_lock_bh(&priv->statistics.lock); - - rx_info = &priv->statistics.rx_non_phy; - - if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { - IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); - spin_unlock_bh(&priv->statistics.lock); - return; - } - - rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); - rxon_chnum = le16_to_cpu(ctx->staging.channel); - stat_band24 = - !!(priv->statistics.flag & STATISTICS_REPLY_FLG_BAND_24G_MSK); - stat_chnum = le32_to_cpu(priv->statistics.flag) >> 16; - - /* Make sure we accumulate data for just the associated channel - * (even if scanning). */ - if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { - IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", - rxon_chnum, rxon_band24); - spin_unlock_bh(&priv->statistics.lock); - return; - } - - /* - * Accumulate beacon statistics values across - * "chain_noise_num_beacons" - */ - chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & - IN_BAND_FILTER; - chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & - IN_BAND_FILTER; - chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & - IN_BAND_FILTER; - - chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; - chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; - chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; - - spin_unlock_bh(&priv->statistics.lock); - - data->beacon_count++; - - data->chain_noise_a = (chain_noise_a + data->chain_noise_a); - data->chain_noise_b = (chain_noise_b + data->chain_noise_b); - data->chain_noise_c = (chain_noise_c + data->chain_noise_c); - - data->chain_signal_a = (chain_sig_a + data->chain_signal_a); - data->chain_signal_b = (chain_sig_b + data->chain_signal_b); - data->chain_signal_c = (chain_sig_c + data->chain_signal_c); - - IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", - rxon_chnum, rxon_band24, data->beacon_count); - IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", - chain_sig_a, chain_sig_b, chain_sig_c); - IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", - chain_noise_a, chain_noise_b, chain_noise_c); - - /* If this is the "chain_noise_num_beacons", determine: - * 1) Disconnected antennas (using signal strengths) - * 2) Differential gain (using silence noise) to balance receivers */ - if (data->beacon_count != IWL_CAL_NUM_BEACONS) - return; - - /* Analyze signal for disconnected antenna */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - /* Disable disconnected antenna algorithm for advanced - bt coex, assuming valid antennas are connected */ - data->active_chains = priv->hw_params.valid_rx_ant; - for (i = 0; i < NUM_RX_CHAINS; i++) - if (!(data->active_chains & (1<disconn_array[i] = 1; - } else - iwl_find_disconn_antenna(priv, average_sig, data); - - /* Analyze noise for rx balance */ - average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; - average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; - average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; - - for (i = 0; i < NUM_RX_CHAINS; i++) { - if (!(data->disconn_array[i]) && - (average_noise[i] <= min_average_noise)) { - /* This means that chain i is active and has - * lower noise values so far: */ - min_average_noise = average_noise[i]; - min_average_noise_antenna_i = i; - } - } - - IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", - average_noise[0], average_noise[1], - average_noise[2]); - - IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", - min_average_noise, min_average_noise_antenna_i); - - iwlagn_gain_computation(priv, average_noise, - find_first_chain(priv->hw_params.valid_rx_ant)); - - /* Some power changes may have been made during the calibration. - * Update and commit the RXON - */ - iwl_update_chain_flags(priv); - - data->state = IWL_CHAIN_NOISE_DONE; - iwl_power_update_mode(priv, false); -} - -void iwl_reset_run_time_calib(struct iwl_priv *priv) -{ - int i; - memset(&(priv->sensitivity_data), 0, - sizeof(struct iwl_sensitivity_data)); - memset(&(priv->chain_noise_data), 0, - sizeof(struct iwl_chain_noise_data)); - for (i = 0; i < NUM_RX_CHAINS; i++) - priv->chain_noise_data.delta_gain_code[i] = - CHAIN_NOISE_DELTA_GAIN_INIT_VAL; - - /* Ask for statistics now, the uCode will send notification - * periodically after association */ - iwl_send_statistics_request(priv, CMD_ASYNC, true); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.h b/drivers/net/wireless/iwlwifi/iwl-agn-calib.h deleted file mode 100644 index dbe13787f272..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.h +++ /dev/null @@ -1,74 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ -#ifndef __iwl_calib_h__ -#define __iwl_calib_h__ - -#include "iwl-dev.h" -#include "iwl-commands.h" - -void iwl_chain_noise_calibration(struct iwl_priv *priv); -void iwl_sensitivity_calibration(struct iwl_priv *priv); - -void iwl_init_sensitivity(struct iwl_priv *priv); -void iwl_reset_run_time_calib(struct iwl_priv *priv); - -#endif /* __iwl_calib_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-devices.c b/drivers/net/wireless/iwlwifi/iwl-agn-devices.c deleted file mode 100644 index 529cdac6f739..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-devices.c +++ /dev/null @@ -1,739 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -/* - * DVM device-specific data & functions - */ -#include "iwl-agn.h" -#include "iwl-dev.h" -#include "iwl-commands.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -/* - * 1000 series - * =========== - */ - -/* - * For 1000, use advance thermal throttling critical temperature threshold, - * but legacy thermal management implementation for now. - * This is for the reason of 1000 uCode using advance thermal throttling API - * but not implement ct_kill_exit based on ct_kill exit temperature - * so the thermal throttling will still based on legacy thermal throttling - * management. - * The code here need to be modified once 1000 uCode has the advanced thermal - * throttling algorithm in place - */ -static void iwl1000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; - priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; -} - -/* NIC configuration for 1000 series */ -static void iwl1000_nic_config(struct iwl_priv *priv) -{ - /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); - - /* Setting digital SVR for 1000 card to 1.32V */ - /* locking is acquired in iwl_set_bits_mask_prph() function */ - iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG, - APMG_SVR_DIGITAL_VOLTAGE_1_32, - ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); -} - -/** - * iwl_beacon_time_mask_low - mask of lower 32 bit of beacon time - * @priv -- pointer to iwl_priv data structure - * @tsf_bits -- number of bits need to shift for masking) - */ -static inline u32 iwl_beacon_time_mask_low(struct iwl_priv *priv, - u16 tsf_bits) -{ - return (1 << tsf_bits) - 1; -} - -/** - * iwl_beacon_time_mask_high - mask of higher 32 bit of beacon time - * @priv -- pointer to iwl_priv data structure - * @tsf_bits -- number of bits need to shift for masking) - */ -static inline u32 iwl_beacon_time_mask_high(struct iwl_priv *priv, - u16 tsf_bits) -{ - return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; -} - -/* - * extended beacon time format - * time in usec will be changed into a 32-bit value in extended:internal format - * the extended part is the beacon counts - * the internal part is the time in usec within one beacon interval - */ -static u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, - u32 beacon_interval) -{ - u32 quot; - u32 rem; - u32 interval = beacon_interval * TIME_UNIT; - - if (!interval || !usec) - return 0; - - quot = (usec / interval) & - (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >> - IWLAGN_EXT_BEACON_TIME_POS); - rem = (usec % interval) & iwl_beacon_time_mask_low(priv, - IWLAGN_EXT_BEACON_TIME_POS); - - return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem; -} - -/* base is usually what we get from ucode with each received frame, - * the same as HW timer counter counting down - */ -static __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, - u32 addon, u32 beacon_interval) -{ - u32 base_low = base & iwl_beacon_time_mask_low(priv, - IWLAGN_EXT_BEACON_TIME_POS); - u32 addon_low = addon & iwl_beacon_time_mask_low(priv, - IWLAGN_EXT_BEACON_TIME_POS); - u32 interval = beacon_interval * TIME_UNIT; - u32 res = (base & iwl_beacon_time_mask_high(priv, - IWLAGN_EXT_BEACON_TIME_POS)) + - (addon & iwl_beacon_time_mask_high(priv, - IWLAGN_EXT_BEACON_TIME_POS)); - - if (base_low > addon_low) - res += base_low - addon_low; - else if (base_low < addon_low) { - res += interval + base_low - addon_low; - res += (1 << IWLAGN_EXT_BEACON_TIME_POS); - } else - res += (1 << IWLAGN_EXT_BEACON_TIME_POS); - - return cpu_to_le32(res); -} - -static const struct iwl_sensitivity_ranges iwl1000_sensitivity = { - .min_nrg_cck = 95, - .auto_corr_min_ofdm = 90, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 120, - .auto_corr_min_ofdm_mrc_x1 = 240, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - .auto_corr_max_ofdm_x1 = 155, - .auto_corr_max_ofdm_mrc_x1 = 290, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 170, - .auto_corr_max_cck_mrc = 400, - .nrg_th_cck = 95, - .nrg_th_ofdm = 95, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) -{ - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); - - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - - iwl1000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl1000_sensitivity; -} - -struct iwl_lib_ops iwl1000_lib = { - .set_hw_params = iwl1000_hw_set_hw_params, - .nic_config = iwl1000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - }, - .temperature = iwlagn_temperature, -}; - - -/* - * 2000 series - * =========== - */ - -static void iwl2000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; - priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; -} - -/* NIC configuration for 2000 series */ -static void iwl2000_nic_config(struct iwl_priv *priv) -{ - iwl_rf_config(priv); - - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); -} - -static const struct iwl_sensitivity_ranges iwl2000_sensitivity = { - .min_nrg_cck = 97, - .auto_corr_min_ofdm = 80, - .auto_corr_min_ofdm_mrc = 128, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 192, - - .auto_corr_max_ofdm = 145, - .auto_corr_max_ofdm_mrc = 232, - .auto_corr_max_ofdm_x1 = 110, - .auto_corr_max_ofdm_mrc_x1 = 232, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 175, - .auto_corr_min_cck_mrc = 160, - .auto_corr_max_cck_mrc = 310, - .nrg_th_cck = 97, - .nrg_th_ofdm = 100, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) -{ - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); - - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - - iwl2000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl2000_sensitivity; -} - -struct iwl_lib_ops iwl2000_lib = { - .set_hw_params = iwl2000_hw_set_hw_params, - .nic_config = iwl2000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - .enhanced_txpower = true, - }, - .temperature = iwlagn_temperature, -}; - -struct iwl_lib_ops iwl2030_lib = { - .set_hw_params = iwl2000_hw_set_hw_params, - .nic_config = iwl2000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - .enhanced_txpower = true, - }, - .temperature = iwlagn_temperature, -}; - -/* - * 5000 series - * =========== - */ - -/* NIC configuration for 5000 series */ -static void iwl5000_nic_config(struct iwl_priv *priv) -{ - iwl_rf_config(priv); - - /* W/A : NIC is stuck in a reset state after Early PCIe power off - * (PCIe power is lost before PERST# is asserted), - * causing ME FW to lose ownership and not being able to obtain it back. - */ - iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); -} - -static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { - .min_nrg_cck = 100, - .auto_corr_min_ofdm = 90, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 220, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - .auto_corr_max_ofdm_x1 = 120, - .auto_corr_max_ofdm_mrc_x1 = 240, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 200, - .auto_corr_max_cck_mrc = 400, - .nrg_th_cck = 100, - .nrg_th_ofdm = 100, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -static struct iwl_sensitivity_ranges iwl5150_sensitivity = { - .min_nrg_cck = 95, - .auto_corr_min_ofdm = 90, - .auto_corr_min_ofdm_mrc = 170, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 220, - - .auto_corr_max_ofdm = 120, - .auto_corr_max_ofdm_mrc = 210, - /* max = min for performance bug in 5150 DSP */ - .auto_corr_max_ofdm_x1 = 105, - .auto_corr_max_ofdm_mrc_x1 = 220, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 200, - .auto_corr_min_cck_mrc = 170, - .auto_corr_max_cck_mrc = 400, - .nrg_th_cck = 95, - .nrg_th_ofdm = 95, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 390, - .nrg_th_cca = 62, -}; - -#define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5) - -static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) -{ - u16 temperature, voltage; - __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(priv, - EEPROM_KELVIN_TEMPERATURE); - - temperature = le16_to_cpu(temp_calib[0]); - voltage = le16_to_cpu(temp_calib[1]); - - /* offset = temp - volt / coeff */ - return (s32)(temperature - - voltage / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF); -} - -static void iwl5150_set_ct_threshold(struct iwl_priv *priv) -{ - const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; - s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - - iwl_temp_calib_to_offset(priv); - - priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; -} - -static void iwl5000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; -} - -static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) -{ - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | - BIT(IEEE80211_BAND_5GHZ); - - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - - iwl5000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl5000_sensitivity; -} - -static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) -{ - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | - BIT(IEEE80211_BAND_5GHZ); - - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - - iwl5150_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl5150_sensitivity; -} - -static void iwl5150_temperature(struct iwl_priv *priv) -{ - u32 vt = 0; - s32 offset = iwl_temp_calib_to_offset(priv); - - vt = le32_to_cpu(priv->statistics.common.temperature); - vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; - /* now vt hold the temperature in Kelvin */ - priv->temperature = KELVIN_TO_CELSIUS(vt); - iwl_tt_handler(priv); -} - -static int iwl5000_hw_channel_switch(struct iwl_priv *priv, - struct ieee80211_channel_switch *ch_switch) -{ - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl5000_channel_switch_cmd cmd; - u32 switch_time_in_usec, ucode_switch_time; - u16 ch; - u32 tsf_low; - u8 switch_count; - u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); - struct ieee80211_vif *vif = ctx->vif; - struct iwl_host_cmd hcmd = { - .id = REPLY_CHANNEL_SWITCH, - .len = { sizeof(cmd), }, - .flags = CMD_SYNC, - .data = { &cmd, }, - }; - - cmd.band = priv->band == IEEE80211_BAND_2GHZ; - ch = ch_switch->channel->hw_value; - IWL_DEBUG_11H(priv, "channel switch from %d to %d\n", - ctx->active.channel, ch); - cmd.channel = cpu_to_le16(ch); - cmd.rxon_flags = ctx->staging.flags; - cmd.rxon_filter_flags = ctx->staging.filter_flags; - switch_count = ch_switch->count; - tsf_low = ch_switch->timestamp & 0x0ffffffff; - /* - * calculate the ucode channel switch time - * adding TSF as one of the factor for when to switch - */ - if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { - if (switch_count > ((priv->ucode_beacon_time - tsf_low) / - beacon_interval)) { - switch_count -= (priv->ucode_beacon_time - - tsf_low) / beacon_interval; - } else - switch_count = 0; - } - if (switch_count <= 1) - cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); - else { - switch_time_in_usec = - vif->bss_conf.beacon_int * switch_count * TIME_UNIT; - ucode_switch_time = iwl_usecs_to_beacons(priv, - switch_time_in_usec, - beacon_interval); - cmd.switch_time = iwl_add_beacon_time(priv, - priv->ucode_beacon_time, - ucode_switch_time, - beacon_interval); - } - IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", - cmd.switch_time); - cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; - - return iwl_dvm_send_cmd(priv, &hcmd); -} - -struct iwl_lib_ops iwl5000_lib = { - .set_hw_params = iwl5000_hw_set_hw_params, - .set_channel_switch = iwl5000_hw_channel_switch, - .nic_config = iwl5000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - }, - .temperature = iwlagn_temperature, -}; - -struct iwl_lib_ops iwl5150_lib = { - .set_hw_params = iwl5150_hw_set_hw_params, - .set_channel_switch = iwl5000_hw_channel_switch, - .nic_config = iwl5000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - }, - .temperature = iwl5150_temperature, -}; - - - -/* - * 6000 series - * =========== - */ - -static void iwl6000_set_ct_threshold(struct iwl_priv *priv) -{ - /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; - priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; -} - -/* NIC configuration for 6000 series */ -static void iwl6000_nic_config(struct iwl_priv *priv) -{ - iwl_rf_config(priv); - - switch (priv->cfg->device_family) { - case IWL_DEVICE_FAMILY_6005: - case IWL_DEVICE_FAMILY_6030: - case IWL_DEVICE_FAMILY_6000: - break; - case IWL_DEVICE_FAMILY_6000i: - /* 2x2 IPA phy type */ - iwl_write32(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); - break; - case IWL_DEVICE_FAMILY_6050: - /* Indicate calibration version to uCode. */ - if (iwl_eeprom_calib_version(priv) >= 6) - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); - break; - case IWL_DEVICE_FAMILY_6150: - /* Indicate calibration version to uCode. */ - if (iwl_eeprom_calib_version(priv) >= 6) - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, - CSR_GP_DRIVER_REG_BIT_6050_1x2); - break; - default: - WARN_ON(1); - } -} - -static const struct iwl_sensitivity_ranges iwl6000_sensitivity = { - .min_nrg_cck = 110, - .auto_corr_min_ofdm = 80, - .auto_corr_min_ofdm_mrc = 128, - .auto_corr_min_ofdm_x1 = 105, - .auto_corr_min_ofdm_mrc_x1 = 192, - - .auto_corr_max_ofdm = 145, - .auto_corr_max_ofdm_mrc = 232, - .auto_corr_max_ofdm_x1 = 110, - .auto_corr_max_ofdm_mrc_x1 = 232, - - .auto_corr_min_cck = 125, - .auto_corr_max_cck = 175, - .auto_corr_min_cck_mrc = 160, - .auto_corr_max_cck_mrc = 310, - .nrg_th_cck = 110, - .nrg_th_ofdm = 110, - - .barker_corr_th_min = 190, - .barker_corr_th_min_mrc = 336, - .nrg_th_cca = 62, -}; - -static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) -{ - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | - BIT(IEEE80211_BAND_5GHZ); - - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - - iwl6000_set_ct_threshold(priv); - - /* Set initial sensitivity parameters */ - priv->hw_params.sens = &iwl6000_sensitivity; - -} - -static int iwl6000_hw_channel_switch(struct iwl_priv *priv, - struct ieee80211_channel_switch *ch_switch) -{ - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl6000_channel_switch_cmd cmd; - u32 switch_time_in_usec, ucode_switch_time; - u16 ch; - u32 tsf_low; - u8 switch_count; - u16 beacon_interval = le16_to_cpu(ctx->timing.beacon_interval); - struct ieee80211_vif *vif = ctx->vif; - struct iwl_host_cmd hcmd = { - .id = REPLY_CHANNEL_SWITCH, - .len = { sizeof(cmd), }, - .flags = CMD_SYNC, - .data = { &cmd, }, - }; - - cmd.band = priv->band == IEEE80211_BAND_2GHZ; - ch = ch_switch->channel->hw_value; - IWL_DEBUG_11H(priv, "channel switch from %u to %u\n", - ctx->active.channel, ch); - cmd.channel = cpu_to_le16(ch); - cmd.rxon_flags = ctx->staging.flags; - cmd.rxon_filter_flags = ctx->staging.filter_flags; - switch_count = ch_switch->count; - tsf_low = ch_switch->timestamp & 0x0ffffffff; - /* - * calculate the ucode channel switch time - * adding TSF as one of the factor for when to switch - */ - if ((priv->ucode_beacon_time > tsf_low) && beacon_interval) { - if (switch_count > ((priv->ucode_beacon_time - tsf_low) / - beacon_interval)) { - switch_count -= (priv->ucode_beacon_time - - tsf_low) / beacon_interval; - } else - switch_count = 0; - } - if (switch_count <= 1) - cmd.switch_time = cpu_to_le32(priv->ucode_beacon_time); - else { - switch_time_in_usec = - vif->bss_conf.beacon_int * switch_count * TIME_UNIT; - ucode_switch_time = iwl_usecs_to_beacons(priv, - switch_time_in_usec, - beacon_interval); - cmd.switch_time = iwl_add_beacon_time(priv, - priv->ucode_beacon_time, - ucode_switch_time, - beacon_interval); - } - IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", - cmd.switch_time); - cmd.expect_beacon = ch_switch->channel->flags & IEEE80211_CHAN_RADAR; - - return iwl_dvm_send_cmd(priv, &hcmd); -} - -struct iwl_lib_ops iwl6000_lib = { - .set_hw_params = iwl6000_hw_set_hw_params, - .set_channel_switch = iwl6000_hw_channel_switch, - .nic_config = iwl6000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - .enhanced_txpower = true, - }, - .temperature = iwlagn_temperature, -}; - -struct iwl_lib_ops iwl6030_lib = { - .set_hw_params = iwl6000_hw_set_hw_params, - .set_channel_switch = iwl6000_hw_channel_switch, - .nic_config = iwl6000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - .enhanced_txpower = true, - }, - .temperature = iwlagn_temperature, -}; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c deleted file mode 100644 index c31072d42791..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ /dev/null @@ -1,1290 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include - -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn-hw.h" -#include "iwl-agn.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" - -int iwlagn_hw_valid_rtc_data_addr(u32 addr) -{ - return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && - (addr < IWLAGN_RTC_DATA_UPPER_BOUND); -} - -int iwlagn_send_tx_power(struct iwl_priv *priv) -{ - struct iwlagn_tx_power_dbm_cmd tx_power_cmd; - u8 tx_ant_cfg_cmd; - - if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), - "TX Power requested while scanning!\n")) - return -EAGAIN; - - /* half dBm need to multiply */ - tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); - - if (priv->tx_power_lmt_in_half_dbm && - priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) { - /* - * For the newer devices which using enhanced/extend tx power - * table in EEPROM, the format is in half dBm. driver need to - * convert to dBm format before report to mac80211. - * By doing so, there is a possibility of 1/2 dBm resolution - * lost. driver will perform "round-up" operation before - * reporting, but it will cause 1/2 dBm tx power over the - * regulatory limit. Perform the checking here, if the - * "tx_power_user_lmt" is higher than EEPROM value (in - * half-dBm format), lower the tx power based on EEPROM - */ - tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm; - } - tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; - tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; - - if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) - tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; - else - tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; - - return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC, - sizeof(tx_power_cmd), &tx_power_cmd); -} - -void iwlagn_temperature(struct iwl_priv *priv) -{ - lockdep_assert_held(&priv->statistics.lock); - - /* store temperature from correct statistics (in Celsius) */ - priv->temperature = le32_to_cpu(priv->statistics.common.temperature); - iwl_tt_handler(priv); -} - -int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) -{ - int idx = 0; - int band_offset = 0; - - /* HT rate format: mac80211 wants an MCS number, which is just LSB */ - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = (rate_n_flags & 0xff); - return idx; - /* Legacy rate format, search for match in table */ - } else { - if (band == IEEE80211_BAND_5GHZ) - band_offset = IWL_FIRST_OFDM_RATE; - for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) - if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) - return idx - band_offset; - } - - return -1; -} - -int iwlagn_manage_ibss_station(struct iwl_priv *priv, - struct ieee80211_vif *vif, bool add) -{ - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - - if (add) - return iwlagn_add_bssid_station(priv, vif_priv->ctx, - vif->bss_conf.bssid, - &vif_priv->ibss_bssid_sta_id); - return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, - vif->bss_conf.bssid); -} - -/** - * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode - * - * pre-requirements: - * 1. acquire mutex before calling - * 2. make sure rf is on and not in exit state - */ -int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) -{ - struct iwl_txfifo_flush_cmd flush_cmd; - struct iwl_host_cmd cmd = { - .id = REPLY_TXFIFO_FLUSH, - .len = { sizeof(struct iwl_txfifo_flush_cmd), }, - .flags = CMD_SYNC, - .data = { &flush_cmd, }, - }; - - might_sleep(); - - memset(&flush_cmd, 0, sizeof(flush_cmd)); - if (flush_control & BIT(IWL_RXON_CTX_BSS)) - flush_cmd.fifo_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | - IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | - IWL_SCD_MGMT_MSK; - if ((flush_control & BIT(IWL_RXON_CTX_PAN)) && - (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) - flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK | - IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK | - IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | - IWL_PAN_SCD_MULTICAST_MSK; - - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) - flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; - - IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", - flush_cmd.fifo_control); - flush_cmd.flush_control = cpu_to_le16(flush_control); - - return iwl_dvm_send_cmd(priv, &cmd); -} - -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control) -{ - mutex_lock(&priv->mutex); - ieee80211_stop_queues(priv->hw); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; - } - IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans); -done: - ieee80211_wake_queues(priv->hw); - mutex_unlock(&priv->mutex); -} - -/* - * BT coex - */ -/* Notmal TDM */ -static const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0x00004000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - - -/* Loose Coex */ -static const __le32 iwlagn_loose_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - -/* Full concurrency */ -static const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), -}; - -void iwlagn_send_advance_bt_config(struct iwl_priv *priv) -{ - struct iwl_basic_bt_cmd basic = { - .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, - .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, - .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, - .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, - }; - struct iwl_bt_cmd_v1 bt_cmd_v1; - struct iwl_bt_cmd_v2 bt_cmd_v2; - int ret; - - BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != - sizeof(basic.bt3_lookup_table)); - - if (priv->cfg->bt_params) { - /* - * newer generation of devices (2000 series and newer) - * use the version 2 of the bt command - * we need to make sure sending the host command - * with correct data structure to avoid uCode assert - */ - if (priv->cfg->bt_params->bt_session_2) { - bt_cmd_v2.prio_boost = cpu_to_le32( - priv->cfg->bt_params->bt_prio_boost); - bt_cmd_v2.tx_prio_boost = 0; - bt_cmd_v2.rx_prio_boost = 0; - } else { - bt_cmd_v1.prio_boost = - priv->cfg->bt_params->bt_prio_boost; - bt_cmd_v1.tx_prio_boost = 0; - bt_cmd_v1.rx_prio_boost = 0; - } - } else { - IWL_ERR(priv, "failed to construct BT Coex Config\n"); - return; - } - - /* - * Possible situations when BT needs to take over for receive, - * at the same time where STA needs to response to AP's frame(s), - * reduce the tx power of the required response frames, by that, - * allow the concurrent BT receive & WiFi transmit - * (BT - ANT A, WiFi -ANT B), without interference to one another - * - * Reduced tx power apply to control frames only (ACK/Back/CTS) - * when indicated by the BT config command - */ - basic.kill_ack_mask = priv->kill_ack_mask; - basic.kill_cts_mask = priv->kill_cts_mask; - if (priv->reduced_txpower) - basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; - basic.valid = priv->bt_valid; - - /* - * Configure BT coex mode to "no coexistence" when the - * user disabled BT coexistence, we have no interface - * (might be in monitor mode), or the interface is in - * IBSS mode (no proper uCode support for coex then). - */ - if (!iwlwifi_mod_params.bt_coex_active || - priv->iw_mode == NL80211_IFTYPE_ADHOC) { - basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; - } else { - basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << - IWLAGN_BT_FLAG_COEX_MODE_SHIFT; - - if (!priv->bt_enable_pspoll) - basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; - else - basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; - - if (priv->bt_ch_announce) - basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; - IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); - } - priv->bt_enable_flag = basic.flags; - if (priv->bt_full_concurrent) - memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, - sizeof(iwlagn_concurrent_lookup)); - else - memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, - sizeof(iwlagn_def_3w_lookup)); - - IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", - basic.flags ? "active" : "disabled", - priv->bt_full_concurrent ? - "full concurrency" : "3-wire"); - - if (priv->cfg->bt_params->bt_session_2) { - memcpy(&bt_cmd_v2.basic, &basic, - sizeof(basic)); - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2); - } else { - memcpy(&bt_cmd_v1.basic, &basic, - sizeof(basic)); - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1); - } - if (ret) - IWL_ERR(priv, "failed to send BT Coex Config\n"); - -} - -void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) -{ - struct iwl_rxon_context *ctx, *found_ctx = NULL; - bool found_ap = false; - - lockdep_assert_held(&priv->mutex); - - /* Check whether AP or GO mode is active. */ - if (rssi_ena) { - for_each_context(priv, ctx) { - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && - iwl_is_associated_ctx(ctx)) { - found_ap = true; - break; - } - } - } - - /* - * If disable was received or If GO/AP mode, disable RSSI - * measurements. - */ - if (!rssi_ena || found_ap) { - if (priv->cur_rssi_ctx) { - ctx = priv->cur_rssi_ctx; - ieee80211_disable_rssi_reports(ctx->vif); - priv->cur_rssi_ctx = NULL; - } - return; - } - - /* - * If rssi measurements need to be enabled, consider all cases now. - * Figure out how many contexts are active. - */ - for_each_context(priv, ctx) { - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && - iwl_is_associated_ctx(ctx)) { - found_ctx = ctx; - break; - } - } - - /* - * rssi monitor already enabled for the correct interface...nothing - * to do. - */ - if (found_ctx == priv->cur_rssi_ctx) - return; - - /* - * Figure out if rssi monitor is currently enabled, and needs - * to be changed. If rssi monitor is already enabled, disable - * it first else just enable rssi measurements on the - * interface found above. - */ - if (priv->cur_rssi_ctx) { - ctx = priv->cur_rssi_ctx; - if (ctx->vif) - ieee80211_disable_rssi_reports(ctx->vif); - } - - priv->cur_rssi_ctx = found_ctx; - - if (!found_ctx) - return; - - ieee80211_enable_rssi_reports(found_ctx->vif, - IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, - IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); -} - -static bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) -{ - return BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3 >> - BT_UART_MSG_FRAME3SCOESCO_POS; -} - -static void iwlagn_bt_traffic_change_work(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, bt_traffic_change_work); - struct iwl_rxon_context *ctx; - int smps_request = -1; - - if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { - /* bt coex disabled */ - return; - } - - /* - * Note: bt_traffic_load can be overridden by scan complete and - * coex profile notifications. Ignore that since only bad consequence - * can be not matching debug print with actual state. - */ - IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", - priv->bt_traffic_load); - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - if (priv->bt_status) - smps_request = IEEE80211_SMPS_DYNAMIC; - else - smps_request = IEEE80211_SMPS_AUTOMATIC; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - smps_request = IEEE80211_SMPS_DYNAMIC; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - smps_request = IEEE80211_SMPS_STATIC; - break; - default: - IWL_ERR(priv, "Invalid BT traffic load: %d\n", - priv->bt_traffic_load); - break; - } - - mutex_lock(&priv->mutex); - - /* - * We can not send command to firmware while scanning. When the scan - * complete we will schedule this work again. We do check with mutex - * locked to prevent new scan request to arrive. We do not check - * STATUS_SCANNING to avoid race when queue_work two times from - * different notifications, but quit and not perform any work at all. - */ - if (test_bit(STATUS_SCAN_HW, &priv->status)) - goto out; - - iwl_update_chain_flags(priv); - - if (smps_request != -1) { - priv->current_ht_config.smps = smps_request; - for_each_context(priv, ctx) { - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) - ieee80211_request_smps(ctx->vif, smps_request); - } - } - - /* - * Dynamic PS poll related functionality. Adjust RSSI measurements if - * necessary. - */ - iwlagn_bt_coex_rssi_monitor(priv); -out: - mutex_unlock(&priv->mutex); -} - -/* - * If BT sco traffic, and RSSI monitor is enabled, move measurements to the - * correct interface or disable it if this is the last interface to be - * removed. - */ -void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) -{ - if (priv->bt_is_sco && - priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) - iwlagn_bt_adjust_rssi_monitor(priv, true); - else - iwlagn_bt_adjust_rssi_monitor(priv, false); -} - -static void iwlagn_print_uartmsg(struct iwl_priv *priv, - struct iwl_bt_uart_msg *uart_msg) -{ - IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " - "Update Req = 0x%X\n", - (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> - BT_UART_MSG_FRAME1MSGTYPE_POS, - (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> - BT_UART_MSG_FRAME1SSN_POS, - (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> - BT_UART_MSG_FRAME1UPDATEREQ_POS); - - IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " - "Chl_SeqN = 0x%X, In band = 0x%X\n", - (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, - (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2TRAFFICLOAD_POS, - (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2CHLSEQN_POS, - (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> - BT_UART_MSG_FRAME2INBAND_POS); - - IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " - "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", - (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3SCOESCO_POS, - (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3SNIFF_POS, - (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3A2DP_POS, - (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3ACL_POS, - (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3MASTER_POS, - (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> - BT_UART_MSG_FRAME3OBEX_POS); - - IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", - (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> - BT_UART_MSG_FRAME4IDLEDURATION_POS); - - IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " - "eSCO Retransmissions = 0x%X\n", - (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> - BT_UART_MSG_FRAME5TXACTIVITY_POS, - (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> - BT_UART_MSG_FRAME5RXACTIVITY_POS, - (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> - BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); - - IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", - (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> - BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, - (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> - BT_UART_MSG_FRAME6DISCOVERABLE_POS); - - IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " - "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", - (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, - (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7PAGE_POS, - (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7INQUIRY_POS, - (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7CONNECTABLE_POS); -} - -static bool iwlagn_set_kill_msk(struct iwl_priv *priv, - struct iwl_bt_uart_msg *uart_msg) -{ - bool need_update = false; - u8 kill_msk = IWL_BT_KILL_REDUCE; - static const __le32 bt_kill_ack_msg[3] = { - IWLAGN_BT_KILL_ACK_MASK_DEFAULT, - IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, - IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; - static const __le32 bt_kill_cts_msg[3] = { - IWLAGN_BT_KILL_CTS_MASK_DEFAULT, - IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, - IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; - - if (!priv->reduced_txpower) - kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) - ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; - if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || - priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { - priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; - priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; - priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; - priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; - need_update = true; - } - return need_update; -} - -/* - * Upon RSSI changes, sends a bt config command with following changes - * 1. enable/disable "reduced control frames tx power - * 2. update the "kill)ack_mask" and "kill_cts_mask" - * - * If "reduced tx power" is enabled, uCode shall - * 1. ACK/Back/CTS rate shall reduced to 6Mbps - * 2. not use duplciate 20/40MHz mode - */ -static bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, - struct iwl_bt_uart_msg *uart_msg) -{ - bool need_update = false; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - int ave_rssi; - - if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { - IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); - return false; - } - - ave_rssi = ieee80211_ave_rssi(ctx->vif); - if (!ave_rssi) { - /* no rssi data, no changes to reduce tx power */ - IWL_DEBUG_COEX(priv, "no rssi data available\n"); - return need_update; - } - if (!priv->reduced_txpower && - !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && - (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && - (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | - BT_UART_MSG_FRAME3OBEX_MSK)) && - !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | - BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { - /* enabling reduced tx power */ - priv->reduced_txpower = true; - priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; - need_update = true; - } else if (priv->reduced_txpower && - (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || - (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || - (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | - BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || - !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | - BT_UART_MSG_FRAME3OBEX_MSK)))) { - /* disable reduced tx power */ - priv->reduced_txpower = false; - priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; - need_update = true; - } - - return need_update; -} - -int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; - struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; - - if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { - /* bt coex disabled */ - return 0; - } - - IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); - IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); - IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); - IWL_DEBUG_COEX(priv, " CI compliance: %d\n", - coex->bt_ci_compliance); - iwlagn_print_uartmsg(priv, uart_msg); - - priv->last_bt_traffic_load = priv->bt_traffic_load; - priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); - - if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { - if (priv->bt_status != coex->bt_status || - priv->last_bt_traffic_load != coex->bt_traffic_load) { - if (coex->bt_status) { - /* BT on */ - if (!priv->bt_ch_announce) - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - else - priv->bt_traffic_load = - coex->bt_traffic_load; - } else { - /* BT off */ - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_NONE; - } - priv->bt_status = coex->bt_status; - queue_work(priv->workqueue, - &priv->bt_traffic_change_work); - } - } - - /* schedule to send runtime bt_config */ - /* check reduce power before change ack/cts kill mask */ - if (iwlagn_fill_txpower_mode(priv, uart_msg) || - iwlagn_set_kill_msk(priv, uart_msg)) - queue_work(priv->workqueue, &priv->bt_runtime_config); - - - /* FIXME: based on notification, adjust the prio_boost */ - - priv->bt_ci_compliance = coex->bt_ci_compliance; - return 0; -} - -void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) -{ - priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = - iwlagn_bt_coex_profile_notif; -} - -void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) -{ - INIT_WORK(&priv->bt_traffic_change_work, - iwlagn_bt_traffic_change_work); -} - -void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) -{ - cancel_work_sync(&priv->bt_traffic_change_work); -} - -static bool is_single_rx_stream(struct iwl_priv *priv) -{ - return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || - priv->current_ht_config.single_chain_sufficient; -} - -#define IWL_NUM_RX_CHAINS_MULTIPLE 3 -#define IWL_NUM_RX_CHAINS_SINGLE 2 -#define IWL_NUM_IDLE_CHAINS_DUAL 2 -#define IWL_NUM_IDLE_CHAINS_SINGLE 1 - -/* - * Determine how many receiver/antenna chains to use. - * - * More provides better reception via diversity. Fewer saves power - * at the expense of throughput, but only when not in powersave to - * start with. - * - * MIMO (dual stream) requires at least 2, but works better with 3. - * This does not determine *which* chains to use, just how many. - */ -static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) -{ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && - (priv->bt_full_concurrent || - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { - /* - * only use chain 'A' in bt high traffic load or - * full concurrency mode - */ - return IWL_NUM_RX_CHAINS_SINGLE; - } - /* # of Rx chains to use when expecting MIMO. */ - if (is_single_rx_stream(priv)) - return IWL_NUM_RX_CHAINS_SINGLE; - else - return IWL_NUM_RX_CHAINS_MULTIPLE; -} - -/* - * When we are in power saving mode, unless device support spatial - * multiplexing power save, use the active count for rx chain count. - */ -static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) -{ - /* # Rx chains when idling, depending on SMPS mode */ - switch (priv->current_ht_config.smps) { - case IEEE80211_SMPS_STATIC: - case IEEE80211_SMPS_DYNAMIC: - return IWL_NUM_IDLE_CHAINS_SINGLE; - case IEEE80211_SMPS_AUTOMATIC: - case IEEE80211_SMPS_OFF: - return active_cnt; - default: - WARN(1, "invalid SMPS mode %d", - priv->current_ht_config.smps); - return active_cnt; - } -} - -/* up to 4 chains */ -static u8 iwl_count_chain_bitmap(u32 chain_bitmap) -{ - u8 res; - res = (chain_bitmap & BIT(0)) >> 0; - res += (chain_bitmap & BIT(1)) >> 1; - res += (chain_bitmap & BIT(2)) >> 2; - res += (chain_bitmap & BIT(3)) >> 3; - return res; -} - -/** - * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image - * - * Selects how many and which Rx receivers/antennas/chains to use. - * This should not be used for scan command ... it puts data in wrong place. - */ -void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - bool is_single = is_single_rx_stream(priv); - bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); - u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; - u32 active_chains; - u16 rx_chain; - - /* Tell uCode which antennas are actually connected. - * Before first association, we assume all antennas are connected. - * Just after first association, iwl_chain_noise_calibration() - * checks which antennas actually *are* connected. */ - if (priv->chain_noise_data.active_chains) - active_chains = priv->chain_noise_data.active_chains; - else - active_chains = priv->hw_params.valid_rx_ant; - - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && - (priv->bt_full_concurrent || - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { - /* - * only use chain 'A' in bt high traffic load or - * full concurrency mode - */ - active_chains = first_antenna(active_chains); - } - - rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; - - /* How many receivers should we use? */ - active_rx_cnt = iwl_get_active_rx_chain_count(priv); - idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); - - - /* correct rx chain count according hw settings - * and chain noise calibration - */ - valid_rx_cnt = iwl_count_chain_bitmap(active_chains); - if (valid_rx_cnt < active_rx_cnt) - active_rx_cnt = valid_rx_cnt; - - if (valid_rx_cnt < idle_rx_cnt) - idle_rx_cnt = valid_rx_cnt; - - rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; - rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; - - ctx->staging.rx_chain = cpu_to_le16(rx_chain); - - if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) - ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; - else - ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; - - IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", - ctx->staging.rx_chain, - active_rx_cnt, idle_rx_cnt); - - WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || - active_rx_cnt < idle_rx_cnt); -} - -u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) -{ - int i; - u8 ind = ant; - - if (priv->band == IEEE80211_BAND_2GHZ && - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) - return 0; - - for (i = 0; i < RATE_ANT_NUM - 1; i++) { - ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; - if (valid & BIT(ind)) - return ind; - } - return ant; -} - -#ifdef CONFIG_PM_SLEEP -static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) -{ - int i; - - for (i = 0; i < IWLAGN_P1K_SIZE; i++) - out[i] = cpu_to_le16(p1k[i]); -} - -struct wowlan_key_data { - struct iwl_rxon_context *ctx; - struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; - struct iwlagn_wowlan_tkip_params_cmd *tkip; - const u8 *bssid; - bool error, use_rsc_tsc, use_tkip; -}; - - -static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct wowlan_key_data *data = _data; - struct iwl_rxon_context *ctx = data->ctx; - struct aes_sc *aes_sc, *aes_tx_sc = NULL; - struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; - struct iwlagn_p1k_cache *rx_p1ks; - u8 *rx_mic_key; - struct ieee80211_key_seq seq; - u32 cur_rx_iv32 = 0; - u16 p1k[IWLAGN_P1K_SIZE]; - int ret, i; - - mutex_lock(&priv->mutex); - - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && - !sta && !ctx->key_mapping_keys) - ret = iwl_set_default_wep_key(priv, ctx, key); - else - ret = iwl_set_dynamic_key(priv, ctx, key, sta); - - if (ret) { - IWL_ERR(priv, "Error setting key during suspend!\n"); - data->error = true; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - if (sta) { - tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; - tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; - - rx_p1ks = data->tkip->rx_uni; - - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); - - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); - iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); - - memcpy(data->tkip->mic_keys.tx, - &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], - IWLAGN_MIC_KEY_SIZE); - - rx_mic_key = data->tkip->mic_keys.rx_unicast; - } else { - tkip_sc = - data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; - rx_p1ks = data->tkip->rx_multi; - rx_mic_key = data->tkip->mic_keys.rx_mcast; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 (as they need to to avoid replay attacks) - * for checking the IV in the frames. - */ - for (i = 0; i < IWLAGN_NUM_RSC; i++) { - ieee80211_get_key_rx_seq(key, i, &seq); - tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); - /* wrapping isn't allowed, AP must rekey */ - if (seq.tkip.iv32 > cur_rx_iv32) - cur_rx_iv32 = seq.tkip.iv32; - } - - ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); - iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); - ieee80211_get_tkip_rx_p1k(key, data->bssid, - cur_rx_iv32 + 1, p1k); - iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); - - memcpy(rx_mic_key, - &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], - IWLAGN_MIC_KEY_SIZE); - - data->use_tkip = true; - data->use_rsc_tsc = true; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (sta) { - u8 *pn = seq.ccmp.pn; - - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; - aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - - ieee80211_get_key_tx_seq(key, &seq); - aes_tx_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } else - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. - */ - for (i = 0; i < IWLAGN_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; - - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } - data->use_rsc_tsc = true; - break; - } - - mutex_unlock(&priv->mutex); -} - -int iwlagn_send_patterns(struct iwl_priv *priv, - struct cfg80211_wowlan *wowlan) -{ - struct iwlagn_wowlan_patterns_cmd *pattern_cmd; - struct iwl_host_cmd cmd = { - .id = REPLY_WOWLAN_PATTERNS, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .flags = CMD_SYNC, - }; - int i, err; - - if (!wowlan->n_patterns) - return 0; - - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); - - pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); - if (!pattern_cmd) - return -ENOMEM; - - pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); - - for (i = 0; i < wowlan->n_patterns; i++) { - int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - - memcpy(&pattern_cmd->patterns[i].mask, - wowlan->patterns[i].mask, mask_len); - memcpy(&pattern_cmd->patterns[i].pattern, - wowlan->patterns[i].pattern, - wowlan->patterns[i].pattern_len); - pattern_cmd->patterns[i].mask_size = mask_len; - pattern_cmd->patterns[i].pattern_size = - wowlan->patterns[i].pattern_len; - } - - cmd.data[0] = pattern_cmd; - err = iwl_dvm_send_cmd(priv, &cmd); - kfree(pattern_cmd); - return err; -} - -int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) -{ - struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; - struct iwl_rxon_cmd rxon; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; - struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; - struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; - struct wowlan_key_data key_data = { - .ctx = ctx, - .bssid = ctx->active.bssid_addr, - .use_rsc_tsc = false, - .tkip = &tkip_cmd, - .use_tkip = false, - }; - int ret, i; - u16 seq; - - key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); - if (!key_data.rsc_tsc) - return -ENOMEM; - - memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); - - /* - * We know the last used seqno, and the uCode expects to know that - * one, it will increment before TX. - */ - seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; - wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); - - /* - * For QoS counters, we store the one to use next, so subtract 0x10 - * since the uCode will add 0x10 before using the value. - */ - for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - seq = priv->tid_data[IWL_AP_ID][i].seq_number; - seq -= 0x10; - wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); - } - - if (wowlan->disconnect) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | - IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); - if (wowlan->magic_pkt) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); - if (wowlan->gtk_rekey_failure) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); - if (wowlan->eap_identity_req) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); - if (wowlan->four_way_handshake) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); - if (wowlan->n_patterns) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); - - if (wowlan->rfkill_release) - d3_cfg_cmd.wakeup_flags |= - cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); - - iwl_scan_cancel_timeout(priv, 200); - - memcpy(&rxon, &ctx->active, sizeof(rxon)); - - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - - priv->wowlan = true; - - ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); - if (ret) - goto out; - - /* now configure WoWLAN ucode */ - ret = iwl_alive_start(priv); - if (ret) - goto out; - - memcpy(&ctx->staging, &rxon, sizeof(rxon)); - ret = iwlagn_commit_rxon(priv, ctx); - if (ret) - goto out; - - ret = iwl_power_update_mode(priv, true); - if (ret) - goto out; - - if (!iwlwifi_mod_params.sw_crypto) { - /* mark all keys clear */ - priv->ucode_key_table = 0; - ctx->key_mapping_keys = 0; - - /* - * This needs to be unlocked due to lock ordering - * constraints. Since we're in the suspend path - * that isn't really a problem though. - */ - mutex_unlock(&priv->mutex); - ieee80211_iter_keys(priv->hw, ctx->vif, - iwlagn_wowlan_program_keys, - &key_data); - mutex_lock(&priv->mutex); - if (key_data.error) { - ret = -EIO; - goto out; - } - - if (key_data.use_rsc_tsc) { - struct iwl_host_cmd rsc_tsc_cmd = { - .id = REPLY_WOWLAN_TSC_RSC_PARAMS, - .flags = CMD_SYNC, - .data[0] = key_data.rsc_tsc, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .len[0] = sizeof(*key_data.rsc_tsc), - }; - - ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); - if (ret) - goto out; - } - - if (key_data.use_tkip) { - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_WOWLAN_TKIP_PARAMS, - CMD_SYNC, sizeof(tkip_cmd), - &tkip_cmd); - if (ret) - goto out; - } - - if (priv->have_rekey_data) { - memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); - memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); - kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); - memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); - kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); - kek_kck_cmd.replay_ctr = priv->replay_ctr; - - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_WOWLAN_KEK_KCK_MATERIAL, - CMD_SYNC, sizeof(kek_kck_cmd), - &kek_kck_cmd); - if (ret) - goto out; - } - } - - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC, - sizeof(d3_cfg_cmd), &d3_cfg_cmd); - if (ret) - goto out; - - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, - CMD_SYNC, sizeof(wakeup_filter_cmd), - &wakeup_filter_cmd); - if (ret) - goto out; - - ret = iwlagn_send_patterns(priv, wowlan); - out: - kfree(key_data.rsc_tsc); - return ret; -} -#endif - -int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) -{ - if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { - IWL_WARN(priv, "Not sending command - %s KILL\n", - iwl_is_rfkill(priv) ? "RF" : "CT"); - return -EIO; - } - - if (test_bit(STATUS_FW_ERROR, &priv->status)) { - IWL_ERR(priv, "Command %s failed: FW Error\n", - iwl_dvm_get_cmd_string(cmd->id)); - return -EIO; - } - - /* - * Synchronous commands from this op-mode must hold - * the mutex, this ensures we don't try to send two - * (or more) synchronous commands at a time. - */ - if (cmd->flags & CMD_SYNC) - lockdep_assert_held(&priv->mutex); - - if (priv->ucode_owner == IWL_OWNERSHIP_TM && - !(cmd->flags & CMD_ON_DEMAND)) { - IWL_DEBUG_HC(priv, "tm own the uCode, no regular hcmd send\n"); - return -EIO; - } - - return iwl_trans_send_cmd(priv->trans, cmd); -} - -int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, - u32 flags, u16 len, const void *data) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - .flags = flags, - }; - - return iwl_dvm_send_cmd(priv, &cmd); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c deleted file mode 100644 index 8cebd7c363fc..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ /dev/null @@ -1,3369 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "iwl-dev.h" -#include "iwl-agn.h" -#include "iwl-op-mode.h" -#include "iwl-modparams.h" - -#define RS_NAME "iwl-agn-rs" - -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_NUMBER_TRY 1 -#define IWL_HT_NUMBER_TRY 3 - -#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ -#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ - -/* max allowed rate miss before sync LQ cmd */ -#define IWL_MISSED_RATE_MAX 15 -/* max time to accum history 2 seconds */ -#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) - -static u8 rs_ht_to_legacy[] = { - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX -}; - -static const u8 ant_toggle_lookup[] = { - /*ANT_NONE -> */ ANT_NONE, - /*ANT_A -> */ ANT_B, - /*ANT_B -> */ ANT_C, - /*ANT_AB -> */ ANT_BC, - /*ANT_C -> */ ANT_A, - /*ANT_AC -> */ ANT_AB, - /*ANT_BC -> */ ANT_AC, - /*ANT_ABC -> */ ANT_ABC, -}; - -#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ - [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ - IWL_RATE_SISO_##s##M_PLCP, \ - IWL_RATE_MIMO2_##s##M_PLCP,\ - IWL_RATE_MIMO3_##s##M_PLCP,\ - IWL_RATE_##r##M_IEEE, \ - IWL_RATE_##ip##M_INDEX, \ - IWL_RATE_##in##M_INDEX, \ - IWL_RATE_##rp##M_INDEX, \ - IWL_RATE_##rn##M_INDEX, \ - IWL_RATE_##pp##M_INDEX, \ - IWL_RATE_##np##M_INDEX } - -/* - * Parameter order: - * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate - * - * If there isn't a valid next or previous rate then INV is used which - * maps to IWL_RATE_INVALID - * - */ -const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT] = { - IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ - IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ - IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ - IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ - IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ - IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ - IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ - IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ - IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ - IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ - IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ - /* FIXME:RS: ^^ should be INV (legacy) */ -}; - -static inline u8 rs_extract_rate(u32 rate_n_flags) -{ - return (u8)(rate_n_flags & RATE_MCS_RATE_MSK); -} - -static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) -{ - int idx = 0; - - /* HT rate format */ - if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = rs_extract_rate(rate_n_flags); - - if (idx >= IWL_RATE_MIMO3_6M_PLCP) - idx = idx - IWL_RATE_MIMO3_6M_PLCP; - else if (idx >= IWL_RATE_MIMO2_6M_PLCP) - idx = idx - IWL_RATE_MIMO2_6M_PLCP; - - idx += IWL_FIRST_OFDM_RATE; - /* skip 9M not supported in ht*/ - if (idx >= IWL_RATE_9M_INDEX) - idx += 1; - if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) - return idx; - - /* legacy rate format, search for match in table */ - } else { - for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) - if (iwl_rates[idx].plcp == - rs_extract_rate(rate_n_flags)) - return idx; - } - - return -1; -} - -static void rs_rate_scale_perform(struct iwl_priv *priv, - struct sk_buff *skb, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta); -static void rs_fill_link_cmd(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, u32 rate_n_flags); -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); - - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index); -#else -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index) -{} -#endif - -/** - * The following tables contain the expected throughput metrics for all rates - * - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * - * where invalid entries are zeros. - * - * CCK rates are only valid in legacy table and will only be used in G - * (2.4 GHz) band. - */ - -static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 -}; - -static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ - {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ - {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ - {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ -}; - -static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ - {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ - {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ -}; - -static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ - {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ - {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ - {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ -}; - -static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ - {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ - {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ -}; - -static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ - {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ - {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ - {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ -}; - -static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ - {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ - {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ - {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */ -}; - -/* mbps, mcs */ -static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { - { "1", "BPSK DSSS"}, - { "2", "QPSK DSSS"}, - {"5.5", "BPSK CCK"}, - { "11", "QPSK CCK"}, - { "6", "BPSK 1/2"}, - { "9", "BPSK 1/2"}, - { "12", "QPSK 1/2"}, - { "18", "QPSK 3/4"}, - { "24", "16QAM 1/2"}, - { "36", "16QAM 3/4"}, - { "48", "64QAM 2/3"}, - { "54", "64QAM 3/4"}, - { "60", "64QAM 5/6"}, -}; - -#define MCS_INDEX_PER_STREAM (8) - -static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) -{ - window->data = 0; - window->success_counter = 0; - window->success_ratio = IWL_INVALID_VALUE; - window->counter = 0; - window->average_tpt = IWL_INVALID_VALUE; - window->stamp = 0; -} - -static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) -{ - return (ant_type & valid_antenna) == ant_type; -} - -/* - * removes the old data from the statistics. All data that is older than - * TID_MAX_TIME_DIFF, will be deleted. - */ -static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) -{ - /* The oldest age we want to keep */ - u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; - - while (tl->queue_count && - (tl->time_stamp < oldest_time)) { - tl->total -= tl->packet_count[tl->head]; - tl->packet_count[tl->head] = 0; - tl->time_stamp += TID_QUEUE_CELL_SPACING; - tl->queue_count--; - tl->head++; - if (tl->head >= TID_QUEUE_MAX_SIZE) - tl->head = 0; - } -} - -/* - * increment traffic load value for tid and also remove - * any old values if passed the certain time period - */ -static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, - struct ieee80211_hdr *hdr) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl_traffic_load *tl = NULL; - u8 tid; - - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } else - return IWL_MAX_TID_COUNT; - - if (unlikely(tid >= IWL_MAX_TID_COUNT)) - return IWL_MAX_TID_COUNT; - - tl = &lq_data->load[tid]; - - curr_time -= curr_time % TID_ROUND_VALUE; - - /* Happens only for the first packet. Initialize the data */ - if (!(tl->queue_count)) { - tl->total = 1; - tl->time_stamp = curr_time; - tl->queue_count = 1; - tl->head = 0; - tl->packet_count[0] = 1; - return IWL_MAX_TID_COUNT; - } - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - index = (tl->head + index) % TID_QUEUE_MAX_SIZE; - tl->packet_count[index] = tl->packet_count[index] + 1; - tl->total = tl->total + 1; - - if ((index + 1) > tl->queue_count) - tl->queue_count = index + 1; - - return tid; -} - -#ifdef CONFIG_MAC80211_DEBUGFS -/** - * Program the device to use fixed rate for frame transmit - * This is for debugging/testing only - * once the device start use fixed rate, we need to reload the module - * to being back the normal operation. - */ -static void rs_program_fix_rate(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta) -{ - struct iwl_station_priv *sta_priv = - container_of(lq_sta, struct iwl_station_priv, lq_sta); - struct iwl_rxon_context *ctx = sta_priv->ctx; - - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - /* testmode has higher priority to overwirte the fixed rate */ - if (priv->tm_fixed_rate) - lq_sta->dbg_fixed_rate = priv->tm_fixed_rate; -#endif - - IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); - - if (lq_sta->dbg_fixed_rate) { - rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, - false); - } -} -#endif - -/* - get the traffic load value for tid -*/ -static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl_traffic_load *tl = NULL; - - if (tid >= IWL_MAX_TID_COUNT) - return 0; - - tl = &(lq_data->load[tid]); - - curr_time -= curr_time % TID_ROUND_VALUE; - - if (!(tl->queue_count)) - return 0; - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - return tl->total; -} - -static int rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, - struct iwl_lq_sta *lq_data, u8 tid, - struct ieee80211_sta *sta) -{ - int ret = -EAGAIN; - u32 load; - - /* - * Don't create TX aggregation sessions when in high - * BT traffic, as they would just be disrupted by BT. - */ - if (priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) { - IWL_ERR(priv, "BT traffic (%d), no aggregation allowed\n", - priv->bt_traffic_load); - return ret; - } - - load = rs_tl_get_load(lq_data, tid); - - if ((iwlwifi_mod_params.auto_agg) || (load > IWL_AGG_LOAD_THRESHOLD)) { - IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", - sta->addr, tid); - ret = ieee80211_start_tx_ba_session(sta, tid, 5000); - if (ret == -EAGAIN) { - /* - * driver and mac80211 is out of sync - * this might be cause by reloading firmware - * stop the tx ba session here - */ - IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", - tid); - ieee80211_stop_tx_ba_session(sta, tid); - } - } else { - IWL_DEBUG_HT(priv, "Aggregation not enabled for tid %d " - "because load = %u\n", tid, load); - } - return ret; -} - -static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, - struct iwl_lq_sta *lq_data, - struct ieee80211_sta *sta) -{ - if (tid < IWL_MAX_TID_COUNT) - rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); - else - IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", - tid, IWL_MAX_TID_COUNT); -} - -static inline int get_num_of_ant_from_rate(u32 rate_n_flags) -{ - return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK); -} - -/* - * Static function to get the expected throughput from an iwl_scale_tbl_info - * that wraps a NULL pointer check - */ -static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) -{ - if (tbl->expected_tpt) - return tbl->expected_tpt[rs_index]; - return 0; -} - -/** - * rs_collect_tx_data - Update the success/failure sliding window - * - * We keep a sliding window of the last 62 packets transmitted - * at this rate. window->data contains the bitmask of successful - * packets. - */ -static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, - int scale_index, int attempts, int successes) -{ - struct iwl_rate_scale_data *window = NULL; - static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); - s32 fail_count, tpt; - - if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) - return -EINVAL; - - /* Select window for current tx bit rate */ - window = &(tbl->win[scale_index]); - - /* Get expected throughput */ - tpt = get_expected_tpt(tbl, scale_index); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history window; anything older isn't really relevant any more. - * If we have filled up the sliding window, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - */ - while (attempts > 0) { - if (window->counter >= IWL_RATE_MAX_WINDOW) { - - /* remove earliest */ - window->counter = IWL_RATE_MAX_WINDOW - 1; - - if (window->data & mask) { - window->data &= ~mask; - window->success_counter--; - } - } - - /* Increment frames-attempted counter */ - window->counter++; - - /* Shift bitmap by one frame to throw away oldest history */ - window->data <<= 1; - - /* Mark the most recent #successes attempts as successful */ - if (successes > 0) { - window->success_counter++; - window->data |= 0x1; - successes--; - } - - attempts--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (window->counter > 0) - window->success_ratio = 128 * (100 * window->success_counter) - / window->counter; - else - window->success_ratio = IWL_INVALID_VALUE; - - fail_count = window->counter - window->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) - window->average_tpt = (window->success_ratio * tpt + 64) / 128; - else - window->average_tpt = IWL_INVALID_VALUE; - - /* Tag this window as having been updated */ - window->stamp = jiffies; - - return 0; -} - -/* - * Fill uCode API rate_n_flags field, based on "search" or "active" table. - */ -/* FIXME:RS:remove this function and put the flags statically in the table */ -static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, - struct iwl_scale_tbl_info *tbl, - int index, u8 use_green) -{ - u32 rate_n_flags = 0; - - if (is_legacy(tbl->lq_type)) { - rate_n_flags = iwl_rates[index].plcp; - if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - rate_n_flags |= RATE_MCS_CCK_MSK; - - } else if (is_Ht(tbl->lq_type)) { - if (index > IWL_LAST_OFDM_RATE) { - IWL_ERR(priv, "Invalid HT rate index %d\n", index); - index = IWL_LAST_OFDM_RATE; - } - rate_n_flags = RATE_MCS_HT_MSK; - - if (is_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_siso; - else if (is_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_mimo2; - else - rate_n_flags |= iwl_rates[index].plcp_mimo3; - } else { - IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); - } - - rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_Ht(tbl->lq_type)) { - if (tbl->is_ht40) { - if (tbl->is_dup) - rate_n_flags |= RATE_MCS_DUP_MSK; - else - rate_n_flags |= RATE_MCS_HT40_MSK; - } - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; - - if (use_green) { - rate_n_flags |= RATE_MCS_GF_MSK; - if (is_siso(tbl->lq_type) && tbl->is_SGI) { - rate_n_flags &= ~RATE_MCS_SGI_MSK; - IWL_ERR(priv, "GF was set with SGI:SISO\n"); - } - } - } - return rate_n_flags; -} - -/* - * Interpret uCode API's rate_n_flags format, - * fill "search" or "active" tx mode table. - */ -static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, - enum ieee80211_band band, - struct iwl_scale_tbl_info *tbl, - int *rate_idx) -{ - u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); - u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); - u8 mcs; - - memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); - *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); - - if (*rate_idx == IWL_RATE_INVALID) { - *rate_idx = -1; - return -EINVAL; - } - tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_ht40 = 0; - tbl->is_dup = 0; - tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); - tbl->lq_type = LQ_NONE; - tbl->max_search = IWL_MAX_SEARCH; - - /* legacy rate format */ - if (!(rate_n_flags & RATE_MCS_HT_MSK)) { - if (num_of_ant == 1) { - if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - } - /* HT rate format */ - } else { - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; - - if ((rate_n_flags & RATE_MCS_HT40_MSK) || - (rate_n_flags & RATE_MCS_DUP_MSK)) - tbl->is_ht40 = 1; - - if (rate_n_flags & RATE_MCS_DUP_MSK) - tbl->is_dup = 1; - - mcs = rs_extract_rate(rate_n_flags); - - /* SISO */ - if (mcs <= IWL_RATE_SISO_60M_PLCP) { - if (num_of_ant == 1) - tbl->lq_type = LQ_SISO; /*else NONE*/ - /* MIMO2 */ - } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { - if (num_of_ant == 2) - tbl->lq_type = LQ_MIMO2; - /* MIMO3 */ - } else { - if (num_of_ant == 3) { - tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; - tbl->lq_type = LQ_MIMO3; - } - } - } - return 0; -} - -/* switch to another antenna/antennas and return 1 */ -/* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, - struct iwl_scale_tbl_info *tbl) -{ - u8 new_ant_type; - - if (!tbl->ant_type || tbl->ant_type > ANT_ABC) - return 0; - - if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) - return 0; - - new_ant_type = ant_toggle_lookup[tbl->ant_type]; - - while ((new_ant_type != tbl->ant_type) && - !rs_is_valid_ant(valid_ant, new_ant_type)) - new_ant_type = ant_toggle_lookup[new_ant_type]; - - if (new_ant_type == tbl->ant_type) - return 0; - - tbl->ant_type = new_ant_type; - *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; - *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; - return 1; -} - -/** - * Green-field mode is valid if the station supports it and - * there are no non-GF stations present in the BSS. - */ -static bool rs_use_green(struct ieee80211_sta *sta) -{ - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && - !(ctx->ht.non_gf_sta_present); -} - -/** - * rs_get_supported_rates - get the available rates - * - * if management frame or broadcast frame only return - * basic available rates. - * - */ -static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, - struct ieee80211_hdr *hdr, - enum iwl_table_type rate_type) -{ - if (is_legacy(rate_type)) { - return lq_sta->active_legacy_rate; - } else { - if (is_siso(rate_type)) - return lq_sta->active_siso_rate; - else if (is_mimo2(rate_type)) - return lq_sta->active_mimo2_rate; - else - return lq_sta->active_mimo3_rate; - } -} - -static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, - int rate_type) -{ - u8 high = IWL_RATE_INVALID; - u8 low = IWL_RATE_INVALID; - - /* 802.11A or ht walks to the next literal adjacent rate in - * the rate table */ - if (is_a_band(rate_type) || !is_legacy(rate_type)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = index - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = index + 1; - for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = index; - while (low != IWL_RATE_INVALID) { - low = iwl_rates[low].prev_rs; - if (low == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); - } - - high = index; - while (high != IWL_RATE_INVALID) { - high = iwl_rates[high].next_rs; - if (high == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); - } - - return (high << 8) | low; -} - -static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - u8 scale_index, u8 ht_possible) -{ - s32 low; - u16 rate_mask; - u16 high_low; - u8 switch_to_legacy = 0; - u8 is_green = lq_sta->is_green; - struct iwl_priv *priv = lq_sta->drv; - - /* check if we need to switch from HT to legacy rates. - * assumption is that mandatory rates (1Mbps or 6Mbps) - * are always supported (spec demand) */ - if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { - switch_to_legacy = 1; - scale_index = rs_ht_to_legacy[scale_index]; - if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - - if (num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = - first_antenna(priv->hw_params.valid_tx_ant); - - tbl->is_ht40 = 0; - tbl->is_SGI = 0; - tbl->max_search = IWL_MAX_SEARCH; - } - - rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); - - /* Mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - /* supp_rates has no CCK bits in A mode */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = (u16)(rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_mask = (u16)(rate_mask & lq_sta->supp_rates); - } - - /* If we switched from HT to legacy, check current rate */ - if (switch_to_legacy && (rate_mask & (1 << scale_index))) { - low = scale_index; - goto out; - } - - high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, - tbl->lq_type); - low = high_low & 0xff; - - if (low == IWL_RATE_INVALID) - low = scale_index; - -out: - return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); -} - -/* - * Simple function to compare two rate scale table types - */ -static bool table_type_matches(struct iwl_scale_tbl_info *a, - struct iwl_scale_tbl_info *b) -{ - return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && - (a->is_SGI == b->is_SGI); -} - -static void rs_bt_update_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct iwl_lq_sta *lq_sta) -{ - struct iwl_scale_tbl_info *tbl; - bool full_concurrent = priv->bt_full_concurrent; - - if (priv->bt_ant_couple_ok) { - /* - * Is there a need to switch between - * full concurrency and 3-wire? - */ - if (priv->bt_ci_compliance && priv->bt_ant_couple_ok) - full_concurrent = true; - else - full_concurrent = false; - } - if ((priv->bt_traffic_load != priv->last_bt_traffic_load) || - (priv->bt_full_concurrent != full_concurrent)) { - priv->bt_full_concurrent = full_concurrent; - priv->last_bt_traffic_load = priv->bt_traffic_load; - - /* Update uCode's rate table. */ - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); - - queue_work(priv->workqueue, &priv->bt_full_concurrency); - } -} - -/* - * mac80211 sends us Tx status - */ -static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) -{ - int legacy_success; - int retries; - int rs_index, mac_index, i; - struct iwl_lq_sta *lq_sta = priv_sta; - struct iwl_link_quality_cmd *table; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_op_mode *op_mode = (struct iwl_op_mode *)priv_r; - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - enum mac80211_rate_control_flags mac_flags; - u32 tx_rate; - struct iwl_scale_tbl_info tbl_type; - struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (!lq_sta) { - IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); - return; - } else if (!lq_sta->drv) { - IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); - return; - } - - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK) - return; - - /* This packet was aggregated but doesn't carry status info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - /* - * Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - table = &lq_sta->lq; - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); - if (priv->band == IEEE80211_BAND_5GHZ) - rs_index -= IWL_FIRST_OFDM_RATE; - mac_flags = info->status.rates[0].flags; - mac_index = info->status.rates[0].idx; - /* For HT packets, map MCS to PLCP */ - if (mac_flags & IEEE80211_TX_RC_MCS) { - mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ - if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) - mac_index++; - /* - * mac80211 HT index is always zero-indexed; we need to move - * HT OFDM rates after CCK rates in 2.4 GHz band - */ - if (priv->band == IEEE80211_BAND_2GHZ) - mac_index += IWL_FIRST_OFDM_RATE; - } - /* Here we actually compare this rate to the latest LQ command */ - if ((mac_index < 0) || - (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || - (tbl_type.is_dup != !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || - (tbl_type.ant_type != info->status.antenna) || - (!!(tx_rate & RATE_MCS_HT_MSK) != !!(mac_flags & IEEE80211_TX_RC_MCS)) || - (!!(tx_rate & RATE_MCS_GF_MSK) != !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || - (rs_index != mac_index)) { - IWL_DEBUG_RATE(priv, "initial rate %d does not match %d (0x%x)\n", mac_index, rs_index, tx_rate); - /* - * Since rates mis-match, the last LQ command may have failed. - * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with - * ... driver. - */ - lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { - lq_sta->missed_rate_counter = 0; - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); - } - /* Regardless, ignore this status info for outdated rate */ - return; - } else - /* Rate did match, so reset the missed_rate_counter */ - lq_sta->missed_rate_counter = 0; - - /* Figure out if rate scale algorithm is in active or search table */ - if (table_type_matches(&tbl_type, - &(lq_sta->lq_info[lq_sta->active_tbl]))) { - curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else if (table_type_matches(&tbl_type, - &lq_sta->lq_info[1 - lq_sta->active_tbl])) { - curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - } else { - IWL_DEBUG_RATE(priv, "Neither active nor search matches tx rate\n"); - tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); - tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); - IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", - tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); - /* - * no matching table found, let's by-pass the data collection - * and continue to perform rate scale to find the rate table - */ - rs_stay_in_table(lq_sta, true); - goto done; - } - - /* - * Updating the frame history depends on whether packets were - * aggregated. - * - * For aggregation, all packets were transmitted at the same rate, the - * first index into rate scale table. - */ - if (info->flags & IEEE80211_TX_STAT_AMPDU) { - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, - &rs_index); - rs_collect_tx_data(curr_tbl, rs_index, - info->status.ampdu_len, - info->status.ampdu_ack_len); - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { - lq_sta->total_success += info->status.ampdu_ack_len; - lq_sta->total_failed += (info->status.ampdu_len - - info->status.ampdu_ack_len); - } - } else { - /* - * For legacy, update frame history with for each Tx retry. - */ - retries = info->status.rates[0].count - 1; - /* HW doesn't send more than 15 retries */ - retries = min(retries, 15); - - /* The last transmission may have been successful */ - legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); - /* Collect data for each rate used during failed TX attempts */ - for (i = 0; i <= retries; ++i) { - tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, - &tbl_type, &rs_index); - /* - * Only collect stats if retried rate is in the same RS - * table as active/search. - */ - if (table_type_matches(&tbl_type, curr_tbl)) - tmp_tbl = curr_tbl; - else if (table_type_matches(&tbl_type, other_tbl)) - tmp_tbl = other_tbl; - else - continue; - rs_collect_tx_data(tmp_tbl, rs_index, 1, - i < retries ? 0 : legacy_success); - } - - /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { - lq_sta->total_success += legacy_success; - lq_sta->total_failed += retries + (1 - legacy_success); - } - } - /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = tx_rate; -done: - /* See if there's a better rate or modulation mode to try. */ - if (sta && sta->supp_rates[sband->band]) - rs_rate_scale_perform(priv, skb, sta, lq_sta); - -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_TESTMODE) - if ((priv->tm_fixed_rate) && - (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) - rs_program_fix_rate(priv, lq_sta); -#endif - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) - rs_bt_update_lq(priv, ctx, lq_sta); -} - -/* - * Begin a period of staying with a selected modulation mode. - * Set "stay_in_tbl" flag to prevent any mode switches. - * Set frame tx success limits according to legacy vs. high-throughput, - * and reset overall (spanning all rates) tx success history statistics. - * These control how long we stay using same modulation mode before - * searching for a new mode. - */ -static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, - struct iwl_lq_sta *lq_sta) -{ - IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); - lq_sta->stay_in_tbl = 1; /* only place this gets set */ - if (is_legacy) { - lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; - } else { - lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; - } - lq_sta->table_count = 0; - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = jiffies; - lq_sta->action_counter = 0; -} - -/* - * Find correct throughput table for given mode of modulation - */ -static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - /* Used to choose among HT tables */ - s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - - /* Check for invalid LQ type */ - if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Legacy rates have only one table */ - if (is_legacy(tbl->lq_type)) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation - * status */ - if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_siso20MHz; - else if (is_siso(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_siso40MHz; - else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - else if (is_mimo2(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_mimo2_40MHz; - else if (is_mimo3(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) - ht_tbl_pointer = expected_tpt_mimo3_20MHz; - else /* if (is_mimo3(tbl->lq_type)) <-- must be true */ - ht_tbl_pointer = expected_tpt_mimo3_40MHz; - - if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ - tbl->expected_tpt = ht_tbl_pointer[0]; - else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ - tbl->expected_tpt = ht_tbl_pointer[1]; - else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ - tbl->expected_tpt = ht_tbl_pointer[2]; - else /* AGG+SGI */ - tbl->expected_tpt = ht_tbl_pointer[3]; -} - -/* - * Find starting rate for new "search" high-throughput mode of modulation. - * Goal is to find lowest expected rate (under perfect conditions) that is - * above the current measured throughput of "active" mode, to give new mode - * a fair chance to prove itself without too many challenges. - * - * This gets called when transitioning to more aggressive modulation - * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive - * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need - * to decrease to match "active" throughput. When moving from MIMO to SISO, - * bit rate will typically need to increase, but not if performance was bad. - */ -static s32 rs_get_best_rate(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, /* "search" */ - u16 rate_mask, s8 index) -{ - /* "active" values */ - struct iwl_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 active_sr = active_tbl->win[index].success_ratio; - s32 active_tpt = active_tbl->expected_tpt[index]; - - /* expected "search" throughput */ - s32 *tpt_tbl = tbl->expected_tpt; - - s32 new_rate, high, low, start_hi; - u16 high_low; - s8 rate = index; - - new_rate = high = low = start_hi = IWL_RATE_INVALID; - - for (; ;) { - high_low = rs_get_adjacent_rate(priv, rate, rate_mask, - tbl->lq_type); - - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* - * Lower the "search" bit rate, to give new "search" mode - * approximately the same throughput as "active" if: - * - * 1) "Active" mode has been working modestly well (but not - * great), and expected "search" throughput (under perfect - * conditions) at candidate rate is above the actual - * measured "active" throughput (but less than expected - * "active" throughput under perfect conditions). - * OR - * 2) "Active" mode has been working perfectly or very well - * and expected "search" throughput (under perfect - * conditions) at candidate rate is above expected - * "active" throughput (under perfect conditions). - */ - if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && - ((active_sr > IWL_RATE_DECREASE_TH) && - (active_sr <= IWL_RATE_HIGH_TH) && - (tpt_tbl[rate] <= active_tpt))) || - ((active_sr >= IWL_RATE_SCALE_SWITCH) && - (tpt_tbl[rate] > active_tpt))) { - - /* (2nd or later pass) - * If we've already tried to raise the rate, and are - * now trying to lower it, use the higher rate. */ - if (start_hi != IWL_RATE_INVALID) { - new_rate = start_hi; - break; - } - - new_rate = rate; - - /* Loop again with lower rate */ - if (low != IWL_RATE_INVALID) - rate = low; - - /* Lower rate not available, use the original */ - else - break; - - /* Else try to raise the "search" rate to match "active" */ - } else { - /* (2nd or later pass) - * If we've already tried to lower the rate, and are - * now trying to raise it, use the lower rate. */ - if (new_rate != IWL_RATE_INVALID) - break; - - /* Loop again with higher rate */ - else if (high != IWL_RATE_INVALID) { - start_hi = high; - rate = high; - - /* Higher rate not available, use the original */ - } else { - new_rate = rate; - break; - } - } - } - - return new_rate; -} - -/* - * Set up search table for MIMO2 - */ -static int rs_switch_to_mimo2(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) - == WLAN_HT_CAP_SM_PS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (priv->hw_params.tx_chains_num < 2) - return -1; - - IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); - - tbl->lq_type = LQ_MIMO2; - tbl->is_dup = lq_sta->is_dup; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_mimo2_rate; - - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); - - IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Set up search table for MIMO3 - */ -static int rs_switch_to_mimo3(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) - == WLAN_HT_CAP_SM_PS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (priv->hw_params.tx_chains_num < 3) - return -1; - - IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO3\n"); - - tbl->lq_type = LQ_MIMO3; - tbl->is_dup = lq_sta->is_dup; - tbl->action = 0; - tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; - rate_mask = lq_sta->active_mimo3_rate; - - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(priv, "LQ: MIMO3 best rate %d mask %X\n", - rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(priv, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); - - IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Set up search table for SISO - */ -static int rs_switch_to_siso(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - u8 is_green = lq_sta->is_green; - s32 rate; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) - return -1; - - IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); - - tbl->is_dup = lq_sta->is_dup; - tbl->lq_type = LQ_SISO; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_siso_rate; - - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - if (is_green) - tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ - - rs_set_expected_tpt_table(lq_sta, tbl); - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(priv, "can not switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, rate, is_green); - IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Try to switch to new modulation mode from legacy - */ -static int rs_move_legacy_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, - int index) -{ - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - int ret = 0; - u8 update_search_tbl_counter = 0; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_LEGACY_SWITCH_SISO; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); - if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && - tbl->action != IWL_LEGACY_SWITCH_SISO) - tbl->action = IWL_LEGACY_SWITCH_SISO; - break; - default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); - break; - } - - if (!iwl_ht_enabled(priv)) - /* stay in Legacy */ - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && - tbl->action > IWL_LEGACY_SWITCH_SISO) - tbl->action = IWL_LEGACY_SWITCH_SISO; - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent) { - if (!iwl_ht_enabled(priv)) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_LEGACY_SWITCH_SISO; - valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); - } - - start_action = tbl->action; - for (; ;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_LEGACY_SWITCH_ANTENNA1: - case IWL_LEGACY_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); - - if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - /* Don't change antenna if success has been great */ - if (window->success_ratio >= IWL_RS_GOOD_RATIO && - !priv->bt_full_concurrent && - priv->bt_traffic_load == - IWL_BT_COEX_TRAFFIC_LOAD_NONE) - break; - - /* Set up search table to try other antenna */ - memcpy(search_tbl, tbl, sz); - - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - update_search_tbl_counter = 1; - rs_set_expected_tpt_table(lq_sta, search_tbl); - goto out; - } - break; - case IWL_LEGACY_SWITCH_SISO: - IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); - - /* Set up search table to try SISO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - - break; - case IWL_LEGACY_SWITCH_MIMO2_AB: - case IWL_LEGACY_SWITCH_MIMO2_AC: - case IWL_LEGACY_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); - - /* Set up search table to try MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; - - case IWL_LEGACY_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO3\n"); - - /* Set up search table to try MIMO3 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - break; - } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - - } - search_tbl->lq_type = LQ_NONE; - return 0; - -out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - return 0; - -} - -/* - * Try to switch to new modulation mode from SISO - */ -static int rs_move_siso_to_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int index) -{ - u8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - u8 update_search_tbl_counter = 0; - int ret; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_MIMO2_AB; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); - if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - break; - default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); - break; - } - - if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && - tbl->action > IWL_SISO_SWITCH_ANTENNA2) { - /* stay in SISO */ - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - } - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent) { - valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); - if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - } - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_SISO_SWITCH_ANTENNA1: - case IWL_SISO_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); - if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO && - !priv->bt_full_concurrent && - priv->bt_traffic_load == - IWL_BT_COEX_TRAFFIC_LOAD_NONE) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IWL_SISO_SWITCH_MIMO2_AB: - case IWL_SISO_SWITCH_MIMO2_AC: - case IWL_SISO_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - break; - case IWL_SISO_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); - - memcpy(search_tbl, tbl, sz); - if (is_green) { - if (!tbl->is_SGI) - break; - else - IWL_ERR(priv, - "SGI was set in GF+SISO\n"); - } - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(priv, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - case IWL_SISO_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO3\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - break; - } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; -} - -/* - * Try to switch to new modulation mode from MIMO2 - */ -static int rs_move_mimo2_to_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int index) -{ - s8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - u8 update_search_tbl_counter = 0; - int ret; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO2_SWITCH_SISO_B || - tbl->action == IWL_MIMO2_SWITCH_SISO_C) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); - break; - } - - if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && - (tbl->action < IWL_MIMO2_SWITCH_SISO_A || - tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { - /* switch in SISO */ - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - } - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent && - (tbl->action < IWL_MIMO2_SWITCH_SISO_A || - tbl->action > IWL_MIMO2_SWITCH_SISO_C)) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO2_SWITCH_ANTENNA1: - case IWL_MIMO2_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); - - if (tx_chains_num <= 2) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; - case IWL_MIMO2_SWITCH_SISO_A: - case IWL_MIMO2_SWITCH_SISO_B: - case IWL_MIMO2_SWITCH_SISO_C: - IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) - search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO2_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO2 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(priv, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - - case IWL_MIMO2_SWITCH_MIMO3_ABC: - IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to MIMO3\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_ABC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo3(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - } - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; - -} - -/* - * Try to switch to new modulation mode from MIMO3 - */ -static int rs_move_mimo3_to_other(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct ieee80211_sta *sta, int index) -{ - s8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - int ret; - u8 update_search_tbl_counter = 0; - - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO3_SWITCH_SISO_A) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO3_SWITCH_SISO_B || - tbl->action == IWL_MIMO3_SWITCH_SISO_C) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - break; - default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); - break; - } - - if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && - (tbl->action < IWL_MIMO3_SWITCH_SISO_A || - tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { - /* switch in SISO */ - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - } - - /* configure as 1x1 if bt full concurrency */ - if (priv->bt_full_concurrent && - (tbl->action < IWL_MIMO3_SWITCH_SISO_A || - tbl->action > IWL_MIMO3_SWITCH_SISO_C)) - tbl->action = IWL_MIMO3_SWITCH_SISO_A; - - start_action = tbl->action; - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO3_SWITCH_ANTENNA1: - case IWL_MIMO3_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle Antennas\n"); - - if (tx_chains_num <= 3) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) - goto out; - break; - case IWL_MIMO3_SWITCH_SISO_A: - case IWL_MIMO3_SWITCH_SISO_B: - case IWL_MIMO3_SWITCH_SISO_C: - IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO3_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B) - search_tbl->ant_type = ANT_B; - else - search_tbl->ant_type = ANT_C; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO3_SWITCH_MIMO2_AB: - case IWL_MIMO3_SWITCH_MIMO2_AC: - case IWL_MIMO3_SWITCH_MIMO2_BC: - IWL_DEBUG_RATE(priv, "LQ: MIMO3 switch to MIMO2\n"); - - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB) - search_tbl->ant_type = ANT_AB; - else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC) - search_tbl->ant_type = ANT_AC; - else - search_tbl->ant_type = ANT_BC; - - if (!rs_is_valid_ant(valid_tx_ant, search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) - goto out; - - break; - - case IWL_MIMO3_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); - - /* Set up new search table for MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(priv, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - } - tbl->action++; - if (tbl->action > IWL_MIMO3_SWITCH_GI) - tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO3_SWITCH_GI) - tbl->action = IWL_MIMO3_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - - return 0; - -} - -/* - * Check whether we should continue using same modulation mode, or - * begin search for a new mode, based on: - * 1) # tx successes or failures while using this mode - * 2) # times calling this function - * 3) elapsed time in this mode (not used, for now) - */ -static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) -{ - struct iwl_scale_tbl_info *tbl; - int i; - int active_tbl; - int flush_interval_passed = 0; - struct iwl_priv *priv; - - priv = lq_sta->drv; - active_tbl = lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->stay_in_tbl) { - - /* Elapsed time using current modulation mode */ - if (lq_sta->flush_timer) - flush_interval_passed = - time_after(jiffies, - (unsigned long)(lq_sta->flush_timer + - IWL_RATE_SCALE_FLUSH_INTVL)); - - /* - * Check if we should allow search for new modulation mode. - * If many frames have failed or succeeded, or we've used - * this same modulation for a long time, allow search, and - * reset history stats that keep track of whether we should - * allow a new search. Also (below) reset all bitmaps and - * stats in active history. - */ - if (force_search || - (lq_sta->total_failed > lq_sta->max_failure_limit) || - (lq_sta->total_success > lq_sta->max_success_limit) || - ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) - && (flush_interval_passed))) { - IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n", - lq_sta->total_failed, - lq_sta->total_success, - flush_interval_passed); - - /* Allow search for new mode */ - lq_sta->stay_in_tbl = 0; /* only place reset */ - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = 0; - - /* - * Else if we've used this modulation mode enough repetitions - * (regardless of elapsed time or success/failure), reset - * history bitmaps and rate-specific stats for all rates in - * active table. - */ - } else { - lq_sta->table_count++; - if (lq_sta->table_count >= - lq_sta->table_count_limit) { - lq_sta->table_count = 0; - - IWL_DEBUG_RATE(priv, "LQ: stay in table clear win\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window( - &(tbl->win[i])); - } - } - - /* If transitioning to allow "search", reset all history - * bitmaps and stats in active table (this will become the new - * "search" table). */ - if (!lq_sta->stay_in_tbl) { - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); - } - } -} - -/* - * setup rate table in uCode - */ -static void rs_update_rate_tbl(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int index, u8 is_green) -{ - u32 rate; - - /* Update uCode's rate table. */ - rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); - rs_fill_link_cmd(priv, lq_sta, rate); - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); -} - -/* - * Do rate scaling and search for new modulation mode. - */ -static void rs_rate_scale_perform(struct iwl_priv *priv, - struct sk_buff *skb, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int low = IWL_RATE_INVALID; - int high = IWL_RATE_INVALID; - int index; - int i; - struct iwl_rate_scale_data *window = NULL; - int current_tpt = IWL_INVALID_VALUE; - int low_tpt = IWL_INVALID_VALUE; - int high_tpt = IWL_INVALID_VALUE; - u32 fail_count; - s8 scale_action = 0; - u16 rate_mask; - u8 update_lq = 0; - struct iwl_scale_tbl_info *tbl, *tbl1; - u16 rate_scale_index_msk = 0; - u8 is_green = 0; - u8 active_tbl = 0; - u8 done_search = 0; - u16 high_low; - s32 sr; - u8 tid = IWL_MAX_TID_COUNT; - struct iwl_tid_data *tid_data; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); - - /* Send management frames and NO_ACK data using lowest rate. */ - /* TODO: this could probably be improved.. */ - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK) - return; - - lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; - - tid = rs_tl_add_packet(lq_sta, hdr); - if ((tid != IWL_MAX_TID_COUNT) && - (lq_sta->tx_agg_tid_en & (1 << tid))) { - tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; - if (tid_data->agg.state == IWL_AGG_OFF) - lq_sta->is_agg = 0; - else - lq_sta->is_agg = 1; - } else - lq_sta->is_agg = 0; - - /* - * Select rate-scale / modulation-mode table to work with in - * the rest of this function: "search" if searching for better - * modulation mode, or "active" if doing rate scaling within a mode. - */ - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - if (is_legacy(tbl->lq_type)) - lq_sta->is_green = 0; - else - lq_sta->is_green = rs_use_green(sta); - is_green = lq_sta->is_green; - - /* current tx rate */ - index = lq_sta->last_txrate_idx; - - IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, - tbl->lq_type); - - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); - - IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); - - /* mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - /* supp_rates has no CCK bits in A mode */ - rate_scale_index_msk = (u16) (rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_scale_index_msk = (u16) (rate_mask & - lq_sta->supp_rates); - - } else - rate_scale_index_msk = rate_mask; - - if (!rate_scale_index_msk) - rate_scale_index_msk = rate_mask; - - if (!((1 << index) & rate_scale_index_msk)) { - IWL_ERR(priv, "Current Rate is not valid\n"); - if (lq_sta->search_better_tbl) { - /* revert to active table if search table is not valid*/ - tbl->lq_type = LQ_NONE; - lq_sta->search_better_tbl = 0; - tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - /* get "active" rate info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - rs_update_rate_tbl(priv, ctx, lq_sta, tbl, - index, is_green); - } - return; - } - - /* Get expected throughput table and history window for current rate */ - if (!tbl->expected_tpt) { - IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); - return; - } - - /* force user max rate if set by user */ - if ((lq_sta->max_rate_idx != -1) && - (lq_sta->max_rate_idx < index)) { - index = lq_sta->max_rate_idx; - update_lq = 1; - window = &(tbl->win[index]); - goto lq_update; - } - - window = &(tbl->win[index]); - - /* - * If there is not enough history to calculate actual average - * throughput, keep analyzing results of more tx frames, without - * changing rate or mode (bypass most of the rest of this function). - * Set up new rate table in uCode only if old rate is not supported - * in current association (use new rate found above). - */ - fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { - IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " - "for index %d\n", - window->success_counter, window->counter, index); - - /* Can't calculate this yet; not enough history */ - window->average_tpt = IWL_INVALID_VALUE; - - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta, false); - - goto out; - } - /* Else we have enough samples; calculate estimate of - * actual average throughput */ - if (window->average_tpt != ((window->success_ratio * - tbl->expected_tpt[index] + 64) / 128)) { - IWL_ERR(priv, "expected_tpt should have been calculated by now\n"); - window->average_tpt = ((window->success_ratio * - tbl->expected_tpt[index] + 64) / 128); - } - - /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl && - (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { - /* If good success, continue using the "search" mode; - * no need to send new link quality command, since we're - * continuing to use the setup that we've been trying. */ - if (window->average_tpt > lq_sta->last_tpt) { - - IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " - "suc=%d cur-tpt=%d old-tpt=%d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - if (!is_legacy(tbl->lq_type)) - lq_sta->enable_counter = 1; - - /* Swap tables; "search" becomes "active" */ - lq_sta->active_tbl = active_tbl; - current_tpt = window->average_tpt; - - /* Else poor success; go back to mode in "active" table */ - } else { - - IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " - "suc=%d cur-tpt=%d old-tpt=%d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Nullify "search" table */ - tbl->lq_type = LQ_NONE; - - /* Revert to "active" table */ - active_tbl = lq_sta->active_tbl; - tbl = &(lq_sta->lq_info[active_tbl]); - - /* Revert to "active" rate and throughput info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - current_tpt = lq_sta->last_tpt; - - /* Need to set up a new rate table in uCode */ - update_lq = 1; - } - - /* Either way, we've made a decision; modulation mode - * search is done, allow rate adjustment next time. */ - lq_sta->search_better_tbl = 0; - done_search = 1; /* Don't switch modes below! */ - goto lq_update; - } - - /* (Else) not in search of better modulation mode, try for better - * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, - tbl->lq_type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* If user set max rate, dont allow higher than user constrain */ - if ((lq_sta->max_rate_idx != -1) && - (lq_sta->max_rate_idx < high)) - high = IWL_RATE_INVALID; - - sr = window->success_ratio; - - /* Collect measured throughputs for current and adjacent rates */ - current_tpt = window->average_tpt; - if (low != IWL_RATE_INVALID) - low_tpt = tbl->win[low].average_tpt; - if (high != IWL_RATE_INVALID) - high_tpt = tbl->win[high].average_tpt; - - scale_action = 0; - - /* Too many failures, decrease rate */ - if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { - IWL_DEBUG_RATE(priv, "decrease rate because of low success_ratio\n"); - scale_action = -1; - - /* No throughput measured yet for adjacent rates; try increase. */ - } else if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE)) { - - if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) - scale_action = 1; - else if (low != IWL_RATE_INVALID) - scale_action = 0; - } - - /* Both adjacent throughputs are measured, but neither one has better - * throughput; we're using the best rate, don't change it! */ - else if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) - scale_action = 0; - - /* At least one adjacent rate's throughput is measured, - * and may have better performance. */ - else { - /* Higher adjacent rate's throughput is measured */ - if (high_tpt != IWL_INVALID_VALUE) { - /* Higher rate has better throughput */ - if (high_tpt > current_tpt && - sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } else { - scale_action = 0; - } - - /* Lower adjacent rate's throughput is measured */ - } else if (low_tpt != IWL_INVALID_VALUE) { - /* Lower rate has better throughput */ - if (low_tpt > current_tpt) { - IWL_DEBUG_RATE(priv, - "decrease rate because of low tpt\n"); - scale_action = -1; - } else if (sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if ((scale_action == -1) && (low != IWL_RATE_INVALID) && - ((sr > IWL_RATE_HIGH_TH) || - (current_tpt > (100 * tbl->expected_tpt[low])))) - scale_action = 0; - if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) - scale_action = -1; - if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) - scale_action = -1; - - if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { - if (lq_sta->last_bt_traffic > priv->bt_traffic_load) { - /* - * don't set scale_action, don't want to scale up if - * the rate scale doesn't otherwise think that is a - * good idea. - */ - } else if (lq_sta->last_bt_traffic <= priv->bt_traffic_load) { - scale_action = -1; - } - } - lq_sta->last_bt_traffic = priv->bt_traffic_load; - - if ((priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && - (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) { - /* search for a new modulation */ - rs_stay_in_table(lq_sta, true); - goto lq_update; - } - - switch (scale_action) { - case -1: - /* Decrease starting rate, update uCode's rate table */ - if (low != IWL_RATE_INVALID) { - update_lq = 1; - index = low; - } - - break; - case 1: - /* Increase starting rate, update uCode's rate table */ - if (high != IWL_RATE_INVALID) { - update_lq = 1; - index = high; - } - - break; - case 0: - /* No change */ - default: - break; - } - - IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " - "high %d type %d\n", - index, scale_action, low, high, tbl->lq_type); - -lq_update: - /* Replace uCode's rate table for the destination station. */ - if (update_lq) - rs_update_rate_tbl(priv, ctx, lq_sta, tbl, index, is_green); - - if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta, false); - } - /* - * Search for new modulation mode if we're: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) Allowing a new search - */ - if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { - /* Save current throughput to compare with "search" throughput*/ - lq_sta->last_tpt = current_tpt; - - /* Select a new "search" modulation mode to try. - * If one is found, set up the new "search" table. */ - if (is_legacy(tbl->lq_type)) - rs_move_legacy_other(priv, lq_sta, conf, sta, index); - else if (is_siso(tbl->lq_type)) - rs_move_siso_to_other(priv, lq_sta, conf, sta, index); - else if (is_mimo2(tbl->lq_type)) - rs_move_mimo2_to_other(priv, lq_sta, conf, sta, index); - else - rs_move_mimo3_to_other(priv, lq_sta, conf, sta, index); - - /* If new "search" mode was selected, set up in uCode table */ - if (lq_sta->search_better_tbl) { - /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); - - /* Use new "search" start rate */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - - IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", - tbl->current_rate, index); - rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); - } else - done_search = 1; - } - - if (done_search && !lq_sta->stay_in_tbl) { - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ - tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && - lq_sta->action_counter > tbl1->max_search) { - IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); - rs_set_stay_in_table(priv, 1, lq_sta); - } - - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if (lq_sta->enable_counter && - (lq_sta->action_counter >= tbl1->max_search) && - iwl_ht_enabled(priv)) { - if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - (tid != IWL_MAX_TID_COUNT)) { - u8 sta_id = lq_sta->lq.sta_id; - tid_data = &priv->tid_data[sta_id][tid]; - if (tid_data->agg.state == IWL_AGG_OFF) { - IWL_DEBUG_RATE(priv, - "try to aggregate tid %d\n", - tid); - rs_tl_turn_on_agg(priv, tid, - lq_sta, sta); - } - } - rs_set_stay_in_table(priv, 0, lq_sta); - } - } - -out: - tbl->current_rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); - lq_sta->last_txrate_idx = index; -} - -/** - * rs_initialize_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void rs_initialize_lq(struct iwl_priv *priv, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta) -{ - struct iwl_scale_tbl_info *tbl; - int rate_idx; - int i; - u32 rate; - u8 use_green = rs_use_green(sta); - u8 active_tbl = 0; - u8 valid_tx_ant; - struct iwl_station_priv *sta_priv; - struct iwl_rxon_context *ctx; - - if (!sta || !lq_sta) - return; - - sta_priv = (void *)sta->drv_priv; - ctx = sta_priv->ctx; - - i = lq_sta->last_txrate_idx; - - valid_tx_ant = priv->hw_params.valid_tx_ant; - - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - if ((i < 0) || (i >= IWL_RATE_COUNT)) - i = 0; - - rate = iwl_rates[i].plcp; - tbl->ant_type = first_antenna(valid_tx_ant); - rate |= tbl->ant_type << RATE_MCS_ANT_POS; - - if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) - rate |= RATE_MCS_CCK_MSK; - - rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); - if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) - rs_toggle_antenna(valid_tx_ant, &rate, tbl); - - rate = rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); - tbl->current_rate = rate; - rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, lq_sta, rate); - priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true); -} - -static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_rate_control *txrc) -{ - - struct sk_buff *skb = txrc->skb; - struct ieee80211_supported_band *sband = txrc->sband; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)priv_r; - struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_lq_sta *lq_sta = priv_sta; - int rate_idx; - - IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); - - /* Get max rate if user set max rate */ - if (lq_sta) { - lq_sta->max_rate_idx = txrc->max_rate_idx; - if ((sband->band == IEEE80211_BAND_5GHZ) && - (lq_sta->max_rate_idx != -1)) - lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; - if ((lq_sta->max_rate_idx < 0) || - (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) - lq_sta->max_rate_idx = -1; - } - - /* Treat uninitialized rate scaling data same as non-existing. */ - if (lq_sta && !lq_sta->drv) { - IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); - priv_sta = NULL; - } - - /* Send management frames and NO_ACK data using lowest rate. */ - if (rate_control_send_low(sta, priv_sta, txrc)) - return; - - rate_idx = lq_sta->last_txrate_idx; - - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { - rate_idx -= IWL_FIRST_OFDM_RATE; - /* 6M and 9M shared same MCS index */ - rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; - if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO3_6M_PLCP) - rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM); - else if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO2_6M_PLCP) - rate_idx = rate_idx + MCS_INDEX_PER_STREAM; - info->control.rates[0].flags = IEEE80211_TX_RC_MCS; - if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; - if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; - if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; - } else { - /* Check for invalid rates */ - if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || - ((sband->band == IEEE80211_BAND_5GHZ) && - (rate_idx < IWL_FIRST_OFDM_RATE))) - rate_idx = rate_lowest_index(sband, sta); - /* On valid 5 GHz rate, adjust index */ - else if (sband->band == IEEE80211_BAND_5GHZ) - rate_idx -= IWL_FIRST_OFDM_RATE; - info->control.rates[0].flags = 0; - } - info->control.rates[0].idx = rate_idx; - -} - -static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, - gfp_t gfp) -{ - struct iwl_station_priv *sta_priv = (struct iwl_station_priv *) sta->drv_priv; - struct iwl_op_mode *op_mode __maybe_unused = - (struct iwl_op_mode *)priv_rate; - struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_DEBUG_RATE(priv, "create station rate scale window\n"); - - return &sta_priv->lq_sta; -} - -/* - * Called after adding a new station to initialize rate scaling - */ -void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) -{ - int i, j; - struct ieee80211_hw *hw = priv->hw; - struct ieee80211_conf *conf = &priv->hw->conf; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct iwl_station_priv *sta_priv; - struct iwl_lq_sta *lq_sta; - struct ieee80211_supported_band *sband; - unsigned long supp; /* must be unsigned long for for_each_set_bit */ - - sta_priv = (struct iwl_station_priv *) sta->drv_priv; - lq_sta = &sta_priv->lq_sta; - sband = hw->wiphy->bands[conf->channel->band]; - - - lq_sta->lq.sta_id = sta_id; - - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); - - lq_sta->flush_timer = 0; - lq_sta->supp_rates = sta->supp_rates[sband->band]; - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); - - IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", - sta_id); - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - lq_sta->is_dup = 0; - lq_sta->max_rate_idx = -1; - lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; - lq_sta->is_green = rs_use_green(sta); - lq_sta->band = sband->band; - /* - * active legacy rates as per supported rates bitmap - */ - supp = sta->supp_rates[sband->band]; - lq_sta->active_legacy_rate = 0; - for_each_set_bit(i, &supp, BITS_PER_LONG) - lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); - - /* - * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), - * supp_rates[] does not; shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; - - /* Same here */ - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - - lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; - lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; - lq_sta->active_mimo3_rate &= ~((u16)0x2); - lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; - - IWL_DEBUG_RATE(priv, "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", - lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate, - lq_sta->active_mimo3_rate); - - /* These values will be overridden later */ - lq_sta->lq.general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); - lq_sta->lq.general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant & - ~first_antenna(priv->hw_params.valid_tx_ant); - if (!lq_sta->lq.general_params.dual_stream_ant_msk) { - lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { - lq_sta->lq.general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant; - } - - /* as default allow aggregation for all tids */ - lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; - lq_sta->drv = priv; - - /* Set last_txrate_idx to lowest rate */ - lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); - if (sband->band == IEEE80211_BAND_5GHZ) - lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; - lq_sta->is_agg = 0; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - priv->tm_fixed_rate = 0; -#endif -#ifdef CONFIG_MAC80211_DEBUGFS - lq_sta->dbg_fixed_rate = 0; -#endif - - rs_initialize_lq(priv, sta, lq_sta); -} - -static void rs_fill_link_cmd(struct iwl_priv *priv, - struct iwl_lq_sta *lq_sta, u32 new_rate) -{ - struct iwl_scale_tbl_info tbl_type; - int index = 0; - int rate_idx; - int repeat_rate = 0; - u8 ant_toggle_cnt = 0; - u8 use_ht_possible = 1; - u8 valid_tx_ant = 0; - struct iwl_station_priv *sta_priv = - container_of(lq_sta, struct iwl_station_priv, lq_sta); - struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; - - /* Override starting rate (index 0) if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Interpret new_rate (rate_n_flags) */ - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, - &tbl_type, &rate_idx); - - if (priv && priv->bt_full_concurrent) { - /* 1x1 only */ - tbl_type.ant_type = - first_antenna(priv->hw_params.valid_tx_ant); - } - - /* How many times should we repeat the initial rate? */ - if (is_legacy(tbl_type.lq_type)) { - ant_toggle_cnt = 1; - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = min(IWL_HT_NUMBER_TRY, - LINK_QUAL_AGG_DISABLE_START_DEF - 1); - } - - lq_cmd->general_params.mimo_delimiter = - is_mimo(tbl_type.lq_type) ? 1 : 0; - - /* Fill 1st table entry (index 0) */ - lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); - - if (num_of_ant(tbl_type.ant_type) == 1) { - lq_cmd->general_params.single_stream_ant_msk = - tbl_type.ant_type; - } else if (num_of_ant(tbl_type.ant_type) == 2) { - lq_cmd->general_params.dual_stream_ant_msk = - tbl_type.ant_type; - } /* otherwise we don't modify the existing value */ - - index++; - repeat_rate--; - if (priv) { - if (priv->bt_full_concurrent) - valid_tx_ant = ANT_A; - else - valid_tx_ant = priv->hw_params.valid_tx_ant; - } - - /* Fill rest of rate table */ - while (index < LINK_QUAL_MAX_RETRY_NUM) { - /* Repeat initial/next rate. - * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. - * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ - while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (priv && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - } - - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Fill next table entry */ - lq_cmd->rs_table[index].rate_n_flags = - cpu_to_le32(new_rate); - repeat_rate--; - index++; - } - - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, - &rate_idx); - - if (priv && priv->bt_full_concurrent) { - /* 1x1 only */ - tbl_type.ant_type = - first_antenna(priv->hw_params.valid_tx_ant); - } - - /* Indicate to uCode which entries might be MIMO. - * If initial rate was MIMO, this will finally end up - * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(tbl_type.lq_type)) - lq_cmd->general_params.mimo_delimiter = index; - - /* Get next rate */ - new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, - use_ht_possible); - - /* How many times should we repeat the next rate? */ - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (priv && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = IWL_HT_NUMBER_TRY; - } - - /* Don't allow HT rates after next pass. - * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ - use_ht_possible = 0; - - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Fill next table entry */ - lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); - - index++; - repeat_rate--; - } - - lq_cmd->agg_params.agg_frame_cnt_limit = - sta_priv->max_agg_bufsize ?: LINK_QUAL_AGG_FRAME_LIMIT_DEF; - lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - - lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - /* - * overwrite if needed, pass aggregation time limit - * to uCode in uSec - */ - if (priv && priv->cfg->bt_params && - priv->cfg->bt_params->agg_time_limit && - priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) - lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(priv->cfg->bt_params->agg_time_limit); -} - -static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} -/* rate scale requires free function to be implemented */ -static void rs_free(void *priv_rate) -{ - return; -} - -static void rs_free_sta(void *priv_r, struct ieee80211_sta *sta, - void *priv_sta) -{ - struct iwl_op_mode *op_mode __maybe_unused = priv_r; - struct iwl_priv *priv __maybe_unused = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_DEBUG_RATE(priv, "enter\n"); - IWL_DEBUG_RATE(priv, "leave\n"); -} - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags, int index) -{ - struct iwl_priv *priv; - u8 valid_tx_ant; - u8 ant_sel_tx; - - priv = lq_sta->drv; - valid_tx_ant = priv->hw_params.valid_tx_ant; - if (lq_sta->dbg_fixed_rate) { - ant_sel_tx = - ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) - >> RATE_MCS_ANT_POS); - if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { - *rate_n_flags = lq_sta->dbg_fixed_rate; - IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); - } else { - lq_sta->dbg_fixed_rate = 0; - IWL_ERR(priv, - "Invalid antenna selection 0x%X, Valid is 0x%X\n", - ant_sel_tx, valid_tx_ant); - IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); - } - } else { - IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); - } -} - -static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_priv *priv; - char buf[64]; - size_t buf_size; - u32 parsed_rate; - - - priv = lq_sta->drv; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->dbg_fixed_rate = parsed_rate; - else - lq_sta->dbg_fixed_rate = 0; - - rs_program_fix_rate(priv, lq_sta); - - return count; -} - -static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i = 0; - int index = 0; - ssize_t ret; - - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_priv *priv; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - - priv = lq_sta->drv; - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", - lq_sta->total_failed, lq_sta->total_success, - lq_sta->active_legacy_rate); - desc += sprintf(buff+desc, "fixed rate 0x%X\n", - lq_sta->dbg_fixed_rate); - desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (priv->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", - (priv->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", - (priv->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); - desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); - if (is_Ht(tbl->lq_type)) { - desc += sprintf(buff+desc, " %s", - (is_siso(tbl->lq_type)) ? "SISO" : - ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); - desc += sprintf(buff+desc, " %s", - (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += sprintf(buff+desc, " %s %s %s\n", (tbl->is_SGI) ? "SGI" : "", - (lq_sta->is_green) ? "GF enabled" : "", - (lq_sta->is_agg) ? "AGG on" : ""); - } - desc += sprintf(buff+desc, "last tx rate=0x%X\n", - lq_sta->last_rate_n_flags); - desc += sprintf(buff+desc, "general:" - "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", - lq_sta->lq.general_params.flags, - lq_sta->lq.general_params.mimo_delimiter, - lq_sta->lq.general_params.single_stream_ant_msk, - lq_sta->lq.general_params.dual_stream_ant_msk); - - desc += sprintf(buff+desc, "agg:" - "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", - le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), - lq_sta->lq.agg_params.agg_dis_start_th, - lq_sta->lq.agg_params.agg_frame_cnt_limit); - - desc += sprintf(buff+desc, - "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", - lq_sta->lq.general_params.start_rate_index[0], - lq_sta->lq.general_params.start_rate_index[1], - lq_sta->lq.general_params.start_rate_index[2], - lq_sta->lq.general_params.start_rate_index[3]); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - index = iwl_hwrate_to_plcp_idx( - le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); - if (is_legacy(tbl->lq_type)) { - desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), - iwl_rate_mcs[index].mbps); - } else { - desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps (%s)\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), - iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); - } - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_scale_table_ops = { - .write = rs_sta_dbgfs_scale_table_write, - .read = rs_sta_dbgfs_scale_table_read, - .open = simple_open, - .llseek = default_llseek, -}; -static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char *buff; - int desc = 0; - int i, j; - ssize_t ret; - - struct iwl_lq_sta *lq_sta = file->private_data; - - buff = kmalloc(1024, GFP_KERNEL); - if (!buff) - return -ENOMEM; - - for (i = 0; i < LQ_SIZE; i++) { - desc += sprintf(buff+desc, - "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" - "rate=0x%X\n", - lq_sta->active_tbl == i ? "*" : "x", - lq_sta->lq_info[i].lq_type, - lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_ht40, - lq_sta->lq_info[i].is_dup, - lq_sta->is_green, - lq_sta->lq_info[i].current_rate); - for (j = 0; j < IWL_RATE_COUNT; j++) { - desc += sprintf(buff+desc, - "counter=%d success=%d %%=%d\n", - lq_sta->lq_info[i].win[j].counter, - lq_sta->lq_info[i].win[j].success_counter, - lq_sta->lq_info[i].win[j].success_ratio); - } - } - ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); - kfree(buff); - return ret; -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = rs_sta_dbgfs_stats_table_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl_lq_sta *lq_sta = file->private_data; - struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; - char buff[120]; - int desc = 0; - - if (is_Ht(tbl->lq_type)) - desc += sprintf(buff+desc, - "Bit Rate= %d Mb/s\n", - tbl->expected_tpt[lq_sta->last_txrate_idx]); - else - desc += sprintf(buff+desc, - "Bit Rate= %d Mb/s\n", - iwl_rates[lq_sta->last_txrate_idx].ieee >> 1); - - return simple_read_from_buffer(user_buf, count, ppos, buff, desc); -} - -static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { - .read = rs_sta_dbgfs_rate_scale_data_read, - .open = simple_open, - .llseek = default_llseek, -}; - -static void rs_add_debugfs(void *priv, void *priv_sta, - struct dentry *dir) -{ - struct iwl_lq_sta *lq_sta = priv_sta; - lq_sta->rs_sta_dbgfs_scale_table_file = - debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - lq_sta->rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_stats_table_ops); - lq_sta->rs_sta_dbgfs_rate_scale_data_file = - debugfs_create_file("rate_scale_data", S_IRUSR, dir, - lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); - lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = - debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, - &lq_sta->tx_agg_tid_en); - -} - -static void rs_remove_debugfs(void *priv, void *priv_sta) -{ - struct iwl_lq_sta *lq_sta = priv_sta; - debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); -} -#endif - -/* - * Initialization of rate scaling information is done by driver after - * the station is added. Since mac80211 calls this function before a - * station is added we ignore it. - */ -static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) -{ -} -static struct rate_control_ops rs_ops = { - .module = NULL, - .name = RS_NAME, - .tx_status = rs_tx_status, - .get_rate = rs_get_rate, - .rate_init = rs_rate_init_stub, - .alloc = rs_alloc, - .free = rs_free, - .alloc_sta = rs_alloc_sta, - .free_sta = rs_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = rs_add_debugfs, - .remove_sta_debugfs = rs_remove_debugfs, -#endif -}; - -int iwlagn_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_ops); -} - -void iwlagn_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_ops); -} - diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h deleted file mode 100644 index 82d02e1ae89f..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ /dev/null @@ -1,432 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_agn_rs_h__ -#define __iwl_agn_rs_h__ - -#include - -#include "iwl-commands.h" -#include "iwl-config.h" - -struct iwl_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ - u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ - u8 prev_ieee; /* previous rate in IEEE speeds */ - u8 next_ieee; /* next rate in IEEE speeds */ - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ - u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ - u8 next_rs_tgg; /* next rate used in TGG rs algo */ -}; - -/* - * These serve as indexes into - * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - */ -enum { - IWL_RATE_1M_INDEX = 0, - IWL_RATE_2M_INDEX, - IWL_RATE_5M_INDEX, - IWL_RATE_11M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, - IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, - IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, - IWL_RATE_54M_INDEX, - IWL_RATE_60M_INDEX, - IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ - IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */ - IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, - IWL_RATE_INVALID = IWL_RATE_COUNT, -}; - -enum { - IWL_RATE_6M_INDEX_TABLE = 0, - IWL_RATE_9M_INDEX_TABLE, - IWL_RATE_12M_INDEX_TABLE, - IWL_RATE_18M_INDEX_TABLE, - IWL_RATE_24M_INDEX_TABLE, - IWL_RATE_36M_INDEX_TABLE, - IWL_RATE_48M_INDEX_TABLE, - IWL_RATE_54M_INDEX_TABLE, - IWL_RATE_1M_INDEX_TABLE, - IWL_RATE_2M_INDEX_TABLE, - IWL_RATE_5M_INDEX_TABLE, - IWL_RATE_11M_INDEX_TABLE, - IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, -}; - -enum { - IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, - IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, - IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, - IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, -}; - -/* #define vs. enum to keep from defaulting to 'large integer' */ -#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) -#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) -#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) -#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) -#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) -#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) -#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) -#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) -#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) -#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) -#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) -#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) -#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) - -/* uCode API values for legacy bit rates, both OFDM and CCK */ -enum { - IWL_RATE_6M_PLCP = 13, - IWL_RATE_9M_PLCP = 15, - IWL_RATE_12M_PLCP = 5, - IWL_RATE_18M_PLCP = 7, - IWL_RATE_24M_PLCP = 9, - IWL_RATE_36M_PLCP = 11, - IWL_RATE_48M_PLCP = 1, - IWL_RATE_54M_PLCP = 3, - IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ - IWL_RATE_1M_PLCP = 10, - IWL_RATE_2M_PLCP = 20, - IWL_RATE_5M_PLCP = 55, - IWL_RATE_11M_PLCP = 110, - /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ - /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ -}; - -/* uCode API values for OFDM high-throughput (HT) bit rates */ -enum { - IWL_RATE_SISO_6M_PLCP = 0, - IWL_RATE_SISO_12M_PLCP = 1, - IWL_RATE_SISO_18M_PLCP = 2, - IWL_RATE_SISO_24M_PLCP = 3, - IWL_RATE_SISO_36M_PLCP = 4, - IWL_RATE_SISO_48M_PLCP = 5, - IWL_RATE_SISO_54M_PLCP = 6, - IWL_RATE_SISO_60M_PLCP = 7, - IWL_RATE_MIMO2_6M_PLCP = 0x8, - IWL_RATE_MIMO2_12M_PLCP = 0x9, - IWL_RATE_MIMO2_18M_PLCP = 0xa, - IWL_RATE_MIMO2_24M_PLCP = 0xb, - IWL_RATE_MIMO2_36M_PLCP = 0xc, - IWL_RATE_MIMO2_48M_PLCP = 0xd, - IWL_RATE_MIMO2_54M_PLCP = 0xe, - IWL_RATE_MIMO2_60M_PLCP = 0xf, - IWL_RATE_MIMO3_6M_PLCP = 0x10, - IWL_RATE_MIMO3_12M_PLCP = 0x11, - IWL_RATE_MIMO3_18M_PLCP = 0x12, - IWL_RATE_MIMO3_24M_PLCP = 0x13, - IWL_RATE_MIMO3_36M_PLCP = 0x14, - IWL_RATE_MIMO3_48M_PLCP = 0x15, - IWL_RATE_MIMO3_54M_PLCP = 0x16, - IWL_RATE_MIMO3_60M_PLCP = 0x17, - IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, -}; - -/* MAC header values for bit rates */ -enum { - IWL_RATE_6M_IEEE = 12, - IWL_RATE_9M_IEEE = 18, - IWL_RATE_12M_IEEE = 24, - IWL_RATE_18M_IEEE = 36, - IWL_RATE_24M_IEEE = 48, - IWL_RATE_36M_IEEE = 72, - IWL_RATE_48M_IEEE = 96, - IWL_RATE_54M_IEEE = 108, - IWL_RATE_60M_IEEE = 120, - IWL_RATE_1M_IEEE = 2, - IWL_RATE_2M_IEEE = 4, - IWL_RATE_5M_IEEE = 11, - IWL_RATE_11M_IEEE = 22, -}; - -#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) - -#define IWL_INVALID_VALUE -1 - -#define IWL_MIN_RSSI_VAL -100 -#define IWL_MAX_RSSI_VAL 0 - -/* These values specify how many Tx frame attempts before - * searching for a new modulation mode */ -#define IWL_LEGACY_FAILURE_LIMIT 160 -#define IWL_LEGACY_SUCCESS_LIMIT 480 -#define IWL_LEGACY_TABLE_COUNT 160 - -#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 -#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_NONE_LEGACY_TABLE_COUNT 1500 - -/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ -#define IWL_RS_GOOD_RATIO 12800 /* 100% */ -#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ -#define IWL_RATE_HIGH_TH 10880 /* 85% */ -#define IWL_RATE_INCREASE_TH 6400 /* 50% */ -#define IWL_RATE_DECREASE_TH 1920 /* 15% */ - -/* possible actions when in legacy mode */ -#define IWL_LEGACY_SWITCH_ANTENNA1 0 -#define IWL_LEGACY_SWITCH_ANTENNA2 1 -#define IWL_LEGACY_SWITCH_SISO 2 -#define IWL_LEGACY_SWITCH_MIMO2_AB 3 -#define IWL_LEGACY_SWITCH_MIMO2_AC 4 -#define IWL_LEGACY_SWITCH_MIMO2_BC 5 -#define IWL_LEGACY_SWITCH_MIMO3_ABC 6 - -/* possible actions when in siso mode */ -#define IWL_SISO_SWITCH_ANTENNA1 0 -#define IWL_SISO_SWITCH_ANTENNA2 1 -#define IWL_SISO_SWITCH_MIMO2_AB 2 -#define IWL_SISO_SWITCH_MIMO2_AC 3 -#define IWL_SISO_SWITCH_MIMO2_BC 4 -#define IWL_SISO_SWITCH_GI 5 -#define IWL_SISO_SWITCH_MIMO3_ABC 6 - - -/* possible actions when in mimo mode */ -#define IWL_MIMO2_SWITCH_ANTENNA1 0 -#define IWL_MIMO2_SWITCH_ANTENNA2 1 -#define IWL_MIMO2_SWITCH_SISO_A 2 -#define IWL_MIMO2_SWITCH_SISO_B 3 -#define IWL_MIMO2_SWITCH_SISO_C 4 -#define IWL_MIMO2_SWITCH_GI 5 -#define IWL_MIMO2_SWITCH_MIMO3_ABC 6 - - -/* possible actions when in mimo3 mode */ -#define IWL_MIMO3_SWITCH_ANTENNA1 0 -#define IWL_MIMO3_SWITCH_ANTENNA2 1 -#define IWL_MIMO3_SWITCH_SISO_A 2 -#define IWL_MIMO3_SWITCH_SISO_B 3 -#define IWL_MIMO3_SWITCH_SISO_C 4 -#define IWL_MIMO3_SWITCH_MIMO2_AB 5 -#define IWL_MIMO3_SWITCH_MIMO2_AC 6 -#define IWL_MIMO3_SWITCH_MIMO2_BC 7 -#define IWL_MIMO3_SWITCH_GI 8 - - -#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI -#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC - -/*FIXME:RS:add possible actions for MIMO3*/ - -#define IWL_ACTION_LIMIT 3 /* # possible actions */ - -#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ - -/* load per tid defines for A-MPDU activation */ -#define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_LOAD_THRESHOLD 10 -#define IWL_AGG_ALL_TID 0xff -#define TID_QUEUE_CELL_SPACING 50 /*mS */ -#define TID_QUEUE_MAX_SIZE 20 -#define TID_ROUND_VALUE 5 /* mS */ - -#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) -#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) - -extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - -enum iwl_table_type { - LQ_NONE, - LQ_G, /* legacy types */ - LQ_A, - LQ_SISO, /* high-throughput types */ - LQ_MIMO2, - LQ_MIMO3, - LQ_MAX, -}; - -#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) -#define is_siso(tbl) ((tbl) == LQ_SISO) -#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) -#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) -#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_A) -#define is_g_and(tbl) ((tbl) == LQ_G) - -#define IWL_MAX_MCS_DISPLAY_SIZE 12 - -struct iwl_rate_mcs_info { - char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; - char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; -}; - -/** - * struct iwl_rate_scale_data -- tx success history for one rate - */ -struct iwl_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; -}; - -/** - * struct iwl_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct iwl_lq_sta, - * one for "active", and one for "search". - */ -struct iwl_scale_tbl_info { - enum iwl_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u8 is_ht40; /* 1 = 40 MHz channel width */ - u8 is_dup; /* 1 = duplicated data streams */ - u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ - u8 max_search; /* maximun number of tables we can search */ - s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ - struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ -}; - -struct iwl_traffic_load { - unsigned long time_stamp; /* age of the oldest statistics */ - u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time - * slice */ - u32 total; /* total num of packets during the - * last TID_MAX_TIME_DIFF */ - u8 queue_count; /* number of queues that has - * been used since the last cleanup */ - u8 head; /* start of the circular buffer */ -}; - -/** - * struct iwl_lq_sta -- driver's rate scaling private structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct iwl_lq_sta { - u8 active_tbl; /* index of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u64 flush_timer; /* time staying in mode before new search */ - - u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; - u8 is_dup; - enum ieee80211_band band; - - /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - u32 supp_rates; - u16 active_legacy_rate; - u16 active_siso_rate; - u16 active_mimo2_rate; - u16 active_mimo3_rate; - s8 max_rate_idx; /* Max rate set by user */ - u8 missed_rate_counter; - - struct iwl_link_quality_cmd lq; - struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; - u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_rate_scale_data_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - u32 dbg_fixed_rate; -#endif - struct iwl_priv *drv; - - /* used to be in sta_info */ - int last_txrate_idx; - /* last tx rate_n_flags */ - u32 last_rate_n_flags; - /* packets destined for this STA are aggregated */ - u8 is_agg; - /* BT traffic this sta was last updated in */ - u8 last_bt_traffic; -}; - -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - -static inline u8 first_antenna(u8 mask) -{ - if (mask & ANT_A) - return ANT_A; - if (mask & ANT_B) - return ANT_B; - return ANT_C; -} - - -/* Initialize station's rate scaling information after adding station */ -extern void iwl_rs_rate_init(struct iwl_priv *priv, - struct ieee80211_sta *sta, u8 sta_id); - -/** - * iwl_rate_control_register - Register the rate control algorithm callbacks - * - * Since the rate control algorithm is hardware specific, there is no need - * or reason to place it as a stand alone module. The driver can call - * iwl_rate_control_register in order to register the rate control callbacks - * with the mac80211 subsystem. This should be performed prior to calling - * ieee80211_register_hw - * - */ -extern int iwlagn_rate_control_register(void); - -/** - * iwl_rate_control_unregister - Unregister the rate control callbacks - * - * This should be called after calling ieee80211_unregister_hw, but before - * the driver is unloaded. - */ -extern void iwlagn_rate_control_unregister(void); - -#endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c deleted file mode 100644 index 5035d2db14b1..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ /dev/null @@ -1,1168 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portionhelp of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn-calib.h" -#include "iwl-agn.h" -#include "iwl-modparams.h" - -#define IWL_CMD_ENTRY(x) [x] = #x - -const char *iwl_dvm_cmd_strings[REPLY_MAX] = { - IWL_CMD_ENTRY(REPLY_ALIVE), - IWL_CMD_ENTRY(REPLY_ERROR), - IWL_CMD_ENTRY(REPLY_ECHO), - IWL_CMD_ENTRY(REPLY_RXON), - IWL_CMD_ENTRY(REPLY_RXON_ASSOC), - IWL_CMD_ENTRY(REPLY_QOS_PARAM), - IWL_CMD_ENTRY(REPLY_RXON_TIMING), - IWL_CMD_ENTRY(REPLY_ADD_STA), - IWL_CMD_ENTRY(REPLY_REMOVE_STA), - IWL_CMD_ENTRY(REPLY_REMOVE_ALL_STA), - IWL_CMD_ENTRY(REPLY_TXFIFO_FLUSH), - IWL_CMD_ENTRY(REPLY_WEPKEY), - IWL_CMD_ENTRY(REPLY_TX), - IWL_CMD_ENTRY(REPLY_LEDS_CMD), - IWL_CMD_ENTRY(REPLY_TX_LINK_QUALITY_CMD), - IWL_CMD_ENTRY(COEX_PRIORITY_TABLE_CMD), - IWL_CMD_ENTRY(COEX_MEDIUM_NOTIFICATION), - IWL_CMD_ENTRY(COEX_EVENT_CMD), - IWL_CMD_ENTRY(REPLY_QUIET_CMD), - IWL_CMD_ENTRY(REPLY_CHANNEL_SWITCH), - IWL_CMD_ENTRY(CHANNEL_SWITCH_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_SPECTRUM_MEASUREMENT_CMD), - IWL_CMD_ENTRY(SPECTRUM_MEASURE_NOTIFICATION), - IWL_CMD_ENTRY(POWER_TABLE_CMD), - IWL_CMD_ENTRY(PM_SLEEP_NOTIFICATION), - IWL_CMD_ENTRY(PM_DEBUG_STATISTIC_NOTIFIC), - IWL_CMD_ENTRY(REPLY_SCAN_CMD), - IWL_CMD_ENTRY(REPLY_SCAN_ABORT_CMD), - IWL_CMD_ENTRY(SCAN_START_NOTIFICATION), - IWL_CMD_ENTRY(SCAN_RESULTS_NOTIFICATION), - IWL_CMD_ENTRY(SCAN_COMPLETE_NOTIFICATION), - IWL_CMD_ENTRY(BEACON_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_BEACON), - IWL_CMD_ENTRY(WHO_IS_AWAKE_NOTIFICATION), - IWL_CMD_ENTRY(QUIET_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_PWR_TABLE_CMD), - IWL_CMD_ENTRY(MEASURE_ABORT_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_BT_CONFIG), - IWL_CMD_ENTRY(REPLY_STATISTICS_CMD), - IWL_CMD_ENTRY(STATISTICS_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_CARD_STATE_CMD), - IWL_CMD_ENTRY(CARD_STATE_NOTIFICATION), - IWL_CMD_ENTRY(MISSED_BEACONS_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_CT_KILL_CONFIG_CMD), - IWL_CMD_ENTRY(SENSITIVITY_CMD), - IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), - IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), - IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), - IWL_CMD_ENTRY(REPLY_RX), - IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), - IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), - IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), - IWL_CMD_ENTRY(CALIBRATION_COMPLETE_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_TX_POWER_DBM_CMD), - IWL_CMD_ENTRY(TEMPERATURE_NOTIFICATION), - IWL_CMD_ENTRY(TX_ANT_CONFIGURATION_CMD), - IWL_CMD_ENTRY(REPLY_BT_COEX_PROFILE_NOTIF), - IWL_CMD_ENTRY(REPLY_BT_COEX_PRIO_TABLE), - IWL_CMD_ENTRY(REPLY_BT_COEX_PROT_ENV), - IWL_CMD_ENTRY(REPLY_WIPAN_PARAMS), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON_TIMING), - IWL_CMD_ENTRY(REPLY_WIPAN_RXON_ASSOC), - IWL_CMD_ENTRY(REPLY_WIPAN_QOS_PARAM), - IWL_CMD_ENTRY(REPLY_WIPAN_WEPKEY), - IWL_CMD_ENTRY(REPLY_WIPAN_P2P_CHANNEL_SWITCH), - IWL_CMD_ENTRY(REPLY_WIPAN_NOA_NOTIFICATION), - IWL_CMD_ENTRY(REPLY_WIPAN_DEACTIVATION_COMPLETE), - IWL_CMD_ENTRY(REPLY_WOWLAN_PATTERNS), - IWL_CMD_ENTRY(REPLY_WOWLAN_WAKEUP_FILTER), - IWL_CMD_ENTRY(REPLY_WOWLAN_TSC_RSC_PARAMS), - IWL_CMD_ENTRY(REPLY_WOWLAN_TKIP_PARAMS), - IWL_CMD_ENTRY(REPLY_WOWLAN_KEK_KCK_MATERIAL), - IWL_CMD_ENTRY(REPLY_WOWLAN_GET_STATUS), - IWL_CMD_ENTRY(REPLY_D3_CONFIG), -}; -#undef IWL_CMD_ENTRY - -/****************************************************************************** - * - * Generic RX handler implementations - * - ******************************************************************************/ - -static int iwlagn_rx_reply_error(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_error_resp *err_resp = (void *)pkt->data; - - IWL_ERR(priv, "Error Reply type 0x%08X cmd REPLY_ERROR (0x%02X) " - "seq 0x%04X ser 0x%08X\n", - le32_to_cpu(err_resp->error_type), - err_resp->cmd_id, - le16_to_cpu(err_resp->bad_cmd_seq_num), - le32_to_cpu(err_resp->error_info)); - return 0; -} - -static int iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_csa_notification *csa = (void *)pkt->data; - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_rxon_cmd *rxon = (void *)&ctx->active; - - if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - return 0; - - if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) { - rxon->channel = csa->channel; - ctx->staging.channel = csa->channel; - IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", - le16_to_cpu(csa->channel)); - iwl_chswitch_done(priv, true); - } else { - IWL_ERR(priv, "CSA notif (fail) : channel %d\n", - le16_to_cpu(csa->channel)); - iwl_chswitch_done(priv, false); - } - return 0; -} - - -static int iwlagn_rx_spectrum_measure_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_spectrum_notification *report = (void *)pkt->data; - - if (!report->state) { - IWL_DEBUG_11H(priv, - "Spectrum Measure Notification: Start\n"); - return 0; - } - - memcpy(&priv->measure_report, report, sizeof(*report)); - priv->measurement_status |= MEASUREMENT_READY; - return 0; -} - -static int iwlagn_rx_pm_sleep_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_sleep_notification *sleep = (void *)pkt->data; - IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", - sleep->pm_sleep_mode, sleep->pm_wakeup_src); -#endif - return 0; -} - -static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 __maybe_unused len = - le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " - "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len); - iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len); - return 0; -} - -static int iwlagn_rx_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwlagn_beacon_notif *beacon = (void *)pkt->data; -#ifdef CONFIG_IWLWIFI_DEBUG - u16 status = le16_to_cpu(beacon->beacon_notify_hdr.status.status); - u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); - - IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d " - "tsf:0x%.8x%.8x rate:%d\n", - status & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le32_to_cpu(beacon->ibss_mgr_status), - le32_to_cpu(beacon->high_tsf), - le32_to_cpu(beacon->low_tsf), rate); -#endif - - priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); - - return 0; -} - -/** - * iwl_good_plcp_health - checks for plcp error. - * - * When the plcp error is exceeding the thresholds, reset the radio - * to improve the throughput. - */ -static bool iwlagn_good_plcp_health(struct iwl_priv *priv, - struct statistics_rx_phy *cur_ofdm, - struct statistics_rx_ht_phy *cur_ofdm_ht, - unsigned int msecs) -{ - int delta; - int threshold = priv->plcp_delta_threshold; - - if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { - IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); - return true; - } - - delta = le32_to_cpu(cur_ofdm->plcp_err) - - le32_to_cpu(priv->statistics.rx_ofdm.plcp_err) + - le32_to_cpu(cur_ofdm_ht->plcp_err) - - le32_to_cpu(priv->statistics.rx_ofdm_ht.plcp_err); - - /* Can be negative if firmware reset statistics */ - if (delta <= 0) - return true; - - if ((delta * 100 / msecs) > threshold) { - IWL_DEBUG_RADIO(priv, - "plcp health threshold %u delta %d msecs %u\n", - threshold, delta, msecs); - return false; - } - - return true; -} - -int iwl_force_rf_reset(struct iwl_priv *priv, bool external) -{ - struct iwl_rf_reset *rf_reset; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return -EAGAIN; - - if (!iwl_is_any_associated(priv)) { - IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); - return -ENOLINK; - } - - rf_reset = &priv->rf_reset; - rf_reset->reset_request_count++; - if (!external && rf_reset->last_reset_jiffies && - time_after(rf_reset->last_reset_jiffies + - IWL_DELAY_NEXT_FORCE_RF_RESET, jiffies)) { - IWL_DEBUG_INFO(priv, "RF reset rejected\n"); - rf_reset->reset_reject_count++; - return -EAGAIN; - } - rf_reset->reset_success_count++; - rf_reset->last_reset_jiffies = jiffies; - - /* - * There is no easy and better way to force reset the radio, - * the only known method is switching channel which will force to - * reset and tune the radio. - * Use internal short scan (single channel) operation to should - * achieve this objective. - * Driver should reset the radio when number of consecutive missed - * beacon, or any other uCode error condition detected. - */ - IWL_DEBUG_INFO(priv, "perform radio reset.\n"); - iwl_internal_short_hw_scan(priv); - return 0; -} - - -static void iwlagn_recover_from_statistics(struct iwl_priv *priv, - struct statistics_rx_phy *cur_ofdm, - struct statistics_rx_ht_phy *cur_ofdm_ht, - struct statistics_tx *tx, - unsigned long stamp) -{ - unsigned int msecs; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - msecs = jiffies_to_msecs(stamp - priv->rx_statistics_jiffies); - - /* Only gather statistics and update time stamp when not associated */ - if (!iwl_is_any_associated(priv)) - return; - - /* Do not check/recover when do not have enough statistics data */ - if (msecs < 99) - return; - - if (iwlwifi_mod_params.plcp_check && - !iwlagn_good_plcp_health(priv, cur_ofdm, cur_ofdm_ht, msecs)) - iwl_force_rf_reset(priv, false); -} - -/* Calculate noise level, based on measurements during network silence just - * before arriving beacon. This measurement can be done only if we know - * exactly when to expect beacons, therefore only when we're associated. */ -static void iwlagn_rx_calc_noise(struct iwl_priv *priv) -{ - struct statistics_rx_non_phy *rx_info; - int num_active_rx = 0; - int total_silence = 0; - int bcn_silence_a, bcn_silence_b, bcn_silence_c; - int last_rx_noise; - - rx_info = &priv->statistics.rx_non_phy; - - bcn_silence_a = - le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; - bcn_silence_b = - le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; - bcn_silence_c = - le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; - - if (bcn_silence_a) { - total_silence += bcn_silence_a; - num_active_rx++; - } - if (bcn_silence_b) { - total_silence += bcn_silence_b; - num_active_rx++; - } - if (bcn_silence_c) { - total_silence += bcn_silence_c; - num_active_rx++; - } - - /* Average among active antennas */ - if (num_active_rx) - last_rx_noise = (total_silence / num_active_rx) - 107; - else - last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; - - IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", - bcn_silence_a, bcn_silence_b, bcn_silence_c, - last_rx_noise); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -/* - * based on the assumption of all statistics counter are in DWORD - * FIXME: This function is for debugging, do not deal with - * the case of counters roll-over. - */ -static void accum_stats(__le32 *prev, __le32 *cur, __le32 *delta, - __le32 *max_delta, __le32 *accum, int size) -{ - int i; - - for (i = 0; - i < size / sizeof(__le32); - i++, prev++, cur++, delta++, max_delta++, accum++) { - if (le32_to_cpu(*cur) > le32_to_cpu(*prev)) { - *delta = cpu_to_le32( - le32_to_cpu(*cur) - le32_to_cpu(*prev)); - le32_add_cpu(accum, le32_to_cpu(*delta)); - if (le32_to_cpu(*delta) > le32_to_cpu(*max_delta)) - *max_delta = *delta; - } - } -} - -static void -iwlagn_accumulative_statistics(struct iwl_priv *priv, - struct statistics_general_common *common, - struct statistics_rx_non_phy *rx_non_phy, - struct statistics_rx_phy *rx_ofdm, - struct statistics_rx_ht_phy *rx_ofdm_ht, - struct statistics_rx_phy *rx_cck, - struct statistics_tx *tx, - struct statistics_bt_activity *bt_activity) -{ -#define ACCUM(_name) \ - accum_stats((__le32 *)&priv->statistics._name, \ - (__le32 *)_name, \ - (__le32 *)&priv->delta_stats._name, \ - (__le32 *)&priv->max_delta_stats._name, \ - (__le32 *)&priv->accum_stats._name, \ - sizeof(*_name)); - - ACCUM(common); - ACCUM(rx_non_phy); - ACCUM(rx_ofdm); - ACCUM(rx_ofdm_ht); - ACCUM(rx_cck); - ACCUM(tx); - if (bt_activity) - ACCUM(bt_activity); -#undef ACCUM -} -#else -static inline void -iwlagn_accumulative_statistics(struct iwl_priv *priv, - struct statistics_general_common *common, - struct statistics_rx_non_phy *rx_non_phy, - struct statistics_rx_phy *rx_ofdm, - struct statistics_rx_ht_phy *rx_ofdm_ht, - struct statistics_rx_phy *rx_cck, - struct statistics_tx *tx, - struct statistics_bt_activity *bt_activity) -{ -} -#endif - -static int iwlagn_rx_statistics(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - unsigned long stamp = jiffies; - const int reg_recalib_period = 60; - int change; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - __le32 *flag; - struct statistics_general_common *common; - struct statistics_rx_non_phy *rx_non_phy; - struct statistics_rx_phy *rx_ofdm; - struct statistics_rx_ht_phy *rx_ofdm_ht; - struct statistics_rx_phy *rx_cck; - struct statistics_tx *tx; - struct statistics_bt_activity *bt_activity; - - len -= sizeof(struct iwl_cmd_header); /* skip header */ - - IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", - len); - - spin_lock(&priv->statistics.lock); - - if (len == sizeof(struct iwl_bt_notif_statistics)) { - struct iwl_bt_notif_statistics *stats; - stats = (void *)&pkt->data; - flag = &stats->flag; - common = &stats->general.common; - rx_non_phy = &stats->rx.general.common; - rx_ofdm = &stats->rx.ofdm; - rx_ofdm_ht = &stats->rx.ofdm_ht; - rx_cck = &stats->rx.cck; - tx = &stats->tx; - bt_activity = &stats->general.activity; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* handle this exception directly */ - priv->statistics.num_bt_kills = stats->rx.general.num_bt_kills; - le32_add_cpu(&priv->statistics.accum_num_bt_kills, - le32_to_cpu(stats->rx.general.num_bt_kills)); -#endif - } else if (len == sizeof(struct iwl_notif_statistics)) { - struct iwl_notif_statistics *stats; - stats = (void *)&pkt->data; - flag = &stats->flag; - common = &stats->general.common; - rx_non_phy = &stats->rx.general; - rx_ofdm = &stats->rx.ofdm; - rx_ofdm_ht = &stats->rx.ofdm_ht; - rx_cck = &stats->rx.cck; - tx = &stats->tx; - bt_activity = NULL; - } else { - WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n", - len, sizeof(struct iwl_bt_notif_statistics), - sizeof(struct iwl_notif_statistics)); - spin_unlock(&priv->statistics.lock); - return 0; - } - - change = common->temperature != priv->statistics.common.temperature || - (*flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK) != - (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK); - - iwlagn_accumulative_statistics(priv, common, rx_non_phy, rx_ofdm, - rx_ofdm_ht, rx_cck, tx, bt_activity); - - iwlagn_recover_from_statistics(priv, rx_ofdm, rx_ofdm_ht, tx, stamp); - - priv->statistics.flag = *flag; - memcpy(&priv->statistics.common, common, sizeof(*common)); - memcpy(&priv->statistics.rx_non_phy, rx_non_phy, sizeof(*rx_non_phy)); - memcpy(&priv->statistics.rx_ofdm, rx_ofdm, sizeof(*rx_ofdm)); - memcpy(&priv->statistics.rx_ofdm_ht, rx_ofdm_ht, sizeof(*rx_ofdm_ht)); - memcpy(&priv->statistics.rx_cck, rx_cck, sizeof(*rx_cck)); - memcpy(&priv->statistics.tx, tx, sizeof(*tx)); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (bt_activity) - memcpy(&priv->statistics.bt_activity, bt_activity, - sizeof(*bt_activity)); -#endif - - priv->rx_statistics_jiffies = stamp; - - set_bit(STATUS_STATISTICS, &priv->status); - - /* Reschedule the statistics timer to occur in - * reg_recalib_period seconds to ensure we get a - * thermal update even if the uCode doesn't give - * us one */ - mod_timer(&priv->statistics_periodic, jiffies + - msecs_to_jiffies(reg_recalib_period * 1000)); - - if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && - (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { - iwlagn_rx_calc_noise(priv); - queue_work(priv->workqueue, &priv->run_time_calib_work); - } - if (priv->lib->temperature && change) - priv->lib->temperature(priv); - - spin_unlock(&priv->statistics.lock); - - return 0; -} - -static int iwlagn_rx_reply_statistics(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_notif_statistics *stats = (void *)pkt->data; - - if (le32_to_cpu(stats->flag) & UCODE_STATISTICS_CLEAR_MSK) { -#ifdef CONFIG_IWLWIFI_DEBUGFS - memset(&priv->accum_stats, 0, - sizeof(priv->accum_stats)); - memset(&priv->delta_stats, 0, - sizeof(priv->delta_stats)); - memset(&priv->max_delta_stats, 0, - sizeof(priv->max_delta_stats)); -#endif - IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); - } - iwlagn_rx_statistics(priv, rxb, cmd); - return 0; -} - -/* Handle notification from uCode that card's power state is changing - * due to software, hardware, or critical temperature RFKILL */ -static int iwlagn_rx_card_state_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; - u32 flags = le32_to_cpu(card_state_notif->flags); - unsigned long status = priv->status; - - IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On", - (flags & CT_CARD_DISABLED) ? - "Reached" : "Not reached"); - - if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | - CT_CARD_DISABLED)) { - - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - if (!(flags & RXON_CARD_DISABLED)) { - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - iwl_write_direct32(priv->trans, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - } - if (flags & CT_CARD_DISABLED) - iwl_tt_enter_ct_kill(priv); - } - if (!(flags & CT_CARD_DISABLED)) - iwl_tt_exit_ct_kill(priv); - - if (flags & HW_CARD_DISABLED) - set_bit(STATUS_RF_KILL_HW, &priv->status); - else - clear_bit(STATUS_RF_KILL_HW, &priv->status); - - - if (!(flags & RXON_CARD_DISABLED)) - iwl_scan_cancel(priv); - - if ((test_bit(STATUS_RF_KILL_HW, &status) != - test_bit(STATUS_RF_KILL_HW, &priv->status))) - wiphy_rfkill_set_hw_state(priv->hw->wiphy, - test_bit(STATUS_RF_KILL_HW, &priv->status)); - else - wake_up(&priv->trans->wait_command_queue); - return 0; -} - -static int iwlagn_rx_missed_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) - -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacon_notif *missed_beacon = (void *)pkt->data; - - if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > - priv->missed_beacon_threshold) { - IWL_DEBUG_CALIB(priv, - "missed bcn cnsq %d totl %d rcd %d expctd %d\n", - le32_to_cpu(missed_beacon->consecutive_missed_beacons), - le32_to_cpu(missed_beacon->total_missed_becons), - le32_to_cpu(missed_beacon->num_recvd_beacons), - le32_to_cpu(missed_beacon->num_expected_beacons)); - if (!test_bit(STATUS_SCANNING, &priv->status)) - iwl_init_sensitivity(priv); - } - return 0; -} - -/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). - * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ -static int iwlagn_rx_reply_rx_phy(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - - priv->last_phy_res_valid = true; - memcpy(&priv->last_phy_res, pkt->data, - sizeof(struct iwl_rx_phy_res)); - return 0; -} - -/* - * returns non-zero if packet should be dropped - */ -static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, - u32 decrypt_res, - struct ieee80211_rx_status *stats) -{ - u16 fc = le16_to_cpu(hdr->frame_control); - - /* - * All contexts have the same setting here due to it being - * a module parameter, so OK to check any context. - */ - if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags & - RXON_FILTER_DIS_DECRYPT_MSK) - return 0; - - if (!(fc & IEEE80211_FCTL_PROTECTED)) - return 0; - - IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); - switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { - case RX_RES_STATUS_SEC_TYPE_TKIP: - /* The uCode has got a bad phase 1 Key, pushes the packet. - * Decryption will be done in SW. */ - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_BAD_KEY_TTAK) - break; - - case RX_RES_STATUS_SEC_TYPE_WEP: - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_BAD_ICV_MIC) { - /* bad ICV, the packet is destroyed since the - * decryption is inplace, drop it */ - IWL_DEBUG_RX(priv, "Packet destroyed\n"); - return -1; - } - case RX_RES_STATUS_SEC_TYPE_CCMP: - if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == - RX_RES_STATUS_DECRYPT_OK) { - IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); - stats->flag |= RX_FLAG_DECRYPTED; - } - break; - - default: - break; - } - return 0; -} - -static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, - u16 len, - u32 ampdu_status, - struct iwl_rx_cmd_buffer *rxb, - struct ieee80211_rx_status *stats) -{ - struct sk_buff *skb; - __le16 fc = hdr->frame_control; - struct iwl_rxon_context *ctx; - unsigned int hdrlen, fraglen; - - /* We only process data packets if the interface is open */ - if (unlikely(!priv->is_open)) { - IWL_DEBUG_DROP_LIMIT(priv, - "Dropping packet while interface is not open.\n"); - return; - } - - /* In case of HW accelerated crypto and bad decryption, drop */ - if (!iwlwifi_mod_params.sw_crypto && - iwlagn_set_decrypted_flag(priv, hdr, ampdu_status, stats)) - return; - - /* Dont use dev_alloc_skb(), we'll have enough headroom once - * ieee80211_hdr pulled. - */ - skb = alloc_skb(128, GFP_ATOMIC); - if (!skb) { - IWL_ERR(priv, "alloc_skb failed\n"); - return; - } - /* If frame is small enough to fit in skb->head, pull it completely. - * If not, only pull ieee80211_hdr so that splice() or TCP coalesce - * are more efficient. - */ - hdrlen = (len <= skb_tailroom(skb)) ? len : sizeof(*hdr); - - memcpy(skb_put(skb, hdrlen), hdr, hdrlen); - fraglen = len - hdrlen; - - if (fraglen) { - int offset = (void *)hdr + hdrlen - - rxb_addr(rxb) + rxb_offset(rxb); - - skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, - fraglen, rxb->truesize); - } - - /* - * Wake any queues that were stopped due to a passive channel tx - * failure. This can happen because the regulatory enforcement in - * the device waits for a beacon before allowing transmission, - * sometimes even after already having transmitted frames for the - * association because the new RXON may reset the information. - */ - if (unlikely(ieee80211_is_beacon(fc) && priv->passive_no_rx)) { - for_each_context(priv, ctx) { - if (!ether_addr_equal(hdr->addr3, - ctx->active.bssid_addr)) - continue; - iwlagn_lift_passive_no_rx(priv); - } - } - - memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - - ieee80211_rx(priv->hw, skb); -} - -static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) -{ - u32 decrypt_out = 0; - - if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == - RX_RES_STATUS_STATION_FOUND) - decrypt_out |= (RX_RES_STATUS_STATION_FOUND | - RX_RES_STATUS_NO_STATION_INFO_MISMATCH); - - decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); - - /* packet was not encrypted */ - if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == - RX_RES_STATUS_SEC_TYPE_NONE) - return decrypt_out; - - /* packet was encrypted with unknown alg */ - if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == - RX_RES_STATUS_SEC_TYPE_ERR) - return decrypt_out; - - /* decryption was not done in HW */ - if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != - RX_MPDU_RES_STATUS_DEC_DONE_MSK) - return decrypt_out; - - switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { - - case RX_RES_STATUS_SEC_TYPE_CCMP: - /* alg is CCM: check MIC only */ - if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) - /* Bad MIC */ - decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; - else - decrypt_out |= RX_RES_STATUS_DECRYPT_OK; - - break; - - case RX_RES_STATUS_SEC_TYPE_TKIP: - if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { - /* Bad TTAK */ - decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; - break; - } - /* fall through if TTAK OK */ - default: - if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) - decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; - else - decrypt_out |= RX_RES_STATUS_DECRYPT_OK; - break; - } - - IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n", - decrypt_in, decrypt_out); - - return decrypt_out; -} - -/* Calc max signal level (dBm) among 3 possible receivers */ -static int iwlagn_calc_rssi(struct iwl_priv *priv, - struct iwl_rx_phy_res *rx_resp) -{ - /* data from PHY/DSP regarding signal strength, etc., - * contents are always there, not configurable by host - */ - struct iwlagn_non_cfg_phy *ncphy = - (struct iwlagn_non_cfg_phy *)rx_resp->non_cfg_phy_buf; - u32 val, rssi_a, rssi_b, rssi_c, max_rssi; - u8 agc; - - val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_AGC_IDX]); - agc = (val & IWLAGN_OFDM_AGC_MSK) >> IWLAGN_OFDM_AGC_BIT_POS; - - /* Find max rssi among 3 possible receivers. - * These values are measured by the digital signal processor (DSP). - * They should stay fairly constant even as the signal strength varies, - * if the radio's automatic gain control (AGC) is working right. - * AGC value (see below) will provide the "interesting" info. - */ - val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_AB_IDX]); - rssi_a = (val & IWLAGN_OFDM_RSSI_INBAND_A_BITMSK) >> - IWLAGN_OFDM_RSSI_A_BIT_POS; - rssi_b = (val & IWLAGN_OFDM_RSSI_INBAND_B_BITMSK) >> - IWLAGN_OFDM_RSSI_B_BIT_POS; - val = le32_to_cpu(ncphy->non_cfg_phy[IWLAGN_RX_RES_RSSI_C_IDX]); - rssi_c = (val & IWLAGN_OFDM_RSSI_INBAND_C_BITMSK) >> - IWLAGN_OFDM_RSSI_C_BIT_POS; - - max_rssi = max_t(u32, rssi_a, rssi_b); - max_rssi = max_t(u32, max_rssi, rssi_c); - - IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", - rssi_a, rssi_b, rssi_c, max_rssi, agc); - - /* dBm = max_rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. */ - return max_rssi - agc - IWLAGN_RSSI_OFFSET; -} - -/* Called for REPLY_RX (legacy ABG frames), or - * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */ -static int iwlagn_rx_reply_rx(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_rx_phy_res *phy_res; - __le32 rx_pkt_status; - struct iwl_rx_mpdu_res_start *amsdu; - u32 len; - u32 ampdu_status; - u32 rate_n_flags; - - /** - * REPLY_RX and REPLY_RX_MPDU_CMD are handled differently. - * REPLY_RX: physical layer info is in this buffer - * REPLY_RX_MPDU_CMD: physical layer info was sent in separate - * command and cached in priv->last_phy_res - * - * Here we set up local variables depending on which command is - * received. - */ - if (pkt->hdr.cmd == REPLY_RX) { - phy_res = (struct iwl_rx_phy_res *)pkt->data; - header = (struct ieee80211_hdr *)(pkt->data + sizeof(*phy_res) - + phy_res->cfg_phy_cnt); - - len = le16_to_cpu(phy_res->byte_count); - rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*phy_res) + - phy_res->cfg_phy_cnt + len); - ampdu_status = le32_to_cpu(rx_pkt_status); - } else { - if (!priv->last_phy_res_valid) { - IWL_ERR(priv, "MPDU frame without cached PHY data\n"); - return 0; - } - phy_res = &priv->last_phy_res; - amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; - header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); - len = le16_to_cpu(amsdu->byte_count); - rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); - ampdu_status = iwlagn_translate_rx_status(priv, - le32_to_cpu(rx_pkt_status)); - } - - if ((unlikely(phy_res->cfg_phy_cnt > 20))) { - IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n", - phy_res->cfg_phy_cnt); - return 0; - } - - if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || - !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { - IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", - le32_to_cpu(rx_pkt_status)); - return 0; - } - - /* This will be used in several places later */ - rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); - - /* rx_status carries information about the packet to mac80211 */ - rx_status.mactime = le64_to_cpu(phy_res->timestamp); - rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? - IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), - rx_status.band); - rx_status.rate_idx = - iwlagn_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); - rx_status.flag = 0; - - /* TSF isn't reliable. In order to allow smooth user experience, - * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ - - priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); - - /* Find max signal strength (dBm) among 3 antenna/receiver chains */ - rx_status.signal = iwlagn_calc_rssi(priv, phy_res); - - IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n", - rx_status.signal, (unsigned long long)rx_status.mactime); - - /* - * "antenna number" - * - * It seems that the antenna field in the phy flags value - * is actually a bit field. This is undefined by radiotap, - * it wants an actual antenna number but I always get "7" - * for most legacy frames I receive indicating that the - * same frame was received on all three RX chains. - * - * I think this field should be removed in favor of a - * new 802.11n radiotap field "RX chains" that is defined - * as a bitmask. - */ - rx_status.antenna = - (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) - >> RX_RES_PHY_FLAGS_ANTENNA_POS; - - /* set the preamble flag if appropriate */ - if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) - rx_status.flag |= RX_FLAG_SHORTPRE; - - /* Set up the HT phy flags */ - if (rate_n_flags & RATE_MCS_HT_MSK) - rx_status.flag |= RX_FLAG_HT; - if (rate_n_flags & RATE_MCS_HT40_MSK) - rx_status.flag |= RX_FLAG_40MHZ; - if (rate_n_flags & RATE_MCS_SGI_MSK) - rx_status.flag |= RX_FLAG_SHORT_GI; - if (rate_n_flags & RATE_MCS_GF_MSK) - rx_status.flag |= RX_FLAG_HT_GF; - - iwlagn_pass_packet_to_mac80211(priv, header, len, ampdu_status, - rxb, &rx_status); - return 0; -} - -static int iwlagn_rx_noa_notification(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_wipan_noa_data *new_data, *old_data; - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->data; - - /* no condition -- we're in softirq */ - old_data = rcu_dereference_protected(priv->noa_data, true); - - if (noa_notif->noa_active) { - u32 len = le16_to_cpu(noa_notif->noa_attribute.length); - u32 copylen = len; - - /* EID, len, OUI, subtype */ - len += 1 + 1 + 3 + 1; - /* P2P id, P2P length */ - len += 1 + 2; - copylen += 1 + 2; - - new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); - if (new_data) { - new_data->length = len; - new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; - new_data->data[1] = len - 2; /* not counting EID, len */ - new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff; - new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff; - new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff; - new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P; - memcpy(&new_data->data[6], &noa_notif->noa_attribute, - copylen); - } - } else - new_data = NULL; - - rcu_assign_pointer(priv->noa_data, new_data); - - if (old_data) - kfree_rcu(old_data, rcu_head); - - return 0; -} - -/** - * iwl_setup_rx_handlers - Initialize Rx handler callbacks - * - * Setup the RX handlers for each of the reply types sent from the uCode - * to the host. - */ -void iwl_setup_rx_handlers(struct iwl_priv *priv) -{ - int (**handlers)(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); - - handlers = priv->rx_handlers; - - handlers[REPLY_ERROR] = iwlagn_rx_reply_error; - handlers[CHANNEL_SWITCH_NOTIFICATION] = iwlagn_rx_csa; - handlers[SPECTRUM_MEASURE_NOTIFICATION] = - iwlagn_rx_spectrum_measure_notif; - handlers[PM_SLEEP_NOTIFICATION] = iwlagn_rx_pm_sleep_notif; - handlers[PM_DEBUG_STATISTIC_NOTIFIC] = - iwlagn_rx_pm_debug_statistics_notif; - handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif; - handlers[REPLY_ADD_STA] = iwl_add_sta_callback; - - handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification; - - /* - * The same handler is used for both the REPLY to a discrete - * statistics request from the host as well as for the periodic - * statistics notifications (after received beacons) from the uCode. - */ - handlers[REPLY_STATISTICS_CMD] = iwlagn_rx_reply_statistics; - handlers[STATISTICS_NOTIFICATION] = iwlagn_rx_statistics; - - iwl_setup_rx_scan_handlers(priv); - - handlers[CARD_STATE_NOTIFICATION] = iwlagn_rx_card_state_notif; - handlers[MISSED_BEACONS_NOTIFICATION] = - iwlagn_rx_missed_beacon_notif; - - /* Rx handlers */ - handlers[REPLY_RX_PHY_CMD] = iwlagn_rx_reply_rx_phy; - handlers[REPLY_RX_MPDU_CMD] = iwlagn_rx_reply_rx; - - /* block ack */ - handlers[REPLY_COMPRESSED_BA] = - iwlagn_rx_reply_compressed_ba; - - priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; - - /* set up notification wait support */ - iwl_notification_wait_init(&priv->notif_wait); - - /* Set up BT Rx handlers */ - if (priv->cfg->bt_params) - iwlagn_bt_rx_handler_setup(priv); -} - -int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - void (*pre_rx_handler)(struct iwl_priv *, - struct iwl_rx_cmd_buffer *); - int err = 0; - - /* - * Do the notification wait before RX handlers so - * even if the RX handler consumes the RXB we have - * access to it in the notification wait entry. - */ - iwl_notification_wait_notify(&priv->notif_wait, pkt); - - /* RX data may be forwarded to userspace (using pre_rx_handler) in one - * of two cases: the first, that the user owns the uCode through - * testmode - in such case the pre_rx_handler is set and no further - * processing takes place. The other case is when the user want to - * monitor the rx w/o affecting the regular flow - the pre_rx_handler - * will be set but the ownership flag != IWL_OWNERSHIP_TM and the flow - * continues. - * We need to use ACCESS_ONCE to prevent a case where the handler - * changes between the check and the call. - */ - pre_rx_handler = ACCESS_ONCE(priv->pre_rx_handler); - if (pre_rx_handler) - pre_rx_handler(priv, rxb); - if (priv->ucode_owner != IWL_OWNERSHIP_TM) { - /* Based on type of command response or notification, - * handle those that need handling via function in - * rx_handlers table. See iwl_setup_rx_handlers() */ - if (priv->rx_handlers[pkt->hdr.cmd]) { - priv->rx_handlers_stats[pkt->hdr.cmd]++; - err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd); - } else { - /* No handling needed */ - IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", - iwl_dvm_get_cmd_string(pkt->hdr.cmd), - pkt->hdr.cmd); - } - } - return err; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c deleted file mode 100644 index 36b4965527c2..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ /dev/null @@ -1,1577 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include "iwl-dev.h" -#include "iwl-agn.h" -#include "iwl-agn-calib.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" - -/* - * initialize rxon structure with default values from eeprom - */ -void iwl_connection_init_rx_config(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - memset(&ctx->staging, 0, sizeof(ctx->staging)); - - if (!ctx->vif) { - ctx->staging.dev_type = ctx->unused_devtype; - } else - switch (ctx->vif->type) { - case NL80211_IFTYPE_AP: - ctx->staging.dev_type = ctx->ap_devtype; - break; - - case NL80211_IFTYPE_STATION: - ctx->staging.dev_type = ctx->station_devtype; - ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; - break; - - case NL80211_IFTYPE_ADHOC: - ctx->staging.dev_type = ctx->ibss_devtype; - ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; - ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | - RXON_FILTER_ACCEPT_GRP_MSK; - break; - - case NL80211_IFTYPE_MONITOR: - ctx->staging.dev_type = RXON_DEV_TYPE_SNIFFER; - break; - - default: - IWL_ERR(priv, "Unsupported interface type %d\n", - ctx->vif->type); - break; - } - -#if 0 - /* TODO: Figure out when short_preamble would be set and cache from - * that */ - if (!hw_to_local(priv->hw)->short_preamble) - ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - else - ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; -#endif - - ctx->staging.channel = cpu_to_le16(priv->hw->conf.channel->hw_value); - priv->band = priv->hw->conf.channel->band; - - iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); - - /* clear both MIX and PURE40 mode flag */ - ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | - RXON_FLG_CHANNEL_MODE_PURE_40); - if (ctx->vif) - memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); - - ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; - ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; - ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; -} - -static int iwlagn_disable_bss(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_rxon_cmd *send) -{ - __le32 old_filter = send->filter_flags; - int ret; - - send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - CMD_SYNC, sizeof(*send), send); - - send->filter_flags = old_filter; - - if (ret) - IWL_DEBUG_QUIET_RFKILL(priv, - "Error clearing ASSOC_MSK on BSS (%d)\n", ret); - - return ret; -} - -static int iwlagn_disable_pan(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_rxon_cmd *send) -{ - struct iwl_notification_wait disable_wait; - __le32 old_filter = send->filter_flags; - u8 old_dev_type = send->dev_type; - int ret; - static const u8 deactivate_cmd[] = { - REPLY_WIPAN_DEACTIVATION_COMPLETE - }; - - iwl_init_notification_wait(&priv->notif_wait, &disable_wait, - deactivate_cmd, ARRAY_SIZE(deactivate_cmd), - NULL, NULL); - - send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - send->dev_type = RXON_DEV_TYPE_P2P; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - CMD_SYNC, sizeof(*send), send); - - send->filter_flags = old_filter; - send->dev_type = old_dev_type; - - if (ret) { - IWL_ERR(priv, "Error disabling PAN (%d)\n", ret); - iwl_remove_notification(&priv->notif_wait, &disable_wait); - } else { - ret = iwl_wait_notification(&priv->notif_wait, - &disable_wait, HZ); - if (ret) - IWL_ERR(priv, "Timed out waiting for PAN disable\n"); - } - - return ret; -} - -static int iwlagn_disconn_pan(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_rxon_cmd *send) -{ - __le32 old_filter = send->filter_flags; - int ret; - - send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, - sizeof(*send), send); - - send->filter_flags = old_filter; - - return ret; -} - -static void iwlagn_update_qos(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret; - - if (!ctx->is_active) - return; - - ctx->qos_data.def_qos_parm.qos_flags = 0; - - if (ctx->qos_data.qos_active) - ctx->qos_data.def_qos_parm.qos_flags |= - QOS_PARAM_FLG_UPDATE_EDCA_MSK; - - if (ctx->ht.enabled) - ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; - - IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", - ctx->qos_data.qos_active, - ctx->qos_data.def_qos_parm.qos_flags); - - ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC, - sizeof(struct iwl_qosparam_cmd), - &ctx->qos_data.def_qos_parm); - if (ret) - IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); -} - -static int iwlagn_update_beacon(struct iwl_priv *priv, - struct ieee80211_vif *vif) -{ - lockdep_assert_held(&priv->mutex); - - dev_kfree_skb(priv->beacon_skb); - priv->beacon_skb = ieee80211_beacon_get(priv->hw, vif); - if (!priv->beacon_skb) - return -ENOMEM; - return iwlagn_send_beacon_cmd(priv); -} - -static int iwlagn_send_rxon_assoc(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret = 0; - struct iwl_rxon_assoc_cmd rxon_assoc; - const struct iwl_rxon_cmd *rxon1 = &ctx->staging; - const struct iwl_rxon_cmd *rxon2 = &ctx->active; - - if ((rxon1->flags == rxon2->flags) && - (rxon1->filter_flags == rxon2->filter_flags) && - (rxon1->cck_basic_rates == rxon2->cck_basic_rates) && - (rxon1->ofdm_ht_single_stream_basic_rates == - rxon2->ofdm_ht_single_stream_basic_rates) && - (rxon1->ofdm_ht_dual_stream_basic_rates == - rxon2->ofdm_ht_dual_stream_basic_rates) && - (rxon1->ofdm_ht_triple_stream_basic_rates == - rxon2->ofdm_ht_triple_stream_basic_rates) && - (rxon1->acquisition_data == rxon2->acquisition_data) && - (rxon1->rx_chain == rxon2->rx_chain) && - (rxon1->ofdm_basic_rates == rxon2->ofdm_basic_rates)) { - IWL_DEBUG_INFO(priv, "Using current RXON_ASSOC. Not resending.\n"); - return 0; - } - - rxon_assoc.flags = ctx->staging.flags; - rxon_assoc.filter_flags = ctx->staging.filter_flags; - rxon_assoc.ofdm_basic_rates = ctx->staging.ofdm_basic_rates; - rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; - rxon_assoc.reserved1 = 0; - rxon_assoc.reserved2 = 0; - rxon_assoc.reserved3 = 0; - rxon_assoc.ofdm_ht_single_stream_basic_rates = - ctx->staging.ofdm_ht_single_stream_basic_rates; - rxon_assoc.ofdm_ht_dual_stream_basic_rates = - ctx->staging.ofdm_ht_dual_stream_basic_rates; - rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; - rxon_assoc.ofdm_ht_triple_stream_basic_rates = - ctx->staging.ofdm_ht_triple_stream_basic_rates; - rxon_assoc.acquisition_data = ctx->staging.acquisition_data; - - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_assoc_cmd, - CMD_ASYNC, sizeof(rxon_assoc), &rxon_assoc); - return ret; -} - -static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) -{ - u16 new_val; - u16 beacon_factor; - - /* - * If mac80211 hasn't given us a beacon interval, program - * the default into the device (not checking this here - * would cause the adjustment below to return the maximum - * value, which may break PAN.) - */ - if (!beacon_val) - return DEFAULT_BEACON_INTERVAL; - - /* - * If the beacon interval we obtained from the peer - * is too large, we'll have to wake up more often - * (and in IBSS case, we'll beacon too much) - * - * For example, if max_beacon_val is 4096, and the - * requested beacon interval is 7000, we'll have to - * use 3500 to be able to wake up on the beacons. - * - * This could badly influence beacon detection stats. - */ - - beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; - new_val = beacon_val / beacon_factor; - - if (!new_val) - new_val = max_beacon_val; - - return new_val; -} - -static int iwl_send_rxon_timing(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - u64 tsf; - s32 interval_tm, rem; - struct ieee80211_conf *conf = NULL; - u16 beacon_int; - struct ieee80211_vif *vif = ctx->vif; - - conf = &priv->hw->conf; - - lockdep_assert_held(&priv->mutex); - - memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); - - ctx->timing.timestamp = cpu_to_le64(priv->timestamp); - ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); - - beacon_int = vif ? vif->bss_conf.beacon_int : 0; - - /* - * TODO: For IBSS we need to get atim_window from mac80211, - * for now just always use 0 - */ - ctx->timing.atim_window = 0; - - if (ctx->ctxid == IWL_RXON_CTX_PAN && - (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION) && - iwl_is_associated(priv, IWL_RXON_CTX_BSS) && - priv->contexts[IWL_RXON_CTX_BSS].vif && - priv->contexts[IWL_RXON_CTX_BSS].vif->bss_conf.beacon_int) { - ctx->timing.beacon_interval = - priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval; - beacon_int = le16_to_cpu(ctx->timing.beacon_interval); - } else if (ctx->ctxid == IWL_RXON_CTX_BSS && - iwl_is_associated(priv, IWL_RXON_CTX_PAN) && - priv->contexts[IWL_RXON_CTX_PAN].vif && - priv->contexts[IWL_RXON_CTX_PAN].vif->bss_conf.beacon_int && - (!iwl_is_associated_ctx(ctx) || !ctx->vif || - !ctx->vif->bss_conf.beacon_int)) { - ctx->timing.beacon_interval = - priv->contexts[IWL_RXON_CTX_PAN].timing.beacon_interval; - beacon_int = le16_to_cpu(ctx->timing.beacon_interval); - } else { - beacon_int = iwl_adjust_beacon_interval(beacon_int, - IWL_MAX_UCODE_BEACON_INTERVAL * TIME_UNIT); - ctx->timing.beacon_interval = cpu_to_le16(beacon_int); - } - - ctx->beacon_int = beacon_int; - - tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ - interval_tm = beacon_int * TIME_UNIT; - rem = do_div(tsf, interval_tm); - ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); - - ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; - - IWL_DEBUG_ASSOC(priv, - "beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(ctx->timing.beacon_interval), - le32_to_cpu(ctx->timing.beacon_init_val), - le16_to_cpu(ctx->timing.atim_window)); - - return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, - CMD_SYNC, sizeof(ctx->timing), &ctx->timing); -} - -static int iwlagn_rxon_disconn(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret; - struct iwl_rxon_cmd *active = (void *)&ctx->active; - - if (ctx->ctxid == IWL_RXON_CTX_BSS) { - ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); - } else { - ret = iwlagn_disable_pan(priv, ctx, &ctx->staging); - if (ret) - return ret; - if (ctx->vif) { - ret = iwl_send_rxon_timing(priv, ctx); - if (ret) { - IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); - return ret; - } - ret = iwlagn_disconn_pan(priv, ctx, &ctx->staging); - } - } - if (ret) - return ret; - - /* - * Un-assoc RXON clears the station table and WEP - * keys, so we have to restore those afterwards. - */ - iwl_clear_ucode_stations(priv, ctx); - /* update -- might need P2P now */ - iwl_update_bcast_station(priv, ctx); - iwl_restore_stations(priv, ctx); - ret = iwl_restore_default_wep_keys(priv, ctx); - if (ret) { - IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); - return ret; - } - - memcpy(active, &ctx->staging, sizeof(*active)); - return 0; -} - -static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) -{ - int ret; - s8 prev_tx_power; - bool defer; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (priv->calib_disabled & IWL_TX_POWER_CALIB_DISABLED) - return 0; - - lockdep_assert_held(&priv->mutex); - - if (priv->tx_power_user_lmt == tx_power && !force) - return 0; - - if (tx_power < IWLAGN_TX_POWER_TARGET_POWER_MIN) { - IWL_WARN(priv, - "Requested user TXPOWER %d below lower limit %d.\n", - tx_power, - IWLAGN_TX_POWER_TARGET_POWER_MIN); - return -EINVAL; - } - - if (tx_power > priv->tx_power_device_lmt) { - IWL_WARN(priv, - "Requested user TXPOWER %d above upper limit %d.\n", - tx_power, priv->tx_power_device_lmt); - return -EINVAL; - } - - if (!iwl_is_ready_rf(priv)) - return -EIO; - - /* scan complete and commit_rxon use tx_power_next value, - * it always need to be updated for newest request */ - priv->tx_power_next = tx_power; - - /* do not set tx power when scanning or channel changing */ - defer = test_bit(STATUS_SCANNING, &priv->status) || - memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); - if (defer && !force) { - IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); - return 0; - } - - prev_tx_power = priv->tx_power_user_lmt; - priv->tx_power_user_lmt = tx_power; - - ret = iwlagn_send_tx_power(priv); - - /* if fail to set tx_power, restore the orig. tx power */ - if (ret) { - priv->tx_power_user_lmt = prev_tx_power; - priv->tx_power_next = prev_tx_power; - } - return ret; -} - -static int iwlagn_rxon_connect(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int ret; - struct iwl_rxon_cmd *active = (void *)&ctx->active; - - /* RXON timing must be before associated RXON */ - if (ctx->ctxid == IWL_RXON_CTX_BSS) { - ret = iwl_send_rxon_timing(priv, ctx); - if (ret) { - IWL_ERR(priv, "Failed to send timing (%d)!\n", ret); - return ret; - } - } - /* QoS info may be cleared by previous un-assoc RXON */ - iwlagn_update_qos(priv, ctx); - - /* - * We'll run into this code path when beaconing is - * enabled, but then we also need to send the beacon - * to the device. - */ - if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_AP)) { - ret = iwlagn_update_beacon(priv, ctx->vif); - if (ret) { - IWL_ERR(priv, - "Error sending required beacon (%d)!\n", - ret); - return ret; - } - } - - priv->start_calib = 0; - /* - * Apply the new configuration. - * - * Associated RXON doesn't clear the station table in uCode, - * so we don't need to restore stations etc. after this. - */ - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, - sizeof(struct iwl_rxon_cmd), &ctx->staging); - if (ret) { - IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); - return ret; - } - memcpy(active, &ctx->staging, sizeof(*active)); - - /* IBSS beacon needs to be sent after setting assoc */ - if (ctx->vif && (ctx->vif->type == NL80211_IFTYPE_ADHOC)) - if (iwlagn_update_beacon(priv, ctx->vif)) - IWL_ERR(priv, "Error sending IBSS beacon\n"); - iwl_init_sensitivity(priv); - - /* - * If we issue a new RXON command which required a tune then - * we must send a new TXPOWER command or we won't be able to - * Tx any frames. - * - * It's expected we set power here if channel is changing. - */ - ret = iwl_set_tx_power(priv, priv->tx_power_next, true); - if (ret) { - IWL_ERR(priv, "Error sending TX power (%d)\n", ret); - return ret; - } - - if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && - priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) - ieee80211_request_smps(ctx->vif, - priv->cfg->ht_params->smps_mode); - - return 0; -} - -int iwlagn_set_pan_params(struct iwl_priv *priv) -{ - struct iwl_wipan_params_cmd cmd; - struct iwl_rxon_context *ctx_bss, *ctx_pan; - int slot0 = 300, slot1 = 0; - int ret; - - if (priv->valid_contexts == BIT(IWL_RXON_CTX_BSS)) - return 0; - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - lockdep_assert_held(&priv->mutex); - - ctx_bss = &priv->contexts[IWL_RXON_CTX_BSS]; - ctx_pan = &priv->contexts[IWL_RXON_CTX_PAN]; - - /* - * If the PAN context is inactive, then we don't need - * to update the PAN parameters, the last thing we'll - * have done before it goes inactive is making the PAN - * parameters be WLAN-only. - */ - if (!ctx_pan->is_active) - return 0; - - memset(&cmd, 0, sizeof(cmd)); - - /* only 2 slots are currently allowed */ - cmd.num_slots = 2; - - cmd.slots[0].type = 0; /* BSS */ - cmd.slots[1].type = 1; /* PAN */ - - if (priv->hw_roc_setup) { - /* both contexts must be used for this to happen */ - slot1 = IWL_MIN_SLOT_TIME; - slot0 = 3000; - } else if (ctx_bss->vif && ctx_pan->vif) { - int bcnint = ctx_pan->beacon_int; - int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1; - - /* should be set, but seems unused?? */ - cmd.flags |= cpu_to_le16(IWL_WIPAN_PARAMS_FLG_SLOTTED_MODE); - - if (ctx_pan->vif->type == NL80211_IFTYPE_AP && - bcnint && - bcnint != ctx_bss->beacon_int) { - IWL_ERR(priv, - "beacon intervals don't match (%d, %d)\n", - ctx_bss->beacon_int, ctx_pan->beacon_int); - } else - bcnint = max_t(int, bcnint, - ctx_bss->beacon_int); - if (!bcnint) - bcnint = DEFAULT_BEACON_INTERVAL; - slot0 = bcnint / 2; - slot1 = bcnint - slot0; - - if (test_bit(STATUS_SCAN_HW, &priv->status) || - (!ctx_bss->vif->bss_conf.idle && - !ctx_bss->vif->bss_conf.assoc)) { - slot0 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; - slot1 = IWL_MIN_SLOT_TIME; - } else if (!ctx_pan->vif->bss_conf.idle && - !ctx_pan->vif->bss_conf.assoc) { - slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; - slot0 = IWL_MIN_SLOT_TIME; - } - } else if (ctx_pan->vif) { - slot0 = 0; - slot1 = max_t(int, 1, ctx_pan->vif->bss_conf.dtim_period) * - ctx_pan->beacon_int; - slot1 = max_t(int, DEFAULT_BEACON_INTERVAL, slot1); - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - slot0 = slot1 * 3 - IWL_MIN_SLOT_TIME; - slot1 = IWL_MIN_SLOT_TIME; - } - } - - cmd.slots[0].width = cpu_to_le16(slot0); - cmd.slots[1].width = cpu_to_le16(slot1); - - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); - - return ret; -} - -static void _iwl_set_rxon_ht(struct iwl_priv *priv, - struct iwl_ht_config *ht_conf, - struct iwl_rxon_context *ctx) -{ - struct iwl_rxon_cmd *rxon = &ctx->staging; - - if (!ctx->ht.enabled) { - rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | - RXON_FLG_HT40_PROT_MSK | - RXON_FLG_HT_PROT_MSK); - return; - } - - /* FIXME: if the definition of ht.protection changed, the "translation" - * will be needed for rxon->flags - */ - rxon->flags |= cpu_to_le32(ctx->ht.protection << - RXON_FLG_HT_OPERATING_MODE_POS); - - /* Set up channel bandwidth: - * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ - /* clear the HT channel mode before set the mode */ - rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) { - /* pure ht40 */ - if (ctx->ht.protection == - IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { - rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; - /* - * Note: control channel is opposite of extension - * channel - */ - switch (ctx->ht.extension_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - rxon->flags &= - ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - rxon->flags |= - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - break; - } - } else { - /* - * Note: control channel is opposite of extension - * channel - */ - switch (ctx->ht.extension_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - rxon->flags &= - ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; - rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; - break; - case IEEE80211_HT_PARAM_CHA_SEC_NONE: - default: - /* - * channel location only valid if in Mixed - * mode - */ - IWL_ERR(priv, - "invalid extension channel offset\n"); - break; - } - } - } else { - rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; - } - - iwlagn_set_rxon_chain(priv, ctx); - - IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " - "extension channel offset 0x%x\n", - le32_to_cpu(rxon->flags), ctx->ht.protection, - ctx->ht.extension_chan_offset); -} - -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) -{ - struct iwl_rxon_context *ctx; - - for_each_context(priv, ctx) - _iwl_set_rxon_ht(priv, ht_conf, ctx); -} - -/** - * iwl_set_rxon_channel - Set the band and channel values in staging RXON - * @ch: requested channel as a pointer to struct ieee80211_channel - - * NOTE: Does not commit to the hardware; it sets appropriate bit fields - * in the staging RXON flag structure based on the ch->band - */ -void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, - struct iwl_rxon_context *ctx) -{ - enum ieee80211_band band = ch->band; - u16 channel = ch->hw_value; - - if ((le16_to_cpu(ctx->staging.channel) == channel) && - (priv->band == band)) - return; - - ctx->staging.channel = cpu_to_le16(channel); - if (band == IEEE80211_BAND_5GHZ) - ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; - else - ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; - - priv->band = band; - - IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band); - -} - -void iwl_set_flags_for_band(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - enum ieee80211_band band, - struct ieee80211_vif *vif) -{ - if (band == IEEE80211_BAND_5GHZ) { - ctx->staging.flags &= - ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK - | RXON_FLG_CCK_MSK); - ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - } else { - /* Copied from iwl_post_associate() */ - if (vif && vif->bss_conf.use_short_slot) - ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; - ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; - ctx->staging.flags &= ~RXON_FLG_CCK_MSK; - } -} - -static void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, int hw_decrypt) -{ - struct iwl_rxon_cmd *rxon = &ctx->staging; - - if (hw_decrypt) - rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; - else - rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; - -} - -/* validate RXON structure is valid */ -static int iwl_check_rxon_cmd(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct iwl_rxon_cmd *rxon = &ctx->staging; - u32 errors = 0; - - if (rxon->flags & RXON_FLG_BAND_24G_MSK) { - if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { - IWL_WARN(priv, "check 2.4G: wrong narrow\n"); - errors |= BIT(0); - } - if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { - IWL_WARN(priv, "check 2.4G: wrong radar\n"); - errors |= BIT(1); - } - } else { - if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { - IWL_WARN(priv, "check 5.2G: not short slot!\n"); - errors |= BIT(2); - } - if (rxon->flags & RXON_FLG_CCK_MSK) { - IWL_WARN(priv, "check 5.2G: CCK!\n"); - errors |= BIT(3); - } - } - if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { - IWL_WARN(priv, "mac/bssid mcast!\n"); - errors |= BIT(4); - } - - /* make sure basic rates 6Mbps and 1Mbps are supported */ - if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 && - (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) { - IWL_WARN(priv, "neither 1 nor 6 are basic\n"); - errors |= BIT(5); - } - - if (le16_to_cpu(rxon->assoc_id) > 2007) { - IWL_WARN(priv, "aid > 2007\n"); - errors |= BIT(6); - } - - if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) - == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { - IWL_WARN(priv, "CCK and short slot\n"); - errors |= BIT(7); - } - - if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) - == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { - IWL_WARN(priv, "CCK and auto detect"); - errors |= BIT(8); - } - - if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | - RXON_FLG_TGG_PROTECT_MSK)) == - RXON_FLG_TGG_PROTECT_MSK) { - IWL_WARN(priv, "TGg but no auto-detect\n"); - errors |= BIT(9); - } - - if (rxon->channel == 0) { - IWL_WARN(priv, "zero channel is invalid\n"); - errors |= BIT(10); - } - - WARN(errors, "Invalid RXON (%#x), channel %d", - errors, le16_to_cpu(rxon->channel)); - - return errors ? -EINVAL : 0; -} - -/** - * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed - * @priv: staging_rxon is compared to active_rxon - * - * If the RXON structure is changing enough to require a new tune, - * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that - * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. - */ -static int iwl_full_rxon_required(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - const struct iwl_rxon_cmd *staging = &ctx->staging; - const struct iwl_rxon_cmd *active = &ctx->active; - -#define CHK(cond) \ - if ((cond)) { \ - IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ - return 1; \ - } - -#define CHK_NEQ(c1, c2) \ - if ((c1) != (c2)) { \ - IWL_DEBUG_INFO(priv, "need full RXON - " \ - #c1 " != " #c2 " - %d != %d\n", \ - (c1), (c2)); \ - return 1; \ - } - - /* These items are only settable from the full RXON command */ - CHK(!iwl_is_associated_ctx(ctx)); - CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); - CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); - CHK(!ether_addr_equal(staging->wlap_bssid_addr, - active->wlap_bssid_addr)); - CHK_NEQ(staging->dev_type, active->dev_type); - CHK_NEQ(staging->channel, active->channel); - CHK_NEQ(staging->air_propagation, active->air_propagation); - CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, - active->ofdm_ht_single_stream_basic_rates); - CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, - active->ofdm_ht_dual_stream_basic_rates); - CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates, - active->ofdm_ht_triple_stream_basic_rates); - CHK_NEQ(staging->assoc_id, active->assoc_id); - - /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can - * be updated with the RXON_ASSOC command -- however only some - * flag transitions are allowed using RXON_ASSOC */ - - /* Check if we are not switching bands */ - CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, - active->flags & RXON_FLG_BAND_24G_MSK); - - /* Check if we are switching association toggle */ - CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, - active->filter_flags & RXON_FILTER_ASSOC_MSK); - -#undef CHK -#undef CHK_NEQ - - return 0; -} - -#ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) -{ - struct iwl_rxon_context *ctx = &priv->contexts[ctxid]; - struct iwl_rxon_cmd *rxon = &ctx->staging; - - IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); - iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); - IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n", - le16_to_cpu(rxon->channel)); - IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n", - le32_to_cpu(rxon->flags)); - IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n", - le32_to_cpu(rxon->filter_flags)); - IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type); - IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n", - rxon->ofdm_basic_rates); - IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n", - rxon->cck_basic_rates); - IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr); - IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); - IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", - le16_to_cpu(rxon->assoc_id)); -} -#endif - -static void iwl_calc_basic_rates(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int lowest_present_ofdm = 100; - int lowest_present_cck = 100; - u8 cck = 0; - u8 ofdm = 0; - - if (ctx->vif) { - struct ieee80211_supported_band *sband; - unsigned long basic = ctx->vif->bss_conf.basic_rates; - int i; - - sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band]; - - for_each_set_bit(i, &basic, BITS_PER_LONG) { - int hw = sband->bitrates[i].hw_value; - if (hw >= IWL_FIRST_OFDM_RATE) { - ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); - if (lowest_present_ofdm > hw) - lowest_present_ofdm = hw; - } else { - BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); - - cck |= BIT(hw); - if (lowest_present_cck > hw) - lowest_present_cck = hw; - } - } - } - - /* - * Now we've got the basic rates as bitmaps in the ofdm and cck - * variables. This isn't sufficient though, as there might not - * be all the right rates in the bitmap. E.g. if the only basic - * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps - * and 6 Mbps because the 802.11-2007 standard says in 9.6: - * - * [...] a STA responding to a received frame shall transmit - * its Control Response frame [...] at the highest rate in the - * BSSBasicRateSet parameter that is less than or equal to the - * rate of the immediately previous frame in the frame exchange - * sequence ([...]) and that is of the same modulation class - * ([...]) as the received frame. If no rate contained in the - * BSSBasicRateSet parameter meets these conditions, then the - * control frame sent in response to a received frame shall be - * transmitted at the highest mandatory rate of the PHY that is - * less than or equal to the rate of the received frame, and - * that is of the same modulation class as the received frame. - * - * As a consequence, we need to add all mandatory rates that are - * lower than all of the basic rates to these bitmaps. - */ - - if (IWL_RATE_24M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_24M_MASK >> IWL_FIRST_OFDM_RATE; - if (IWL_RATE_12M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_12M_MASK >> IWL_FIRST_OFDM_RATE; - /* 6M already there or needed so always add */ - ofdm |= IWL_RATE_6M_MASK >> IWL_FIRST_OFDM_RATE; - - /* - * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. - * Note, however: - * - if no CCK rates are basic, it must be ERP since there must - * be some basic rates at all, so they're OFDM => ERP PHY - * (or we're in 5 GHz, and the cck bitmap will never be used) - * - if 11M is a basic rate, it must be ERP as well, so add 5.5M - * - if 5.5M is basic, 1M and 2M are mandatory - * - if 2M is basic, 1M is mandatory - * - if 1M is basic, that's the only valid ACK rate. - * As a consequence, it's not as complicated as it sounds, just add - * any lower rates to the ACK rate bitmap. - */ - if (IWL_RATE_11M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_11M_MASK >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_5M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_5M_MASK >> IWL_FIRST_CCK_RATE; - if (IWL_RATE_2M_INDEX < lowest_present_ofdm) - ofdm |= IWL_RATE_2M_MASK >> IWL_FIRST_CCK_RATE; - /* 1M already there or needed so always add */ - cck |= IWL_RATE_1M_MASK >> IWL_FIRST_CCK_RATE; - - IWL_DEBUG_RATE(priv, "Set basic rates cck:0x%.2x ofdm:0x%.2x\n", - cck, ofdm); - - /* "basic_rates" is a misnomer here -- should be called ACK rates */ - ctx->staging.cck_basic_rates = cck; - ctx->staging.ofdm_basic_rates = ofdm; -} - -/** - * iwlagn_commit_rxon - commit staging_rxon to hardware - * - * The RXON command in staging_rxon is committed to the hardware and - * the active_rxon structure is updated with the new data. This - * function correctly transitions out of the RXON_ASSOC_MSK state if - * a HW tune is required based on the RXON structure changes. - * - * The connect/disconnect flow should be as the following: - * - * 1. make sure send RXON command with association bit unset if not connect - * this should include the channel and the band for the candidate - * to be connected to - * 2. Add Station before RXON association with the AP - * 3. RXON_timing has to send before RXON for connection - * 4. full RXON command - associated bit set - * 5. use RXON_ASSOC command to update any flags changes - */ -int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - /* cast away the const for active_rxon in this function */ - struct iwl_rxon_cmd *active = (void *)&ctx->active; - bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); - int ret; - - lockdep_assert_held(&priv->mutex); - - if (!iwl_is_alive(priv)) - return -EBUSY; - - /* This function hardcodes a bunch of dual-mode assumptions */ - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - if (!ctx->is_active) - return 0; - - /* always get timestamp with Rx frame */ - ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; - - /* recalculate basic rates */ - iwl_calc_basic_rates(priv, ctx); - - /* - * force CTS-to-self frames protection if RTS-CTS is not preferred - * one aggregation protection method - */ - if (!priv->hw_params.use_rts_for_aggregation) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - - if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || - !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) - ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - iwl_print_rx_config_cmd(priv, ctx->ctxid); - ret = iwl_check_rxon_cmd(priv, ctx); - if (ret) { - IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); - return -EINVAL; - } - - /* - * receive commit_rxon request - * abort any previous channel switch if still in process - */ - if (test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status) && - (priv->switch_channel != ctx->staging.channel)) { - IWL_DEBUG_11H(priv, "abort channel switch on %d\n", - le16_to_cpu(priv->switch_channel)); - iwl_chswitch_done(priv, false); - } - - /* - * If we don't need to send a full RXON, we can use - * iwl_rxon_assoc_cmd which is used to reconfigure filter - * and other flags for the current radio configuration. - */ - if (!iwl_full_rxon_required(priv, ctx)) { - ret = iwlagn_send_rxon_assoc(priv, ctx); - if (ret) { - IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret); - return ret; - } - - memcpy(active, &ctx->staging, sizeof(*active)); - /* - * We do not commit tx power settings while channel changing, - * do it now if after settings changed. - */ - iwl_set_tx_power(priv, priv->tx_power_next, false); - - /* make sure we are in the right PS state */ - iwl_power_update_mode(priv, true); - - return 0; - } - - iwl_set_rxon_hwcrypto(priv, ctx, !iwlwifi_mod_params.sw_crypto); - - IWL_DEBUG_INFO(priv, - "Going to commit RXON\n" - " * with%s RXON_FILTER_ASSOC_MSK\n" - " * channel = %d\n" - " * bssid = %pM\n", - (new_assoc ? "" : "out"), - le16_to_cpu(ctx->staging.channel), - ctx->staging.bssid_addr); - - /* - * Always clear associated first, but with the correct config. - * This is required as for example station addition for the - * AP station must be done after the BSSID is set to correctly - * set up filters in the device. - */ - ret = iwlagn_rxon_disconn(priv, ctx); - if (ret) - return ret; - - ret = iwlagn_set_pan_params(priv); - if (ret) - return ret; - - if (new_assoc) - return iwlagn_rxon_connect(priv, ctx); - - return 0; -} - -void iwlagn_config_ht40(struct ieee80211_conf *conf, - struct iwl_rxon_context *ctx) -{ - if (conf_is_ht40_minus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ctx->ht.is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ctx->ht.is_40mhz = true; - } else { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - ctx->ht.is_40mhz = false; - } -} - -int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = conf->channel; - int ret = 0; - - IWL_DEBUG_MAC80211(priv, "enter: changed %#x\n", changed); - - mutex_lock(&priv->mutex); - - if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { - IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); - goto out; - } - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - goto out; - } - - if (changed & (IEEE80211_CONF_CHANGE_SMPS | - IEEE80211_CONF_CHANGE_CHANNEL)) { - /* mac80211 uses static for non-HT which is what we want */ - priv->current_ht_config.smps = conf->smps_mode; - - /* - * Recalculate chain counts. - * - * If monitor mode is enabled then mac80211 will - * set up the SM PS mode to OFF if an HT channel is - * configured. - */ - for_each_context(priv, ctx) - iwlagn_set_rxon_chain(priv, ctx); - } - - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - for_each_context(priv, ctx) { - /* Configure HT40 channels */ - if (ctx->ht.enabled != conf_is_ht(conf)) - ctx->ht.enabled = conf_is_ht(conf); - - if (ctx->ht.enabled) { - /* if HT40 is used, it should not change - * after associated except channel switch */ - if (!ctx->ht.is_40mhz || - !iwl_is_associated_ctx(ctx)) - iwlagn_config_ht40(conf, ctx); - } else - ctx->ht.is_40mhz = false; - - /* - * Default to no protection. Protection mode will - * later be set from BSS config in iwl_ht_conf - */ - ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; - - /* if we are switching from ht to 2.4 clear flags - * from any ht related info since 2.4 does not - * support ht */ - if (le16_to_cpu(ctx->staging.channel) != - channel->hw_value) - ctx->staging.flags = 0; - - iwl_set_rxon_channel(priv, channel, ctx); - iwl_set_rxon_ht(priv, &priv->current_ht_config); - - iwl_set_flags_for_band(priv, ctx, channel->band, - ctx->vif); - } - - iwl_update_bcast_stations(priv); - } - - if (changed & (IEEE80211_CONF_CHANGE_PS | - IEEE80211_CONF_CHANGE_IDLE)) { - ret = iwl_power_update_mode(priv, false); - if (ret) - IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); - } - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", - priv->tx_power_user_lmt, conf->power_level); - - iwl_set_tx_power(priv, conf->power_level, false); - } - - for_each_context(priv, ctx) { - if (!memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - continue; - iwlagn_commit_rxon(priv, ctx); - } - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static void iwlagn_check_needed_chains(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_bss_conf *bss_conf) -{ - struct ieee80211_vif *vif = ctx->vif; - struct iwl_rxon_context *tmp; - struct ieee80211_sta *sta; - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - struct ieee80211_sta_ht_cap *ht_cap; - bool need_multiple; - - lockdep_assert_held(&priv->mutex); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - rcu_read_lock(); - sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (!sta) { - /* - * If at all, this can only happen through a race - * when the AP disconnects us while we're still - * setting up the connection, in that case mac80211 - * will soon tell us about that. - */ - need_multiple = false; - rcu_read_unlock(); - break; - } - - ht_cap = &sta->ht_cap; - - need_multiple = true; - - /* - * If the peer advertises no support for receiving 2 and 3 - * stream MCS rates, it can't be transmitting them either. - */ - if (ht_cap->mcs.rx_mask[1] == 0 && - ht_cap->mcs.rx_mask[2] == 0) { - need_multiple = false; - } else if (!(ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_DEFINED)) { - /* If it can't TX MCS at all ... */ - need_multiple = false; - } else if (ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_RX_DIFF) { - int maxstreams; - - /* - * But if it can receive them, it might still not - * be able to transmit them, which is what we need - * to check here -- so check the number of streams - * it advertises for TX (if different from RX). - */ - - maxstreams = (ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK); - maxstreams >>= - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; - maxstreams += 1; - - if (maxstreams <= 1) - need_multiple = false; - } - - rcu_read_unlock(); - break; - case NL80211_IFTYPE_ADHOC: - /* currently */ - need_multiple = false; - break; - default: - /* only AP really */ - need_multiple = true; - break; - } - - ctx->ht_need_multiple_chains = need_multiple; - - if (!need_multiple) { - /* check all contexts */ - for_each_context(priv, tmp) { - if (!tmp->vif) - continue; - if (tmp->ht_need_multiple_chains) { - need_multiple = true; - break; - } - } - } - - ht_conf->single_chain_sufficient = !need_multiple; -} - -static void iwlagn_chain_noise_reset(struct iwl_priv *priv) -{ - struct iwl_chain_noise_data *data = &priv->chain_noise_data; - int ret; - - if (!(priv->calib_disabled & IWL_CHAIN_NOISE_CALIB_DISABLED)) - return; - - if ((data->state == IWL_CHAIN_NOISE_ALIVE) && - iwl_is_any_associated(priv)) { - struct iwl_calib_chain_noise_reset_cmd cmd; - - /* clear data for chain noise calibration algorithm */ - data->chain_noise_a = 0; - data->chain_noise_b = 0; - data->chain_noise_c = 0; - data->chain_signal_a = 0; - data->chain_signal_b = 0; - data->chain_signal_c = 0; - data->beacon_count = 0; - - memset(&cmd, 0, sizeof(cmd)); - iwl_set_calib_hdr(&cmd.hdr, - priv->phy_calib_chain_noise_reset_cmd); - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_PHY_CALIBRATION_CMD, - CMD_SYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, - "Could not send REPLY_PHY_CALIBRATION_CMD\n"); - data->state = IWL_CHAIN_NOISE_ACCUMULATE; - IWL_DEBUG_CALIB(priv, "Run chain_noise_calibrate\n"); - } -} - -void iwlagn_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - int ret; - bool force = false; - - mutex_lock(&priv->mutex); - - if (unlikely(!iwl_is_ready(priv))) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - mutex_unlock(&priv->mutex); - return; - } - - if (unlikely(!ctx->vif)) { - IWL_DEBUG_MAC80211(priv, "leave - vif is NULL\n"); - mutex_unlock(&priv->mutex); - return; - } - - if (changes & BSS_CHANGED_BEACON_INT) - force = true; - - if (changes & BSS_CHANGED_QOS) { - ctx->qos_data.qos_active = bss_conf->qos; - iwlagn_update_qos(priv, ctx); - } - - ctx->staging.assoc_id = cpu_to_le16(vif->bss_conf.aid); - if (vif->bss_conf.use_short_preamble) - ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (changes & BSS_CHANGED_ASSOC) { - if (bss_conf->assoc) { - priv->timestamp = bss_conf->last_tsf; - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - } else { - /* - * If we disassociate while there are pending - * frames, just wake up the queues and let the - * frames "escape" ... This shouldn't really - * be happening to start with, but we should - * not get stuck in this case either since it - * can happen if userspace gets confused. - */ - iwlagn_lift_passive_no_rx(priv); - - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - if (ctx->ctxid == IWL_RXON_CTX_BSS) - priv->have_rekey_data = false; - } - - iwlagn_bt_coex_rssi_monitor(priv); - } - - if (ctx->ht.enabled) { - ctx->ht.protection = bss_conf->ht_operation_mode & - IEEE80211_HT_OP_MODE_PROTECTION; - ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode & - IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - iwlagn_check_needed_chains(priv, ctx, bss_conf); - iwl_set_rxon_ht(priv, &priv->current_ht_config); - } - - iwlagn_set_rxon_chain(priv, ctx); - - if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) - ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - - if (bss_conf->use_cts_prot) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - - memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); - - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { - if (vif->bss_conf.enable_beacon) { - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - priv->beacon_ctx = ctx; - } else { - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - priv->beacon_ctx = NULL; - } - } - - /* - * If the ucode decides to do beacon filtering before - * association, it will lose beacons that are needed - * before sending frames out on passive channels. This - * causes association failures on those channels. Enable - * receiving beacons in such cases. - */ - - if (vif->type == NL80211_IFTYPE_STATION) { - if (!bss_conf->assoc) - ctx->staging.filter_flags |= RXON_FILTER_BCON_AWARE_MSK; - else - ctx->staging.filter_flags &= - ~RXON_FILTER_BCON_AWARE_MSK; - } - - if (force || memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - iwlagn_commit_rxon(priv, ctx); - - if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) { - /* - * The chain noise calibration will enable PM upon - * completion. If calibration has already been run - * then we need to enable power management here. - */ - if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) - iwl_power_update_mode(priv, false); - - /* Enable RX differential gain and sensitivity calibrations */ - iwlagn_chain_noise_reset(priv); - priv->start_calib = 1; - } - - if (changes & BSS_CHANGED_IBSS) { - ret = iwlagn_manage_ibss_station(priv, vif, - bss_conf->ibss_joined); - if (ret) - IWL_ERR(priv, "failed to %s IBSS station %pM\n", - bss_conf->ibss_joined ? "add" : "remove", - bss_conf->bssid); - } - - if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_ADHOC && - priv->beacon_ctx) { - if (iwlagn_update_beacon(priv, vif)) - IWL_ERR(priv, "Error sending IBSS beacon\n"); - } - - mutex_unlock(&priv->mutex); -} - -void iwlagn_post_scan(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - - /* - * We do not commit power settings while scan is pending, - * do it now if the settings changed. - */ - iwl_power_set_mode(priv, &priv->power_data.sleep_cmd_next, false); - iwl_set_tx_power(priv, priv->tx_power_next, false); - - /* - * Since setting the RXON may have been deferred while - * performing the scan, fire one off if needed - */ - for_each_context(priv, ctx) - if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - iwlagn_commit_rxon(priv, ctx); - - iwlagn_set_pan_params(priv); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c deleted file mode 100644 index 01fbf06980c8..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c +++ /dev/null @@ -1,1480 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include - -#include "iwl-dev.h" -#include "iwl-agn.h" -#include "iwl-trans.h" - -const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - -static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) -{ - lockdep_assert_held(&priv->sta_lock); - - if (sta_id >= IWLAGN_STATION_COUNT) { - IWL_ERR(priv, "invalid sta_id %u", sta_id); - return -EINVAL; - } - if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) - IWL_ERR(priv, "ACTIVATE a non DRIVER active station id %u " - "addr %pM\n", - sta_id, priv->stations[sta_id].sta.sta.addr); - - if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { - IWL_DEBUG_ASSOC(priv, - "STA id %u addr %pM already present in uCode " - "(according to driver)\n", - sta_id, priv->stations[sta_id].sta.sta.addr); - } else { - priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; - IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", - sta_id, priv->stations[sta_id].sta.sta.addr); - } - return 0; -} - -static int iwl_process_add_sta_resp(struct iwl_priv *priv, - struct iwl_addsta_cmd *addsta, - struct iwl_rx_packet *pkt) -{ - struct iwl_add_sta_resp *add_sta_resp = (void *)pkt->data; - u8 sta_id = addsta->sta.sta_id; - int ret = -EIO; - - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", - pkt->hdr.flags); - return ret; - } - - IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n", - sta_id); - - spin_lock(&priv->sta_lock); - - switch (add_sta_resp->status) { - case ADD_STA_SUCCESS_MSK: - IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); - ret = iwl_sta_ucode_activate(priv, sta_id); - break; - case ADD_STA_NO_ROOM_IN_TABLE: - IWL_ERR(priv, "Adding station %d failed, no room in table.\n", - sta_id); - break; - case ADD_STA_NO_BLOCK_ACK_RESOURCE: - IWL_ERR(priv, "Adding station %d failed, no block ack " - "resource.\n", sta_id); - break; - case ADD_STA_MODIFY_NON_EXIST_STA: - IWL_ERR(priv, "Attempting to modify non-existing station %d\n", - sta_id); - break; - default: - IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", - add_sta_resp->status); - break; - } - - IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", - priv->stations[sta_id].sta.mode == - STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", - sta_id, priv->stations[sta_id].sta.sta.addr); - - /* - * XXX: The MAC address in the command buffer is often changed from - * the original sent to the device. That is, the MAC address - * written to the command buffer often is not the same MAC address - * read from the command buffer when the command returns. This - * issue has not yet been resolved and this debugging is left to - * observe the problem. - */ - IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", - priv->stations[sta_id].sta.mode == - STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", - addsta->sta.addr); - spin_unlock(&priv->sta_lock); - - return ret; -} - -int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_addsta_cmd *addsta = - (struct iwl_addsta_cmd *) cmd->payload; - - return iwl_process_add_sta_resp(priv, addsta, pkt); -} - -int iwl_send_add_sta(struct iwl_priv *priv, - struct iwl_addsta_cmd *sta, u8 flags) -{ - int ret = 0; - struct iwl_host_cmd cmd = { - .id = REPLY_ADD_STA, - .flags = flags, - .data = { sta, }, - .len = { sizeof(*sta), }, - }; - u8 sta_id __maybe_unused = sta->sta.sta_id; - - IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", - sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); - - if (!(flags & CMD_ASYNC)) { - cmd.flags |= CMD_WANT_SKB; - might_sleep(); - } - - ret = iwl_dvm_send_cmd(priv, &cmd); - - if (ret || (flags & CMD_ASYNC)) - return ret; - /*else the command was successfully sent in SYNC mode, need to free - * the reply page */ - - iwl_free_resp(&cmd); - - if (cmd.handler_status) - IWL_ERR(priv, "%s - error in the CMD response %d", __func__, - cmd.handler_status); - - return cmd.handler_status; -} - -bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_sta_ht_cap *ht_cap) -{ - if (!ctx->ht.enabled || !ctx->ht.is_40mhz) - return false; - - /* - * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 - * the bit will not set if it is pure 40MHz case - */ - if (ht_cap && !ht_cap->ht_supported) - return false; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (priv->disable_ht40) - return false; -#endif - - return true; -} - -static void iwl_sta_calc_ht_flags(struct iwl_priv *priv, - struct ieee80211_sta *sta, - struct iwl_rxon_context *ctx, - __le32 *flags, __le32 *mask) -{ - struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; - u8 mimo_ps_mode; - - *mask = STA_FLG_RTS_MIMO_PROT_MSK | - STA_FLG_MIMO_DIS_MSK | - STA_FLG_HT40_EN_MSK | - STA_FLG_MAX_AGG_SIZE_MSK | - STA_FLG_AGG_MPDU_DENSITY_MSK; - *flags = 0; - - if (!sta || !sta_ht_inf->ht_supported) - return; - - mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; - - IWL_DEBUG_INFO(priv, "STA %pM SM PS mode: %s\n", - sta->addr, - (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? - "static" : - (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? - "dynamic" : "disabled"); - - switch (mimo_ps_mode) { - case WLAN_HT_CAP_SM_PS_STATIC: - *flags |= STA_FLG_MIMO_DIS_MSK; - break; - case WLAN_HT_CAP_SM_PS_DYNAMIC: - *flags |= STA_FLG_RTS_MIMO_PROT_MSK; - break; - case WLAN_HT_CAP_SM_PS_DISABLED: - break; - default: - IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); - break; - } - - *flags |= cpu_to_le32( - (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); - - *flags |= cpu_to_le32( - (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - - if (iwl_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) - *flags |= STA_FLG_HT40_EN_MSK; -} - -int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_sta *sta) -{ - u8 sta_id = iwl_sta_id(sta); - __le32 flags, mask; - struct iwl_addsta_cmd cmd; - - if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) - return -EINVAL; - - iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.station_flags &= ~mask; - priv->stations[sta_id].sta.station_flags |= flags; - spin_unlock_bh(&priv->sta_lock); - - memset(&cmd, 0, sizeof(cmd)); - cmd.mode = STA_CONTROL_MODIFY_MSK; - cmd.station_flags_msk = mask; - cmd.station_flags = flags; - cmd.sta.sta_id = sta_id; - - return iwl_send_add_sta(priv, &cmd, CMD_SYNC); -} - -static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, - struct ieee80211_sta *sta, - struct iwl_rxon_context *ctx) -{ - __le32 flags, mask; - - iwl_sta_calc_ht_flags(priv, sta, ctx, &flags, &mask); - - lockdep_assert_held(&priv->sta_lock); - priv->stations[index].sta.station_flags &= ~mask; - priv->stations[index].sta.station_flags |= flags; -} - -/** - * iwl_prep_station - Prepare station information for addition - * - * should be called with sta_lock held - */ -u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, struct ieee80211_sta *sta) -{ - struct iwl_station_entry *station; - int i; - u8 sta_id = IWL_INVALID_STATION; - - if (is_ap) - sta_id = ctx->ap_sta_id; - else if (is_broadcast_ether_addr(addr)) - sta_id = ctx->bcast_sta_id; - else - for (i = IWL_STA_ID; i < IWLAGN_STATION_COUNT; i++) { - if (ether_addr_equal(priv->stations[i].sta.sta.addr, - addr)) { - sta_id = i; - break; - } - - if (!priv->stations[i].used && - sta_id == IWL_INVALID_STATION) - sta_id = i; - } - - /* - * These two conditions have the same outcome, but keep them - * separate - */ - if (unlikely(sta_id == IWL_INVALID_STATION)) - return sta_id; - - /* - * uCode is not able to deal with multiple requests to add a - * station. Keep track if one is in progress so that we do not send - * another. - */ - if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { - IWL_DEBUG_INFO(priv, "STA %d already in process of being " - "added.\n", sta_id); - return sta_id; - } - - if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && - (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && - ether_addr_equal(priv->stations[sta_id].sta.sta.addr, addr)) { - IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " - "adding again.\n", sta_id, addr); - return sta_id; - } - - station = &priv->stations[sta_id]; - station->used = IWL_STA_DRIVER_ACTIVE; - IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", - sta_id, addr); - priv->num_stations++; - - /* Set up the REPLY_ADD_STA command to send to device */ - memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); - memcpy(station->sta.sta.addr, addr, ETH_ALEN); - station->sta.mode = 0; - station->sta.sta.sta_id = sta_id; - station->sta.station_flags = ctx->station_flags; - station->ctxid = ctx->ctxid; - - if (sta) { - struct iwl_station_priv *sta_priv; - - sta_priv = (void *)sta->drv_priv; - sta_priv->ctx = ctx; - } - - /* - * OK to call unconditionally, since local stations (IBSS BSSID - * STA and broadcast STA) pass in a NULL sta, and mac80211 - * doesn't allow HT IBSS. - */ - iwl_set_ht_add_station(priv, sta_id, sta, ctx); - - return sta_id; - -} - -#define STA_WAIT_TIMEOUT (HZ/2) - -/** - * iwl_add_station_common - - */ -int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, - struct ieee80211_sta *sta, u8 *sta_id_r) -{ - int ret = 0; - u8 sta_id; - struct iwl_addsta_cmd sta_cmd; - - *sta_id_r = 0; - spin_lock_bh(&priv->sta_lock); - sta_id = iwl_prep_station(priv, ctx, addr, is_ap, sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Unable to prepare station %pM for addition\n", - addr); - spin_unlock_bh(&priv->sta_lock); - return -EINVAL; - } - - /* - * uCode is not able to deal with multiple requests to add a - * station. Keep track if one is in progress so that we do not send - * another. - */ - if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { - IWL_DEBUG_INFO(priv, "STA %d already in process of being " - "added.\n", sta_id); - spin_unlock_bh(&priv->sta_lock); - return -EEXIST; - } - - if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && - (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { - IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not " - "adding again.\n", sta_id, addr); - spin_unlock_bh(&priv->sta_lock); - return -EEXIST; - } - - priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, - sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - /* Add station to device's station table */ - ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); - if (ret) { - spin_lock_bh(&priv->sta_lock); - IWL_ERR(priv, "Adding station %pM failed.\n", - priv->stations[sta_id].sta.sta.addr); - priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; - priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - spin_unlock_bh(&priv->sta_lock); - } - *sta_id_r = sta_id; - return ret; -} - -/** - * iwl_sta_ucode_deactivate - deactivate ucode status for a station - */ -static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) -{ - lockdep_assert_held(&priv->sta_lock); - - /* Ucode must be active and driver must be non active */ - if ((priv->stations[sta_id].used & - (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != - IWL_STA_UCODE_ACTIVE) - IWL_ERR(priv, "removed non active STA %u\n", sta_id); - - priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; - - memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); - IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); -} - -static int iwl_send_remove_station(struct iwl_priv *priv, - const u8 *addr, int sta_id, - bool temporary) -{ - struct iwl_rx_packet *pkt; - int ret; - struct iwl_rem_sta_cmd rm_sta_cmd; - - struct iwl_host_cmd cmd = { - .id = REPLY_REMOVE_STA, - .len = { sizeof(struct iwl_rem_sta_cmd), }, - .flags = CMD_SYNC, - .data = { &rm_sta_cmd, }, - }; - - memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); - rm_sta_cmd.num_sta = 1; - memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); - - cmd.flags |= CMD_WANT_SKB; - - ret = iwl_dvm_send_cmd(priv, &cmd); - - if (ret) - return ret; - - pkt = cmd.resp_pkt; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", - pkt->hdr.flags); - ret = -EIO; - } - - if (!ret) { - struct iwl_rem_sta_resp *rem_sta_resp = (void *)pkt->data; - switch (rem_sta_resp->status) { - case REM_STA_SUCCESS_MSK: - if (!temporary) { - spin_lock_bh(&priv->sta_lock); - iwl_sta_ucode_deactivate(priv, sta_id); - spin_unlock_bh(&priv->sta_lock); - } - IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); - break; - default: - ret = -EIO; - IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); - break; - } - } - iwl_free_resp(&cmd); - - return ret; -} - -/** - * iwl_remove_station - Remove driver's knowledge of station. - */ -int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr) -{ - u8 tid; - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_INFO(priv, - "Unable to remove station %pM, device not ready.\n", - addr); - /* - * It is typical for stations to be removed when we are - * going down. Return success since device will be down - * soon anyway - */ - return 0; - } - - IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", - sta_id, addr); - - if (WARN_ON(sta_id == IWL_INVALID_STATION)) - return -EINVAL; - - spin_lock_bh(&priv->sta_lock); - - if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { - IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", - addr); - goto out_err; - } - - if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { - IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", - addr); - goto out_err; - } - - if (priv->stations[sta_id].used & IWL_STA_LOCAL) { - kfree(priv->stations[sta_id].lq); - priv->stations[sta_id].lq = NULL; - } - - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) - memset(&priv->tid_data[sta_id][tid], 0, - sizeof(priv->tid_data[sta_id][tid])); - - priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; - - priv->num_stations--; - - if (WARN_ON(priv->num_stations < 0)) - priv->num_stations = 0; - - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_remove_station(priv, addr, sta_id, false); -out_err: - spin_unlock_bh(&priv->sta_lock); - return -EINVAL; -} - -void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr) -{ - u8 tid; - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_INFO(priv, - "Unable to remove station %pM, device not ready.\n", - addr); - return; - } - - IWL_DEBUG_ASSOC(priv, "Deactivating STA: %pM (%d)\n", addr, sta_id); - - if (WARN_ON_ONCE(sta_id == IWL_INVALID_STATION)) - return; - - spin_lock_bh(&priv->sta_lock); - - WARN_ON_ONCE(!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)); - - for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) - memset(&priv->tid_data[sta_id][tid], 0, - sizeof(priv->tid_data[sta_id][tid])); - - priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; - - priv->num_stations--; - - if (WARN_ON_ONCE(priv->num_stations < 0)) - priv->num_stations = 0; - - spin_unlock_bh(&priv->sta_lock); -} - -static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - u8 sta_id, struct iwl_link_quality_cmd *link_cmd) -{ - int i, r; - u32 rate_flags = 0; - __le32 rate_n_flags; - - lockdep_assert_held(&priv->mutex); - - memset(link_cmd, 0, sizeof(*link_cmd)); - - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (priv->band == IEEE80211_BAND_5GHZ) - r = IWL_RATE_6M_INDEX; - else if (ctx && ctx->vif && ctx->vif->p2p) - r = IWL_RATE_6M_INDEX; - else - r = IWL_RATE_1M_INDEX; - - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << - RATE_MCS_ANT_POS; - rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - link_cmd->rs_table[i].rate_n_flags = rate_n_flags; - - link_cmd->general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); - - link_cmd->general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant & - ~first_antenna(priv->hw_params.valid_tx_ant); - if (!link_cmd->general_params.dual_stream_ant_msk) { - link_cmd->general_params.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { - link_cmd->general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant; - } - - link_cmd->agg_params.agg_dis_start_th = - LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd->agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - link_cmd->sta_id = sta_id; -} - -/** - * iwl_clear_ucode_stations - clear ucode station table bits - * - * This function clears all the bits in the driver indicating - * which stations are active in the ucode. Call when something - * other than explicit station management would cause this in - * the ucode, e.g. unassociated RXON. - */ -void iwl_clear_ucode_stations(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - int i; - bool cleared = false; - - IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); - - spin_lock_bh(&priv->sta_lock); - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if (ctx && ctx->ctxid != priv->stations[i].ctxid) - continue; - - if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { - IWL_DEBUG_INFO(priv, - "Clearing ucode active for station %d\n", i); - priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; - cleared = true; - } - } - spin_unlock_bh(&priv->sta_lock); - - if (!cleared) - IWL_DEBUG_INFO(priv, - "No active stations found to be cleared\n"); -} - -/** - * iwl_restore_stations() - Restore driver known stations to device - * - * All stations considered active by driver, but not present in ucode, is - * restored. - * - * Function sleeps. - */ -void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - struct iwl_addsta_cmd sta_cmd; - struct iwl_link_quality_cmd lq; - int i; - bool found = false; - int ret; - bool send_lq; - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_INFO(priv, - "Not ready yet, not restoring any stations.\n"); - return; - } - - IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); - spin_lock_bh(&priv->sta_lock); - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if (ctx->ctxid != priv->stations[i].ctxid) - continue; - if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && - !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { - IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", - priv->stations[i].sta.sta.addr); - priv->stations[i].sta.mode = 0; - priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; - found = true; - } - } - - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { - memcpy(&sta_cmd, &priv->stations[i].sta, - sizeof(struct iwl_addsta_cmd)); - send_lq = false; - if (priv->stations[i].lq) { - if (priv->wowlan) - iwl_sta_fill_lq(priv, ctx, i, &lq); - else - memcpy(&lq, priv->stations[i].lq, - sizeof(struct iwl_link_quality_cmd)); - send_lq = true; - } - spin_unlock_bh(&priv->sta_lock); - ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); - if (ret) { - spin_lock_bh(&priv->sta_lock); - IWL_ERR(priv, "Adding station %pM failed.\n", - priv->stations[i].sta.sta.addr); - priv->stations[i].used &= - ~IWL_STA_DRIVER_ACTIVE; - priv->stations[i].used &= - ~IWL_STA_UCODE_INPROGRESS; - continue; - } - /* - * Rate scaling has already been initialized, send - * current LQ command - */ - if (send_lq) - iwl_send_lq_cmd(priv, ctx, &lq, - CMD_SYNC, true); - spin_lock_bh(&priv->sta_lock); - priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; - } - } - - spin_unlock_bh(&priv->sta_lock); - if (!found) - IWL_DEBUG_INFO(priv, "Restoring all known stations .... " - "no stations to be restored.\n"); - else - IWL_DEBUG_INFO(priv, "Restoring all known stations .... " - "complete.\n"); -} - -int iwl_get_free_ucode_key_offset(struct iwl_priv *priv) -{ - int i; - - for (i = 0; i < priv->sta_key_max_num; i++) - if (!test_and_set_bit(i, &priv->ucode_key_table)) - return i; - - return WEP_INVALID_OFFSET; -} - -void iwl_dealloc_bcast_stations(struct iwl_priv *priv) -{ - int i; - - spin_lock_bh(&priv->sta_lock); - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - if (!(priv->stations[i].used & IWL_STA_BCAST)) - continue; - - priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; - priv->num_stations--; - if (WARN_ON(priv->num_stations < 0)) - priv->num_stations = 0; - kfree(priv->stations[i].lq); - priv->stations[i].lq = NULL; - } - spin_unlock_bh(&priv->sta_lock); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -static void iwl_dump_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq) -{ - int i; - IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); - IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", - lq->general_params.single_stream_ant_msk, - lq->general_params.dual_stream_ant_msk); - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", - i, lq->rs_table[i].rate_n_flags); -} -#else -static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq) -{ -} -#endif - -/** - * is_lq_table_valid() - Test one aspect of LQ cmd for validity - * - * It sometimes happens when a HT rate has been in use and we - * loose connectivity with AP then mac80211 will first tell us that the - * current channel is not HT anymore before removing the station. In such a - * scenario the RXON flags will be updated to indicate we are not - * communicating HT anymore, but the LQ command may still contain HT rates. - * Test for this to prevent driver from sending LQ command between the time - * RXON flags are updated and when LQ command is updated. - */ -static bool is_lq_table_valid(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct iwl_link_quality_cmd *lq) -{ - int i; - - if (ctx->ht.enabled) - return true; - - IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", - ctx->active.channel); - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & - RATE_MCS_HT_MSK) { - IWL_DEBUG_INFO(priv, - "index %d of LQ expects HT channel\n", - i); - return false; - } - } - return true; -} - -/** - * iwl_send_lq_cmd() - Send link quality command - * @init: This command is sent as part of station initialization right - * after station has been added. - * - * The link quality command is sent as the last step of station creation. - * This is the special case in which init is set and we call a callback in - * this case to clear the state indicating that station creation is in - * progress. - */ -int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct iwl_link_quality_cmd *lq, u8 flags, bool init) -{ - int ret = 0; - struct iwl_host_cmd cmd = { - .id = REPLY_TX_LINK_QUALITY_CMD, - .len = { sizeof(struct iwl_link_quality_cmd), }, - .flags = flags, - .data = { lq, }, - }; - - if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) - return -EINVAL; - - - spin_lock_bh(&priv->sta_lock); - if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { - spin_unlock_bh(&priv->sta_lock); - return -EINVAL; - } - spin_unlock_bh(&priv->sta_lock); - - iwl_dump_lq_cmd(priv, lq); - if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) - return -EINVAL; - - if (is_lq_table_valid(priv, ctx, lq)) - ret = iwl_dvm_send_cmd(priv, &cmd); - else - ret = -EINVAL; - - if (cmd.flags & CMD_ASYNC) - return ret; - - if (init) { - IWL_DEBUG_INFO(priv, "init LQ command complete, " - "clearing sta addition status for sta %d\n", - lq->sta_id); - spin_lock_bh(&priv->sta_lock); - priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - spin_unlock_bh(&priv->sta_lock); - } - return ret; -} - - -static struct iwl_link_quality_cmd * -iwl_sta_alloc_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - u8 sta_id) -{ - struct iwl_link_quality_cmd *link_cmd; - - link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); - if (!link_cmd) { - IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); - return NULL; - } - - iwl_sta_fill_lq(priv, ctx, sta_id, link_cmd); - - return link_cmd; -} - -/* - * iwlagn_add_bssid_station - Add the special IBSS BSSID station - * - * Function sleeps. - */ -int iwlagn_add_bssid_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - const u8 *addr, u8 *sta_id_r) -{ - int ret; - u8 sta_id; - struct iwl_link_quality_cmd *link_cmd; - - if (sta_id_r) - *sta_id_r = IWL_INVALID_STATION; - - ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); - if (ret) { - IWL_ERR(priv, "Unable to add station %pM\n", addr); - return ret; - } - - if (sta_id_r) - *sta_id_r = sta_id; - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].used |= IWL_STA_LOCAL; - spin_unlock_bh(&priv->sta_lock); - - /* Set up default rate scaling table in device's station table */ - link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); - if (!link_cmd) { - IWL_ERR(priv, - "Unable to initialize rate scaling for station %pM.\n", - addr); - return -ENOMEM; - } - - ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true); - if (ret) - IWL_ERR(priv, "Link quality command failed (%d)\n", ret); - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].lq = link_cmd; - spin_unlock_bh(&priv->sta_lock); - - return 0; -} - -/* - * static WEP keys - * - * For each context, the device has a table of 4 static WEP keys - * (one for each key index) that is updated with the following - * commands. - */ - -static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - bool send_if_empty) -{ - int i, not_empty = 0; - u8 buff[sizeof(struct iwl_wep_cmd) + - sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; - struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; - size_t cmd_size = sizeof(struct iwl_wep_cmd); - struct iwl_host_cmd cmd = { - .id = ctx->wep_key_cmd, - .data = { wep_cmd, }, - .flags = CMD_SYNC, - }; - - might_sleep(); - - memset(wep_cmd, 0, cmd_size + - (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); - - for (i = 0; i < WEP_KEYS_MAX ; i++) { - wep_cmd->key[i].key_index = i; - if (ctx->wep_keys[i].key_size) { - wep_cmd->key[i].key_offset = i; - not_empty = 1; - } else { - wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; - } - - wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; - memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, - ctx->wep_keys[i].key_size); - } - - wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; - wep_cmd->num_keys = WEP_KEYS_MAX; - - cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; - - cmd.len[0] = cmd_size; - - if (not_empty || send_if_empty) - return iwl_dvm_send_cmd(priv, &cmd); - else - return 0; -} - -int iwl_restore_default_wep_keys(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - lockdep_assert_held(&priv->mutex); - - return iwl_send_static_wepkey_cmd(priv, ctx, false); -} - -int iwl_remove_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", - keyconf->keyidx); - - memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_WEP(priv, - "Not sending REPLY_WEPKEY command due to RFKILL.\n"); - /* but keys in device are clear anyway so return success */ - return 0; - } - ret = iwl_send_static_wepkey_cmd(priv, ctx, 1); - IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", - keyconf->keyidx, ret); - - return ret; -} - -int iwl_set_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - if (keyconf->keylen != WEP_KEY_LEN_128 && - keyconf->keylen != WEP_KEY_LEN_64) { - IWL_DEBUG_WEP(priv, - "Bad WEP key length %d\n", keyconf->keylen); - return -EINVAL; - } - - keyconf->hw_key_idx = IWLAGN_HW_KEY_DEFAULT; - - ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; - memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, - keyconf->keylen); - - ret = iwl_send_static_wepkey_cmd(priv, ctx, false); - IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", - keyconf->keylen, keyconf->keyidx, ret); - - return ret; -} - -/* - * dynamic (per-station) keys - * - * The dynamic keys are a little more complicated. The device has - * a key cache of up to STA_KEY_MAX_NUM/STA_KEY_MAX_NUM_PAN keys. - * These are linked to stations by a table that contains an index - * into the key table for each station/key index/{mcast,unicast}, - * i.e. it's basically an array of pointers like this: - * key_offset_t key_mapping[NUM_STATIONS][4][2]; - * (it really works differently, but you can think of it as such) - * - * The key uploading and linking happens in the same command, the - * add station command with STA_MODIFY_KEY_MASK. - */ - -static u8 iwlagn_key_sta_id(struct iwl_priv *priv, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - - if (sta) - return iwl_sta_id(sta); - - /* - * The device expects GTKs for station interfaces to be - * installed as GTKs for the AP station. If we have no - * station ID, then use the ap_sta_id in that case. - */ - if (vif->type == NL80211_IFTYPE_STATION && vif_priv->ctx) - return vif_priv->ctx->ap_sta_id; - - return IWL_INVALID_STATION; -} - -static int iwlagn_send_sta_key(struct iwl_priv *priv, - struct ieee80211_key_conf *keyconf, - u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, - u32 cmd_flags) -{ - __le16 key_flags; - struct iwl_addsta_cmd sta_cmd; - int i; - - spin_lock_bh(&priv->sta_lock); - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags |= STA_KEY_FLG_MAP_KEY_MSK; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - key_flags |= STA_KEY_FLG_CCMP; - memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_TKIP: - key_flags |= STA_KEY_FLG_TKIP; - sta_cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; - for (i = 0; i < 5; i++) - sta_cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(sta_cmd.key.key, keyconf->key, keyconf->keylen); - break; - case WLAN_CIPHER_SUITE_WEP104: - key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - key_flags |= STA_KEY_FLG_WEP; - memcpy(&sta_cmd.key.key[3], keyconf->key, keyconf->keylen); - break; - default: - WARN_ON(1); - return -EINVAL; - } - - if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - key_flags |= STA_KEY_MULTICAST_MSK; - - /* key pointer (offset) */ - sta_cmd.key.key_offset = keyconf->hw_key_idx; - - sta_cmd.key.key_flags = key_flags; - sta_cmd.mode = STA_CONTROL_MODIFY_MSK; - sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; - - return iwl_send_add_sta(priv, &sta_cmd, cmd_flags); -} - -void iwl_update_tkip_key(struct iwl_priv *priv, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) -{ - u8 sta_id = iwlagn_key_sta_id(priv, vif, sta); - - if (sta_id == IWL_INVALID_STATION) - return; - - if (iwl_scan_cancel(priv)) { - /* cancel scan failed, just live w/ bad key and rely - briefly on SW decryption */ - return; - } - - iwlagn_send_sta_key(priv, keyconf, sta_id, - iv32, phase1key, CMD_ASYNC); -} - -int iwl_remove_dynamic_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta) -{ - struct iwl_addsta_cmd sta_cmd; - u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); - __le16 key_flags; - - /* if station isn't there, neither is the key */ - if (sta_id == IWL_INVALID_STATION) - return -ENOENT; - - spin_lock_bh(&priv->sta_lock); - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); - if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) - sta_id = IWL_INVALID_STATION; - spin_unlock_bh(&priv->sta_lock); - - if (sta_id == IWL_INVALID_STATION) - return 0; - - lockdep_assert_held(&priv->mutex); - - ctx->key_mapping_keys--; - - IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", - keyconf->keyidx, sta_id); - - if (!test_and_clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table)) - IWL_ERR(priv, "offset %d not used in uCode key table.\n", - keyconf->hw_key_idx); - - key_flags = cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags |= STA_KEY_FLG_MAP_KEY_MSK | STA_KEY_FLG_NO_ENC | - STA_KEY_FLG_INVALID; - - if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) - key_flags |= STA_KEY_MULTICAST_MSK; - - sta_cmd.key.key_flags = key_flags; - sta_cmd.key.key_offset = WEP_INVALID_OFFSET; - sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; - sta_cmd.mode = STA_CONTROL_MODIFY_MSK; - - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); -} - -int iwl_set_dynamic_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta) -{ - struct ieee80211_key_seq seq; - u16 p1k[5]; - int ret; - u8 sta_id = iwlagn_key_sta_id(priv, ctx->vif, sta); - const u8 *addr; - - if (sta_id == IWL_INVALID_STATION) - return -EINVAL; - - lockdep_assert_held(&priv->mutex); - - keyconf->hw_key_idx = iwl_get_free_ucode_key_offset(priv); - if (keyconf->hw_key_idx == WEP_INVALID_OFFSET) - return -ENOSPC; - - ctx->key_mapping_keys++; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - if (sta) - addr = sta->addr; - else /* station mode case only */ - addr = ctx->active.bssid_addr; - - /* pre-fill phase 1 key into device cache */ - ieee80211_get_key_rx_seq(keyconf, 0, &seq); - ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); - ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - seq.tkip.iv32, p1k, CMD_SYNC); - break; - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - 0, NULL, CMD_SYNC); - break; - default: - IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); - ret = -EINVAL; - } - - if (ret) { - ctx->key_mapping_keys--; - clear_bit(keyconf->hw_key_idx, &priv->ucode_key_table); - } - - IWL_DEBUG_WEP(priv, "Set dynamic key: cipher=%x len=%d idx=%d sta=%pM ret=%d\n", - keyconf->cipher, keyconf->keylen, keyconf->keyidx, - sta ? sta->addr : NULL, ret); - - return ret; -} - -/** - * iwlagn_alloc_bcast_station - add broadcast station into driver's station table. - * - * This adds the broadcast station into the driver's station table - * and marks it driver active, so that it will be restored to the - * device at the next best time. - */ -int iwlagn_alloc_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct iwl_link_quality_cmd *link_cmd; - u8 sta_id; - - spin_lock_bh(&priv->sta_lock); - sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Unable to prepare broadcast station\n"); - spin_unlock_bh(&priv->sta_lock); - - return -EINVAL; - } - - priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; - priv->stations[sta_id].used |= IWL_STA_BCAST; - spin_unlock_bh(&priv->sta_lock); - - link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); - if (!link_cmd) { - IWL_ERR(priv, - "Unable to initialize rate scaling for bcast station.\n"); - return -ENOMEM; - } - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].lq = link_cmd; - spin_unlock_bh(&priv->sta_lock); - - return 0; -} - -/** - * iwl_update_bcast_station - update broadcast station's LQ command - * - * Only used by iwlagn. Placed here to have all bcast station management - * code together. - */ -int iwl_update_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct iwl_link_quality_cmd *link_cmd; - u8 sta_id = ctx->bcast_sta_id; - - link_cmd = iwl_sta_alloc_lq(priv, ctx, sta_id); - if (!link_cmd) { - IWL_ERR(priv, "Unable to initialize rate scaling for bcast station.\n"); - return -ENOMEM; - } - - spin_lock_bh(&priv->sta_lock); - if (priv->stations[sta_id].lq) - kfree(priv->stations[sta_id].lq); - else - IWL_DEBUG_INFO(priv, "Bcast station rate scaling has not been initialized yet.\n"); - priv->stations[sta_id].lq = link_cmd; - spin_unlock_bh(&priv->sta_lock); - - return 0; -} - -int iwl_update_bcast_stations(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - int ret = 0; - - for_each_context(priv, ctx) { - ret = iwl_update_bcast_station(priv, ctx); - if (ret) - break; - } - - return ret; -} - -/** - * iwl_sta_tx_modify_enable_tid - Enable Tx for this TID in station table - */ -int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) -{ - struct iwl_addsta_cmd sta_cmd; - - lockdep_assert_held(&priv->mutex); - - /* Remove "disable" flag, to enable Tx for this TID */ - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; - priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); -} - -int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid, u16 ssn) -{ - int sta_id; - struct iwl_addsta_cmd sta_cmd; - - lockdep_assert_held(&priv->mutex); - - sta_id = iwl_sta_id(sta); - if (sta_id == IWL_INVALID_STATION) - return -ENXIO; - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.station_flags_msk = 0; - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; - priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; - priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); -} - -int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid) -{ - int sta_id; - struct iwl_addsta_cmd sta_cmd; - - lockdep_assert_held(&priv->mutex); - - sta_id = iwl_sta_id(sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); - return -ENXIO; - } - - spin_lock_bh(&priv->sta_lock); - priv->stations[sta_id].sta.station_flags_msk = 0; - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; - priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); - spin_unlock_bh(&priv->sta_lock); - - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); -} - - - -void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) -{ - struct iwl_addsta_cmd cmd = { - .mode = STA_CONTROL_MODIFY_MSK, - .station_flags = STA_FLG_PWR_SAVE_MSK, - .station_flags_msk = STA_FLG_PWR_SAVE_MSK, - .sta.sta_id = sta_id, - .sta.modify_mask = STA_MODIFY_SLEEP_TX_COUNT_MSK, - .sleep_tx_count = cpu_to_le16(cnt), - }; - - iwl_send_add_sta(priv, &cmd, CMD_ASYNC); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c deleted file mode 100644 index a5cfe0aceedb..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c +++ /dev/null @@ -1,696 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - - -#include -#include -#include -#include - -#include - -#include "iwl-agn.h" -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-commands.h" -#include "iwl-debug.h" -#include "iwl-agn-tt.h" -#include "iwl-modparams.h" - -/* default Thermal Throttling transaction table - * Current state | Throttling Down | Throttling Up - *============================================================================= - * Condition Nxt State Condition Nxt State Condition Nxt State - *----------------------------------------------------------------------------- - * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A - * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 - * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 - * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 - *============================================================================= - */ -static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, - {IWL_TI_1, 105, CT_KILL_THRESHOLD - 1}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} -}; -static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, - {IWL_TI_2, 110, CT_KILL_THRESHOLD - 1}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} -}; -static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX}, - {IWL_TI_CT_KILL, CT_KILL_THRESHOLD, IWL_ABSOLUTE_MAX} -}; -static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { - {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, - {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, - {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} -}; - -/* Advance Thermal Throttling default restriction table */ -static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { - {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, - {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, - {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, - {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } -}; - -bool iwl_tt_is_low_power_state(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (tt->state >= IWL_TI_1) - return true; - return false; -} - -u8 iwl_tt_current_power_mode(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - return tt->tt_power_mode; -} - -bool iwl_ht_enabled(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return true; - restriction = tt->restriction + tt->state; - return restriction->is_ht; -} - -static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) -{ - s32 temp = priv->temperature; /* degrees CELSIUS except specified */ - bool within_margin = false; - - if (!priv->thermal_throttle.advanced_tt) - within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= - CT_KILL_THRESHOLD_LEGACY) ? true : false; - else - within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= - CT_KILL_THRESHOLD) ? true : false; - return within_margin; -} - -bool iwl_check_for_ct_kill(struct iwl_priv *priv) -{ - bool is_ct_kill = false; - - if (iwl_within_ct_kill_margin(priv)) { - iwl_tt_enter_ct_kill(priv); - is_ct_kill = true; - } - return is_ct_kill; -} - -enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return IWL_ANT_OK_MULTI; - restriction = tt->restriction + tt->state; - return restriction->tx_stream; -} - -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return IWL_ANT_OK_MULTI; - restriction = tt->restriction + tt->state; - return restriction->rx_stream; -} - -#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ -#define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ - -/* - * toggle the bit to wake up uCode and check the temperature - * if the temperature is below CT, uCode will stay awake and send card - * state notification with CT_KILL bit clear to inform Thermal Throttling - * Management to change state. Otherwise, uCode will go back to sleep - * without doing anything, driver should continue the 5 seconds timer - * to wake up uCode for temperature check until temperature drop below CT - */ -static void iwl_tt_check_exit_ct_kill(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - unsigned long flags; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (tt->state == IWL_TI_CT_KILL) { - if (priv->thermal_throttle.ct_kill_toggle) { - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - priv->thermal_throttle.ct_kill_toggle = false; - } else { - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - priv->thermal_throttle.ct_kill_toggle = true; - } - iwl_read32(priv->trans, CSR_UCODE_DRV_GP1); - spin_lock_irqsave(&priv->trans->reg_lock, flags); - if (likely(iwl_grab_nic_access(priv->trans))) - iwl_release_nic_access(priv->trans); - spin_unlock_irqrestore(&priv->trans->reg_lock, flags); - - /* Reschedule the ct_kill timer to occur in - * CT_KILL_EXIT_DURATION seconds to ensure we get a - * thermal update */ - IWL_DEBUG_TEMP(priv, "schedule ct_kill exit timer\n"); - mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, - jiffies + CT_KILL_EXIT_DURATION * HZ); - } -} - -static void iwl_perform_ct_kill_task(struct iwl_priv *priv, - bool stop) -{ - if (stop) { - IWL_DEBUG_TEMP(priv, "Stop all queues\n"); - if (priv->mac80211_registered) - ieee80211_stop_queues(priv->hw); - IWL_DEBUG_TEMP(priv, - "Schedule 5 seconds CT_KILL Timer\n"); - mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, - jiffies + CT_KILL_EXIT_DURATION * HZ); - } else { - IWL_DEBUG_TEMP(priv, "Wake all queues\n"); - if (priv->mac80211_registered) - ieee80211_wake_queues(priv->hw); - } -} - -static void iwl_tt_ready_for_ct_kill(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* temperature timer expired, ready to go into CT_KILL state */ - if (tt->state != IWL_TI_CT_KILL) { - IWL_DEBUG_TEMP(priv, "entering CT_KILL state when " - "temperature timer expired\n"); - tt->state = IWL_TI_CT_KILL; - set_bit(STATUS_CT_KILL, &priv->status); - iwl_perform_ct_kill_task(priv, true); - } -} - -static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) -{ - IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); - /* make request to retrieve statistics information */ - iwl_send_statistics_request(priv, CMD_SYNC, false); - /* Reschedule the ct_kill wait timer */ - mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, - jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); -} - -#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) -#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) -#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) - -/* - * Legacy thermal throttling - * 1) Avoid NIC destruction due to high temperatures - * Chip will identify dangerously high temperatures that can - * harm the device and will power down - * 2) Avoid the NIC power down due to high temperature - * Throttle early enough to lower the power consumption before - * drastic steps are needed - */ -static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - enum iwl_tt_state old_state; - -#ifdef CONFIG_IWLWIFI_DEBUG - if ((tt->tt_previous_temp) && - (temp > tt->tt_previous_temp) && - ((temp - tt->tt_previous_temp) > - IWL_TT_INCREASE_MARGIN)) { - IWL_DEBUG_TEMP(priv, - "Temperature increase %d degree Celsius\n", - (temp - tt->tt_previous_temp)); - } -#endif - old_state = tt->state; - /* in Celsius */ - if (temp >= IWL_MINIMAL_POWER_THRESHOLD) - tt->state = IWL_TI_CT_KILL; - else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) - tt->state = IWL_TI_2; - else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) - tt->state = IWL_TI_1; - else - tt->state = IWL_TI_0; - -#ifdef CONFIG_IWLWIFI_DEBUG - tt->tt_previous_temp = temp; -#endif - /* stop ct_kill_waiting_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); - if (tt->state != old_state) { - switch (tt->state) { - case IWL_TI_0: - /* - * When the system is ready to go back to IWL_TI_0 - * we only have to call iwl_power_update_mode() to - * do so. - */ - break; - case IWL_TI_1: - tt->tt_power_mode = IWL_POWER_INDEX_3; - break; - case IWL_TI_2: - tt->tt_power_mode = IWL_POWER_INDEX_4; - break; - default: - tt->tt_power_mode = IWL_POWER_INDEX_5; - break; - } - mutex_lock(&priv->mutex); - if (old_state == IWL_TI_CT_KILL) - clear_bit(STATUS_CT_KILL, &priv->status); - if (tt->state != IWL_TI_CT_KILL && - iwl_power_update_mode(priv, true)) { - /* TT state not updated - * try again during next temperature read - */ - if (old_state == IWL_TI_CT_KILL) - set_bit(STATUS_CT_KILL, &priv->status); - tt->state = old_state; - IWL_ERR(priv, "Cannot update power mode, " - "TT state not updated\n"); - } else { - if (tt->state == IWL_TI_CT_KILL) { - if (force) { - set_bit(STATUS_CT_KILL, &priv->status); - iwl_perform_ct_kill_task(priv, true); - } else { - iwl_prepare_ct_kill_task(priv); - tt->state = old_state; - } - } else if (old_state == IWL_TI_CT_KILL && - tt->state != IWL_TI_CT_KILL) - iwl_perform_ct_kill_task(priv, false); - IWL_DEBUG_TEMP(priv, "Temperature state changed %u\n", - tt->state); - IWL_DEBUG_TEMP(priv, "Power Index change to %u\n", - tt->tt_power_mode); - } - mutex_unlock(&priv->mutex); - } -} - -/* - * Advance thermal throttling - * 1) Avoid NIC destruction due to high temperatures - * Chip will identify dangerously high temperatures that can - * harm the device and will power down - * 2) Avoid the NIC power down due to high temperature - * Throttle early enough to lower the power consumption before - * drastic steps are needed - * Actions include relaxing the power down sleep thresholds and - * decreasing the number of TX streams - * 3) Avoid throughput performance impact as much as possible - * - *============================================================================= - * Condition Nxt State Condition Nxt State Condition Nxt State - *----------------------------------------------------------------------------- - * IWL_TI_0 T >= 114 CT_KILL 114>T>=105 TI_1 N/A N/A - * IWL_TI_1 T >= 114 CT_KILL 114>T>=110 TI_2 T<=95 TI_0 - * IWL_TI_2 T >= 114 CT_KILL T<=100 TI_1 - * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 - *============================================================================= - */ -static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - int i; - bool changed = false; - enum iwl_tt_state old_state; - struct iwl_tt_trans *transaction; - - old_state = tt->state; - for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { - /* based on the current TT state, - * find the curresponding transaction table - * each table has (IWL_TI_STATE_MAX - 1) entries - * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) - * will advance to the correct table. - * then based on the current temperature - * find the next state need to transaction to - * go through all the possible (IWL_TI_STATE_MAX - 1) entries - * in the current table to see if transaction is needed - */ - transaction = tt->transaction + - ((old_state * (IWL_TI_STATE_MAX - 1)) + i); - if (temp >= transaction->tt_low && - temp <= transaction->tt_high) { -#ifdef CONFIG_IWLWIFI_DEBUG - if ((tt->tt_previous_temp) && - (temp > tt->tt_previous_temp) && - ((temp - tt->tt_previous_temp) > - IWL_TT_INCREASE_MARGIN)) { - IWL_DEBUG_TEMP(priv, - "Temperature increase %d " - "degree Celsius\n", - (temp - tt->tt_previous_temp)); - } - tt->tt_previous_temp = temp; -#endif - if (old_state != - transaction->next_state) { - changed = true; - tt->state = - transaction->next_state; - } - break; - } - } - /* stop ct_kill_waiting_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); - if (changed) { - if (tt->state >= IWL_TI_1) { - /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ - tt->tt_power_mode = IWL_POWER_INDEX_5; - - if (!iwl_ht_enabled(priv)) { - struct iwl_rxon_context *ctx; - - for_each_context(priv, ctx) { - struct iwl_rxon_cmd *rxon; - - rxon = &ctx->staging; - - /* disable HT */ - rxon->flags &= ~( - RXON_FLG_CHANNEL_MODE_MSK | - RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | - RXON_FLG_HT40_PROT_MSK | - RXON_FLG_HT_PROT_MSK); - } - } else { - /* check HT capability and set - * according to the system HT capability - * in case get disabled before */ - iwl_set_rxon_ht(priv, &priv->current_ht_config); - } - - } else { - /* - * restore system power setting -- it will be - * recalculated automatically. - */ - - /* check HT capability and set - * according to the system HT capability - * in case get disabled before */ - iwl_set_rxon_ht(priv, &priv->current_ht_config); - } - mutex_lock(&priv->mutex); - if (old_state == IWL_TI_CT_KILL) - clear_bit(STATUS_CT_KILL, &priv->status); - if (tt->state != IWL_TI_CT_KILL && - iwl_power_update_mode(priv, true)) { - /* TT state not updated - * try again during next temperature read - */ - IWL_ERR(priv, "Cannot update power mode, " - "TT state not updated\n"); - if (old_state == IWL_TI_CT_KILL) - set_bit(STATUS_CT_KILL, &priv->status); - tt->state = old_state; - } else { - IWL_DEBUG_TEMP(priv, - "Thermal Throttling to new state: %u\n", - tt->state); - if (old_state != IWL_TI_CT_KILL && - tt->state == IWL_TI_CT_KILL) { - if (force) { - IWL_DEBUG_TEMP(priv, - "Enter IWL_TI_CT_KILL\n"); - set_bit(STATUS_CT_KILL, &priv->status); - iwl_perform_ct_kill_task(priv, true); - } else { - iwl_prepare_ct_kill_task(priv); - tt->state = old_state; - } - } else if (old_state == IWL_TI_CT_KILL && - tt->state != IWL_TI_CT_KILL) { - IWL_DEBUG_TEMP(priv, "Exit IWL_TI_CT_KILL\n"); - iwl_perform_ct_kill_task(priv, false); - } - } - mutex_unlock(&priv->mutex); - } -} - -/* Card State Notification indicated reach critical temperature - * if PSP not enable, no Thermal Throttling function will be performed - * just set the GP1 bit to acknowledge the event - * otherwise, go into IWL_TI_CT_KILL state - * since Card State Notification will not provide any temperature reading - * for Legacy mode - * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() - * for advance mode - * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state - */ -static void iwl_bg_ct_enter(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!iwl_is_ready(priv)) - return; - - if (tt->state != IWL_TI_CT_KILL) { - IWL_ERR(priv, "Device reached critical temperature " - "- ucode going to sleep!\n"); - if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, - IWL_MINIMAL_POWER_THRESHOLD, - true); - else - iwl_advance_tt_handler(priv, - CT_KILL_THRESHOLD + 1, true); - } -} - -/* Card State Notification indicated out of critical temperature - * since Card State Notification will not provide any temperature reading - * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature - * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state - */ -static void iwl_bg_ct_exit(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!iwl_is_ready(priv)) - return; - - /* stop ct_kill_exit_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); - - if (tt->state == IWL_TI_CT_KILL) { - IWL_ERR(priv, - "Device temperature below critical" - "- ucode awake!\n"); - /* - * exit from CT_KILL state - * reset the current temperature reading - */ - priv->temperature = 0; - if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, - IWL_REDUCED_PERFORMANCE_THRESHOLD_2, - true); - else - iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD, - true); - } -} - -void iwl_tt_enter_ct_kill(struct iwl_priv *priv) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_TEMP(priv, "Queueing critical temperature enter.\n"); - queue_work(priv->workqueue, &priv->ct_enter); -} - -void iwl_tt_exit_ct_kill(struct iwl_priv *priv) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_TEMP(priv, "Queueing critical temperature exit.\n"); - queue_work(priv->workqueue, &priv->ct_exit); -} - -static void iwl_bg_tt_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); - s32 temp = priv->temperature; /* degrees CELSIUS except specified */ - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!priv->thermal_throttle.advanced_tt) - iwl_legacy_tt_handler(priv, temp, false); - else - iwl_advance_tt_handler(priv, temp, false); -} - -void iwl_tt_handler(struct iwl_priv *priv) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - IWL_DEBUG_TEMP(priv, "Queueing thermal throttling work.\n"); - queue_work(priv->workqueue, &priv->tt_work); -} - -/* Thermal throttling initialization - * For advance thermal throttling: - * Initialize Thermal Index and temperature threshold table - * Initialize thermal throttling restriction table - */ -void iwl_tt_initialize(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); - struct iwl_tt_trans *transaction; - - IWL_DEBUG_TEMP(priv, "Initialize Thermal Throttling\n"); - - memset(tt, 0, sizeof(struct iwl_tt_mgmt)); - - tt->state = IWL_TI_0; - init_timer(&priv->thermal_throttle.ct_kill_exit_tm); - priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; - priv->thermal_throttle.ct_kill_exit_tm.function = - iwl_tt_check_exit_ct_kill; - init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); - priv->thermal_throttle.ct_kill_waiting_tm.data = - (unsigned long)priv; - priv->thermal_throttle.ct_kill_waiting_tm.function = - iwl_tt_ready_for_ct_kill; - /* setup deferred ct kill work */ - INIT_WORK(&priv->tt_work, iwl_bg_tt_work); - INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); - INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - - if (priv->cfg->base_params->adv_thermal_throttle) { - IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); - tt->restriction = kcalloc(IWL_TI_STATE_MAX, - sizeof(struct iwl_tt_restriction), - GFP_KERNEL); - tt->transaction = kcalloc(IWL_TI_STATE_MAX * - (IWL_TI_STATE_MAX - 1), - sizeof(struct iwl_tt_trans), - GFP_KERNEL); - if (!tt->restriction || !tt->transaction) { - IWL_ERR(priv, "Fallback to Legacy Throttling\n"); - priv->thermal_throttle.advanced_tt = false; - kfree(tt->restriction); - tt->restriction = NULL; - kfree(tt->transaction); - tt->transaction = NULL; - } else { - transaction = tt->transaction + - (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_0[0], size); - transaction = tt->transaction + - (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_1[0], size); - transaction = tt->transaction + - (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_2[0], size); - transaction = tt->transaction + - (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); - memcpy(transaction, &tt_range_3[0], size); - size = sizeof(struct iwl_tt_restriction) * - IWL_TI_STATE_MAX; - memcpy(tt->restriction, - &restriction_range[0], size); - priv->thermal_throttle.advanced_tt = true; - } - } else { - IWL_DEBUG_TEMP(priv, "Legacy Thermal Throttling\n"); - priv->thermal_throttle.advanced_tt = false; - } -} - -/* cleanup thermal throttling management related memory and timer */ -void iwl_tt_exit(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - - /* stop ct_kill_exit_tm timer if activated */ - del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); - /* stop ct_kill_waiting_tm timer if activated */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); - cancel_work_sync(&priv->tt_work); - cancel_work_sync(&priv->ct_enter); - cancel_work_sync(&priv->ct_exit); - - if (priv->thermal_throttle.advanced_tt) { - /* free advance thermal throttling memory */ - kfree(tt->restriction); - tt->restriction = NULL; - kfree(tt->transaction); - tt->transaction = NULL; - } -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tt.h b/drivers/net/wireless/iwlwifi/iwl-agn-tt.h deleted file mode 100644 index 86bbf47501c1..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tt.h +++ /dev/null @@ -1,128 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#ifndef __iwl_tt_setting_h__ -#define __iwl_tt_setting_h__ - -#include "iwl-commands.h" - -#define IWL_ABSOLUTE_ZERO 0 -#define IWL_ABSOLUTE_MAX 0xFFFFFFFF -#define IWL_TT_INCREASE_MARGIN 5 -#define IWL_TT_CT_KILL_MARGIN 3 - -enum iwl_antenna_ok { - IWL_ANT_OK_NONE, - IWL_ANT_OK_SINGLE, - IWL_ANT_OK_MULTI, -}; - -/* Thermal Throttling State Machine states */ -enum iwl_tt_state { - IWL_TI_0, /* normal temperature, system power state */ - IWL_TI_1, /* high temperature detect, low power state */ - IWL_TI_2, /* higher temperature detected, lower power state */ - IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ - IWL_TI_STATE_MAX -}; - -/** - * struct iwl_tt_restriction - Thermal Throttling restriction table - * @tx_stream: number of tx stream allowed - * @is_ht: ht enable/disable - * @rx_stream: number of rx stream allowed - * - * This table is used by advance thermal throttling management - * based on the current thermal throttling state, and determines - * the number of tx/rx streams and the status of HT operation. - */ -struct iwl_tt_restriction { - enum iwl_antenna_ok tx_stream; - enum iwl_antenna_ok rx_stream; - bool is_ht; -}; - -/** - * struct iwl_tt_trans - Thermal Throttling transaction table - * @next_state: next thermal throttling mode - * @tt_low: low temperature threshold to change state - * @tt_high: high temperature threshold to change state - * - * This is used by the advanced thermal throttling algorithm - * to determine the next thermal state to go based on the - * current temperature. - */ -struct iwl_tt_trans { - enum iwl_tt_state next_state; - u32 tt_low; - u32 tt_high; -}; - -/** - * struct iwl_tt_mgnt - Thermal Throttling Management structure - * @advanced_tt: advanced thermal throttle required - * @state: current Thermal Throttling state - * @tt_power_mode: Thermal Throttling power mode index - * being used to set power level when - * when thermal throttling state != IWL_TI_0 - * the tt_power_mode should set to different - * power mode based on the current tt state - * @tt_previous_temperature: last measured temperature - * @iwl_tt_restriction: ptr to restriction tbl, used by advance - * thermal throttling to determine how many tx/rx streams - * should be used in tt state; and can HT be enabled or not - * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling - * state transaction - * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature - * @ct_kill_exit_tm: timer to exit thermal kill - */ -struct iwl_tt_mgmt { - enum iwl_tt_state state; - bool advanced_tt; - u8 tt_power_mode; - bool ct_kill_toggle; -#ifdef CONFIG_IWLWIFI_DEBUG - s32 tt_previous_temp; -#endif - struct iwl_tt_restriction *restriction; - struct iwl_tt_trans *transaction; - struct timer_list ct_kill_exit_tm; - struct timer_list ct_kill_waiting_tm; -}; - -u8 iwl_tt_current_power_mode(struct iwl_priv *priv); -bool iwl_tt_is_low_power_state(struct iwl_priv *priv); -bool iwl_ht_enabled(struct iwl_priv *priv); -enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); -void iwl_tt_enter_ct_kill(struct iwl_priv *priv); -void iwl_tt_exit_ct_kill(struct iwl_priv *priv); -void iwl_tt_handler(struct iwl_priv *priv); -void iwl_tt_initialize(struct iwl_priv *priv); -void iwl_tt_exit(struct iwl_priv *priv); - -#endif /* __iwl_tt_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c deleted file mode 100644 index ad1d36fa7642..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ /dev/null @@ -1,1386 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include - -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn-hw.h" -#include "iwl-agn.h" -#include "iwl-trans.h" - -static const u8 tid_to_ac[] = { - IEEE80211_AC_BE, - IEEE80211_AC_BK, - IEEE80211_AC_BK, - IEEE80211_AC_BE, - IEEE80211_AC_VI, - IEEE80211_AC_VI, - IEEE80211_AC_VO, - IEEE80211_AC_VO, -}; - -static void iwlagn_tx_cmd_protection(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - __le16 fc, __le32 *tx_flags) -{ - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS || - info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT || - info->flags & IEEE80211_TX_CTL_AMPDU) - *tx_flags |= TX_CMD_FLG_PROT_REQUIRE_MSK; -} - -/* - * handle build REPLY_TX command notification. - */ -static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, - struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, u8 sta_id) -{ - __le16 fc = hdr->frame_control; - __le32 tx_flags = tx_cmd->tx_flags; - - tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) - tx_flags |= TX_CMD_FLG_ACK_MSK; - else - tx_flags &= ~TX_CMD_FLG_ACK_MSK; - - if (ieee80211_is_probe_resp(fc)) - tx_flags |= TX_CMD_FLG_TSF_MSK; - else if (ieee80211_is_back_req(fc)) - tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; - else if (info->band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && - (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || - ieee80211_is_reassoc_req(fc) || - skb->protocol == cpu_to_be16(ETH_P_PAE))) - tx_flags |= TX_CMD_FLG_IGNORE_BT; - - - tx_cmd->sta_id = sta_id; - if (ieee80211_has_morefrags(fc)) - tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tx_cmd->tid_tspec = qc[0] & 0xf; - tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; - } else { - tx_cmd->tid_tspec = IWL_TID_NON_QOS; - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - else - tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; - } - - iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); - - tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if (ieee80211_is_mgmt(fc)) { - if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); - else - tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); - } else { - tx_cmd->timeout.pm_frame_timeout = 0; - } - - tx_cmd->driver_txop = 0; - tx_cmd->tx_flags = tx_flags; - tx_cmd->next_frame_len = 0; -} - -static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, - struct iwl_tx_cmd *tx_cmd, - struct ieee80211_tx_info *info, - __le16 fc) -{ - u32 rate_flags; - int rate_idx; - u8 rts_retry_limit; - u8 data_retry_limit; - u8 rate_plcp; - - if (priv->wowlan) { - rts_retry_limit = IWLAGN_LOW_RETRY_LIMIT; - data_retry_limit = IWLAGN_LOW_RETRY_LIMIT; - } else { - /* Set retry limit on RTS packets */ - rts_retry_limit = IWLAGN_RTS_DFAULT_RETRY_LIMIT; - - /* Set retry limit on DATA packets and Probe Responses*/ - if (ieee80211_is_probe_resp(fc)) { - data_retry_limit = IWLAGN_MGMT_DFAULT_RETRY_LIMIT; - rts_retry_limit = - min(data_retry_limit, rts_retry_limit); - } else if (ieee80211_is_back_req(fc)) - data_retry_limit = IWLAGN_BAR_DFAULT_RETRY_LIMIT; - else - data_retry_limit = IWLAGN_DEFAULT_TX_RETRY; - } - - tx_cmd->data_retry_limit = data_retry_limit; - tx_cmd->rts_retry_limit = rts_retry_limit; - - /* DATA packets will use the uCode station table for rate/antenna - * selection */ - if (ieee80211_is_data(fc)) { - tx_cmd->initial_rate_index = 0; - tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - if (priv->tm_fixed_rate) { - /* - * rate overwrite by testmode - * we not only send lq command to change rate - * we also re-enforce per data pkt base. - */ - tx_cmd->tx_flags &= ~TX_CMD_FLG_STA_RATE_MSK; - memcpy(&tx_cmd->rate_n_flags, &priv->tm_fixed_rate, - sizeof(tx_cmd->rate_n_flags)); - } -#endif - return; - } else if (ieee80211_is_back_req(fc)) - tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; - - /** - * If the current TX rate stored in mac80211 has the MCS bit set, it's - * not really a TX rate. Thus, we use the lowest supported rate for - * this band. Also use the lowest supported rate if the stored rate - * index is invalid. - */ - rate_idx = info->control.rates[0].idx; - if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || - (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) - rate_idx = rate_lowest_index(&priv->bands[info->band], - info->control.sta); - /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ - if (info->band == IEEE80211_BAND_5GHZ) - rate_idx += IWL_FIRST_OFDM_RATE; - /* Get PLCP rate for tx_cmd->rate_n_flags */ - rate_plcp = iwl_rates[rate_idx].plcp; - /* Zero out flags for this packet */ - rate_flags = 0; - - /* Set CCK flag as needed */ - if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) - rate_flags |= RATE_MCS_CCK_MSK; - - /* Set up antennas */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && - priv->bt_full_concurrent) { - /* operated as 1x1 in full concurrency mode */ - priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - first_antenna(priv->hw_params.valid_tx_ant)); - } else - priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - priv->hw_params.valid_tx_ant); - rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); - - /* Set the rate in the TX cmd */ - tx_cmd->rate_n_flags = iwl_hw_set_rate_n_flags(rate_plcp, rate_flags); -} - -static void iwlagn_tx_cmd_build_hwcrypto(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd, - struct sk_buff *skb_frag) -{ - struct ieee80211_key_conf *keyconf = info->control.hw_key; - - switch (keyconf->cipher) { - case WLAN_CIPHER_SUITE_CCMP: - tx_cmd->sec_ctl = TX_CMD_SEC_CCM; - memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); - if (info->flags & IEEE80211_TX_CTL_AMPDU) - tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; - IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); - break; - - case WLAN_CIPHER_SUITE_TKIP: - tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; - ieee80211_get_tkip_p2k(keyconf, skb_frag, tx_cmd->key); - IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); - break; - - case WLAN_CIPHER_SUITE_WEP104: - tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; - /* fall through */ - case WLAN_CIPHER_SUITE_WEP40: - tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | - (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); - - memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); - - IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " - "with key %d\n", keyconf->keyidx); - break; - - default: - IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); - break; - } -} - -/** - * iwl_sta_id_or_broadcast - return sta_id or broadcast sta - * @context: the current context - * @sta: mac80211 station - * - * In certain circumstances mac80211 passes a station pointer - * that may be %NULL, for example during TX or key setup. In - * that case, we need to use the broadcast station, so this - * inline wraps that pattern. - */ -static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, - struct ieee80211_sta *sta) -{ - int sta_id; - - if (!sta) - return context->bcast_sta_id; - - sta_id = iwl_sta_id(sta); - - /* - * mac80211 should not be passing a partially - * initialised station! - */ - WARN_ON(sta_id == IWL_INVALID_STATION); - - return sta_id; -} - -/* - * start REPLY_TX command process - */ -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_station_priv *sta_priv = NULL; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_device_cmd *dev_cmd = NULL; - struct iwl_tx_cmd *tx_cmd; - __le16 fc; - u8 hdr_len; - u16 len, seq_number = 0; - u8 sta_id, tid = IWL_MAX_TID_COUNT; - bool is_agg = false; - int txq_id; - - if (info->control.vif) - ctx = iwl_rxon_ctx_from_vif(info->control.vif); - - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); - goto drop_unlock_priv; - } - - fc = hdr->frame_control; - -#ifdef CONFIG_IWLWIFI_DEBUG - if (ieee80211_is_auth(fc)) - IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); - else if (ieee80211_is_assoc_req(fc)) - IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_req(fc)) - IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); -#endif - - if (unlikely(ieee80211_is_probe_resp(fc))) { - struct iwl_wipan_noa_data *noa_data = - rcu_dereference(priv->noa_data); - - if (noa_data && - pskb_expand_head(skb, 0, noa_data->length, - GFP_ATOMIC) == 0) { - memcpy(skb_put(skb, noa_data->length), - noa_data->data, noa_data->length); - hdr = (struct ieee80211_hdr *)skb->data; - } - } - - hdr_len = ieee80211_hdrlen(fc); - - /* For management frames use broadcast id to do not break aggregation */ - if (!ieee80211_is_data(fc)) - sta_id = ctx->bcast_sta_id; - else { - /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast(ctx, info->control.sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", - hdr->addr1); - goto drop_unlock_priv; - } - } - - IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); - - if (info->control.sta) - sta_priv = (void *)info->control.sta->drv_priv; - - if (sta_priv && sta_priv->asleep && - (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { - /* - * This sends an asynchronous command to the device, - * but we can rely on it being processed before the - * next frame is processed -- and the next frame to - * this station is the one that will consume this - * counter. - * For now set the counter to just 1 since we do not - * support uAPSD yet. - * - * FIXME: If we get two non-bufferable frames one - * after the other, we might only send out one of - * them because this is racy. - */ - iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); - } - - if (info->flags & IEEE80211_TX_CTL_AMPDU) - is_agg = true; - - dev_cmd = kmem_cache_alloc(iwl_tx_cmd_pool, GFP_ATOMIC); - - if (unlikely(!dev_cmd)) - goto drop_unlock_priv; - - memset(dev_cmd, 0, sizeof(*dev_cmd)); - tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; - - /* Total # bytes to be transmitted */ - len = (u16)skb->len; - tx_cmd->len = cpu_to_le16(len); - - if (info->control.hw_key) - iwlagn_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb); - - /* TODO need this for burst mode later on */ - iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); - - iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); - - memset(&info->status, 0, sizeof(info->status)); - - info->driver_data[0] = ctx; - info->driver_data[1] = dev_cmd; - - spin_lock(&priv->sta_lock); - - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - u8 *qc = NULL; - struct iwl_tid_data *tid_data; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - goto drop_unlock_sta; - tid_data = &priv->tid_data[sta_id][tid]; - - /* aggregation is on for this */ - if (info->flags & IEEE80211_TX_CTL_AMPDU && - tid_data->agg.state != IWL_AGG_ON) { - IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:" - " Tx flags = 0x%08x, agg.state = %d", - info->flags, tid_data->agg.state); - IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", - sta_id, tid, SEQ_TO_SN(tid_data->seq_number)); - goto drop_unlock_sta; - } - - /* We can receive packets from the stack in IWL_AGG_{ON,OFF} - * only. Check this here. - */ - if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && - tid_data->agg.state != IWL_AGG_OFF, - "Tx while agg.state = %d", tid_data->agg.state)) - goto drop_unlock_sta; - - seq_number = tid_data->seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; - } - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdr_len); - - if (is_agg) - txq_id = priv->tid_data[sta_id][tid].agg.txq_id; - else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* - * Send this frame after DTIM -- there's a special queue - * reserved for this for contexts that support AP mode. - */ - txq_id = ctx->mcast_queue; - - /* - * The microcode will clear the more data - * bit in the last frame it transmits. - */ - hdr->frame_control |= - cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - txq_id = IWL_AUX_QUEUE; - else - txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)]; - - WARN_ON_ONCE(!is_agg && txq_id != info->hw_queue); - WARN_ON_ONCE(is_agg && - priv->queue_to_mac80211[txq_id] != info->hw_queue); - - if (iwl_trans_tx(priv->trans, skb, dev_cmd, txq_id)) - goto drop_unlock_sta; - - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) && - !ieee80211_has_morefrags(fc)) - priv->tid_data[sta_id][tid].seq_number = seq_number; - - spin_unlock(&priv->sta_lock); - - /* - * Avoid atomic ops if it isn't an associated client. - * Also, if this is a packet for aggregation, don't - * increase the counter because the ucode will stop - * aggregation queues when their respective station - * goes to sleep. - */ - if (sta_priv && sta_priv->client && !is_agg) - atomic_inc(&sta_priv->pending_frames); - - if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - iwl_scan_offchannel_skb(priv); - - return 0; - -drop_unlock_sta: - if (dev_cmd) - kmem_cache_free(iwl_tx_cmd_pool, dev_cmd); - spin_unlock(&priv->sta_lock); -drop_unlock_priv: - return -1; -} - -static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) -{ - int q; - - for (q = IWLAGN_FIRST_AMPDU_QUEUE; - q < priv->cfg->base_params->num_of_queues; q++) { - if (!test_and_set_bit(q, priv->agg_q_alloc)) { - priv->queue_to_mac80211[q] = mq; - return q; - } - } - - return -ENOSPC; -} - -static void iwlagn_dealloc_agg_txq(struct iwl_priv *priv, int q) -{ - clear_bit(q, priv->agg_q_alloc); - priv->queue_to_mac80211[q] = IWL_INVALID_MAC80211_QUEUE; -} - -int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) -{ - struct iwl_tid_data *tid_data; - int sta_id, txq_id; - enum iwl_agg_state agg_state; - - sta_id = iwl_sta_id(sta); - - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); - return -ENXIO; - } - - spin_lock_bh(&priv->sta_lock); - - tid_data = &priv->tid_data[sta_id][tid]; - txq_id = priv->tid_data[sta_id][tid].agg.txq_id; - - switch (priv->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* - * This can happen if the peer stops aggregation - * again before we've had a chance to drain the - * queue we selected previously, i.e. before the - * session was really started completely. - */ - IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); - goto turn_off; - case IWL_AGG_STARTING: - /* - * This can happen when the session is stopped before - * we receive ADDBA response - */ - IWL_DEBUG_HT(priv, "AGG stop before AGG became operational\n"); - goto turn_off; - case IWL_AGG_ON: - break; - default: - IWL_WARN(priv, "Stopping AGG while state not ON " - "or starting for %d on %d (%d)\n", sta_id, tid, - priv->tid_data[sta_id][tid].agg.state); - spin_unlock_bh(&priv->sta_lock); - return 0; - } - - tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); - - /* There are still packets for this RA / TID in the HW */ - if (!test_bit(txq_id, priv->agg_q_alloc)) { - IWL_DEBUG_TX_QUEUES(priv, - "stopping AGG on STA/TID %d/%d but hwq %d not used\n", - sta_id, tid, txq_id); - } else if (tid_data->agg.ssn != tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " - "next_recl = %d\n", - tid_data->agg.ssn, - tid_data->next_reclaimed); - priv->tid_data[sta_id][tid].agg.state = - IWL_EMPTYING_HW_QUEUE_DELBA; - spin_unlock_bh(&priv->sta_lock); - return 0; - } - - IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", - tid_data->agg.ssn); -turn_off: - agg_state = priv->tid_data[sta_id][tid].agg.state; - priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF; - - spin_unlock_bh(&priv->sta_lock); - - if (test_bit(txq_id, priv->agg_q_alloc)) { - /* - * If the transport didn't know that we wanted to start - * agreggation, don't tell it that we want to stop them. - * This can happen when we don't get the addBA response on - * time, or we hadn't time to drain the AC queues. - */ - if (agg_state == IWL_AGG_ON) - iwl_trans_tx_agg_disable(priv->trans, txq_id); - else - IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", - agg_state); - iwlagn_dealloc_agg_txq(priv, txq_id); - } - - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - - return 0; -} - -int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn) -{ - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - struct iwl_tid_data *tid_data; - int sta_id, txq_id, ret; - - IWL_DEBUG_HT(priv, "TX AGG request on ra = %pM tid = %d\n", - sta->addr, tid); - - sta_id = iwl_sta_id(sta); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Start AGG on invalid station\n"); - return -ENXIO; - } - if (unlikely(tid >= IWL_MAX_TID_COUNT)) - return -EINVAL; - - if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { - IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); - return -ENXIO; - } - - txq_id = iwlagn_alloc_agg_txq(priv, ctx->ac_to_queue[tid_to_ac[tid]]); - if (txq_id < 0) { - IWL_DEBUG_TX_QUEUES(priv, - "No free aggregation queue for %pM/%d\n", - sta->addr, tid); - return txq_id; - } - - ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); - if (ret) - return ret; - - spin_lock_bh(&priv->sta_lock); - tid_data = &priv->tid_data[sta_id][tid]; - tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); - tid_data->agg.txq_id = txq_id; - - *ssn = tid_data->agg.ssn; - - if (*ssn == tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d\n", - tid_data->agg.ssn); - tid_data->agg.state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); - } else { - IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " - "next_reclaimed = %d\n", - tid_data->agg.ssn, - tid_data->next_reclaimed); - tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; - } - spin_unlock_bh(&priv->sta_lock); - - return ret; -} - -int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size) -{ - struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - int q, fifo; - u16 ssn; - - buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); - - spin_lock_bh(&priv->sta_lock); - ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; - q = priv->tid_data[sta_priv->sta_id][tid].agg.txq_id; - priv->tid_data[sta_priv->sta_id][tid].agg.state = IWL_AGG_ON; - spin_unlock_bh(&priv->sta_lock); - - fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; - - iwl_trans_tx_agg_setup(priv->trans, q, fifo, - sta_priv->sta_id, tid, - buf_size, ssn); - - /* - * If the limit is 0, then it wasn't initialised yet, - * use the default. We can do that since we take the - * minimum below, and we don't want to go above our - * default due to hardware restrictions. - */ - if (sta_priv->max_agg_bufsize == 0) - sta_priv->max_agg_bufsize = - LINK_QUAL_AGG_FRAME_LIMIT_DEF; - - /* - * Even though in theory the peer could have different - * aggregation reorder buffer sizes for different sessions, - * our ucode doesn't allow for that and has a global limit - * for each station. Therefore, use the minimum of all the - * aggregation sessions and our default value. - */ - sta_priv->max_agg_bufsize = - min(sta_priv->max_agg_bufsize, buf_size); - - if (priv->hw_params.use_rts_for_aggregation) { - /* - * switch to RTS/CTS if it is the prefer protection - * method for HT traffic - */ - - sta_priv->lq_sta.lq.general_params.flags |= - LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; - } - priv->agg_tids_count++; - IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", - priv->agg_tids_count); - - sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = - sta_priv->max_agg_bufsize; - - IWL_DEBUG_HT(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - - return iwl_send_lq_cmd(priv, ctx, - &sta_priv->lq_sta.lq, CMD_ASYNC, false); -} - -static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) -{ - struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; - enum iwl_rxon_context_id ctx; - struct ieee80211_vif *vif; - u8 *addr; - - lockdep_assert_held(&priv->sta_lock); - - addr = priv->stations[sta_id].sta.sta.addr; - ctx = priv->stations[sta_id].ctxid; - vif = priv->contexts[ctx].vif; - - switch (priv->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_DELBA: - /* There are no packets for this RA / TID in the HW any more */ - if (tid_data->agg.ssn == tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, - "Can continue DELBA flow ssn = next_recl =" - " %d", tid_data->next_reclaimed); - iwl_trans_tx_agg_disable(priv->trans, - tid_data->agg.txq_id); - iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); - tid_data->agg.state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); - } - break; - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* There are no packets for this RA / TID in the HW any more */ - if (tid_data->agg.ssn == tid_data->next_reclaimed) { - IWL_DEBUG_TX_QUEUES(priv, - "Can continue ADDBA flow ssn = next_recl =" - " %d", tid_data->next_reclaimed); - tid_data->agg.state = IWL_AGG_STARTING; - ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); - } - break; - default: - break; - } -} - -static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - const u8 *addr1) -{ - struct ieee80211_sta *sta; - struct iwl_station_priv *sta_priv; - - rcu_read_lock(); - sta = ieee80211_find_sta(ctx->vif, addr1); - if (sta) { - sta_priv = (void *)sta->drv_priv; - /* avoid atomic ops if this isn't a client */ - if (sta_priv->client && - atomic_dec_return(&sta_priv->pending_frames) == 0) - ieee80211_sta_block_awake(priv->hw, sta, false); - } - rcu_read_unlock(); -} - -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwlagn_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, - struct ieee80211_tx_info *info) -{ - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); - if (rate_n_flags & RATE_MCS_HT_MSK) - r->flags |= IEEE80211_TX_RC_MCS; - if (rate_n_flags & RATE_MCS_GF_MSK) - r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - if (rate_n_flags & RATE_MCS_HT40_MSK) - r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (rate_n_flags & RATE_MCS_DUP_MSK) - r->flags |= IEEE80211_TX_RC_DUP_DATA; - if (rate_n_flags & RATE_MCS_SGI_MSK) - r->flags |= IEEE80211_TX_RC_SHORT_GI; - r->idx = iwlagn_hwrate_to_mac80211_idx(rate_n_flags, info->band); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_get_tx_fail_reason(u32 status) -{ -#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x -#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x - - switch (status & TX_STATUS_MSK) { - case TX_STATUS_SUCCESS: - return "SUCCESS"; - TX_STATUS_POSTPONE(DELAY); - TX_STATUS_POSTPONE(FEW_BYTES); - TX_STATUS_POSTPONE(BT_PRIO); - TX_STATUS_POSTPONE(QUIET_PERIOD); - TX_STATUS_POSTPONE(CALC_TTAK); - TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); - TX_STATUS_FAIL(SHORT_LIMIT); - TX_STATUS_FAIL(LONG_LIMIT); - TX_STATUS_FAIL(FIFO_UNDERRUN); - TX_STATUS_FAIL(DRAIN_FLOW); - TX_STATUS_FAIL(RFKILL_FLUSH); - TX_STATUS_FAIL(LIFE_EXPIRE); - TX_STATUS_FAIL(DEST_PS); - TX_STATUS_FAIL(HOST_ABORTED); - TX_STATUS_FAIL(BT_RETRY); - TX_STATUS_FAIL(STA_INVALID); - TX_STATUS_FAIL(FRAG_DROPPED); - TX_STATUS_FAIL(TID_DISABLE); - TX_STATUS_FAIL(FIFO_FLUSHED); - TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); - TX_STATUS_FAIL(PASSIVE_NO_RX); - TX_STATUS_FAIL(NO_BEACON_ON_RADAR); - } - - return "UNKNOWN"; - -#undef TX_STATUS_FAIL -#undef TX_STATUS_POSTPONE -} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static void iwlagn_count_agg_tx_err_status(struct iwl_priv *priv, u16 status) -{ - status &= AGG_TX_STATUS_MSK; - - switch (status) { - case AGG_TX_STATE_UNDERRUN_MSK: - priv->reply_agg_tx_stats.underrun++; - break; - case AGG_TX_STATE_BT_PRIO_MSK: - priv->reply_agg_tx_stats.bt_prio++; - break; - case AGG_TX_STATE_FEW_BYTES_MSK: - priv->reply_agg_tx_stats.few_bytes++; - break; - case AGG_TX_STATE_ABORT_MSK: - priv->reply_agg_tx_stats.abort++; - break; - case AGG_TX_STATE_LAST_SENT_TTL_MSK: - priv->reply_agg_tx_stats.last_sent_ttl++; - break; - case AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK: - priv->reply_agg_tx_stats.last_sent_try++; - break; - case AGG_TX_STATE_LAST_SENT_BT_KILL_MSK: - priv->reply_agg_tx_stats.last_sent_bt_kill++; - break; - case AGG_TX_STATE_SCD_QUERY_MSK: - priv->reply_agg_tx_stats.scd_query++; - break; - case AGG_TX_STATE_TEST_BAD_CRC32_MSK: - priv->reply_agg_tx_stats.bad_crc32++; - break; - case AGG_TX_STATE_RESPONSE_MSK: - priv->reply_agg_tx_stats.response++; - break; - case AGG_TX_STATE_DUMP_TX_MSK: - priv->reply_agg_tx_stats.dump_tx++; - break; - case AGG_TX_STATE_DELAY_TX_MSK: - priv->reply_agg_tx_stats.delay_tx++; - break; - default: - priv->reply_agg_tx_stats.unknown++; - break; - } -} - -static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, - struct iwlagn_tx_resp *tx_resp) -{ - struct agg_tx_status *frame_status = &tx_resp->status; - int tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> - IWLAGN_TX_RES_TID_POS; - int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> - IWLAGN_TX_RES_RA_POS; - struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; - u32 status = le16_to_cpu(tx_resp->status.status); - int i; - - WARN_ON(tid == IWL_TID_NON_QOS); - - if (agg->wait_for_ba) - IWL_DEBUG_TX_REPLY(priv, - "got tx response w/o block-ack\n"); - - agg->rate_n_flags = le32_to_cpu(tx_resp->rate_n_flags); - agg->wait_for_ba = (tx_resp->frame_count > 1); - - /* - * If the BT kill count is non-zero, we'll get this - * notification again. - */ - if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); - } - - if (tx_resp->frame_count == 1) - return; - - /* Construct bit-map of pending frames within Tx window */ - for (i = 0; i < tx_resp->frame_count; i++) { - u16 fstatus = le16_to_cpu(frame_status[i].status); - - if (status & AGG_TX_STATUS_MSK) - iwlagn_count_agg_tx_err_status(priv, fstatus); - - if (status & (AGG_TX_STATE_FEW_BYTES_MSK | - AGG_TX_STATE_ABORT_MSK)) - continue; - - IWL_DEBUG_TX_REPLY(priv, "status %s (0x%08x), " - "try-count (0x%08x)\n", - iwl_get_agg_tx_fail_reason(fstatus), - fstatus & AGG_TX_STATUS_MSK, - fstatus & AGG_TX_TRY_MSK); - } -} - -#ifdef CONFIG_IWLWIFI_DEBUG -#define AGG_TX_STATE_FAIL(x) case AGG_TX_STATE_ ## x: return #x - -const char *iwl_get_agg_tx_fail_reason(u16 status) -{ - status &= AGG_TX_STATUS_MSK; - switch (status) { - case AGG_TX_STATE_TRANSMITTED: - return "SUCCESS"; - AGG_TX_STATE_FAIL(UNDERRUN_MSK); - AGG_TX_STATE_FAIL(BT_PRIO_MSK); - AGG_TX_STATE_FAIL(FEW_BYTES_MSK); - AGG_TX_STATE_FAIL(ABORT_MSK); - AGG_TX_STATE_FAIL(LAST_SENT_TTL_MSK); - AGG_TX_STATE_FAIL(LAST_SENT_TRY_CNT_MSK); - AGG_TX_STATE_FAIL(LAST_SENT_BT_KILL_MSK); - AGG_TX_STATE_FAIL(SCD_QUERY_MSK); - AGG_TX_STATE_FAIL(TEST_BAD_CRC32_MSK); - AGG_TX_STATE_FAIL(RESPONSE_MSK); - AGG_TX_STATE_FAIL(DUMP_TX_MSK); - AGG_TX_STATE_FAIL(DELAY_TX_MSK); - } - - return "UNKNOWN"; -} -#endif /* CONFIG_IWLWIFI_DEBUG */ - -static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) -{ - return le32_to_cpup((__le32 *)&tx_resp->status + - tx_resp->frame_count) & MAX_SN; -} - -static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) -{ - status &= TX_STATUS_MSK; - - switch (status) { - case TX_STATUS_POSTPONE_DELAY: - priv->reply_tx_stats.pp_delay++; - break; - case TX_STATUS_POSTPONE_FEW_BYTES: - priv->reply_tx_stats.pp_few_bytes++; - break; - case TX_STATUS_POSTPONE_BT_PRIO: - priv->reply_tx_stats.pp_bt_prio++; - break; - case TX_STATUS_POSTPONE_QUIET_PERIOD: - priv->reply_tx_stats.pp_quiet_period++; - break; - case TX_STATUS_POSTPONE_CALC_TTAK: - priv->reply_tx_stats.pp_calc_ttak++; - break; - case TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: - priv->reply_tx_stats.int_crossed_retry++; - break; - case TX_STATUS_FAIL_SHORT_LIMIT: - priv->reply_tx_stats.short_limit++; - break; - case TX_STATUS_FAIL_LONG_LIMIT: - priv->reply_tx_stats.long_limit++; - break; - case TX_STATUS_FAIL_FIFO_UNDERRUN: - priv->reply_tx_stats.fifo_underrun++; - break; - case TX_STATUS_FAIL_DRAIN_FLOW: - priv->reply_tx_stats.drain_flow++; - break; - case TX_STATUS_FAIL_RFKILL_FLUSH: - priv->reply_tx_stats.rfkill_flush++; - break; - case TX_STATUS_FAIL_LIFE_EXPIRE: - priv->reply_tx_stats.life_expire++; - break; - case TX_STATUS_FAIL_DEST_PS: - priv->reply_tx_stats.dest_ps++; - break; - case TX_STATUS_FAIL_HOST_ABORTED: - priv->reply_tx_stats.host_abort++; - break; - case TX_STATUS_FAIL_BT_RETRY: - priv->reply_tx_stats.bt_retry++; - break; - case TX_STATUS_FAIL_STA_INVALID: - priv->reply_tx_stats.sta_invalid++; - break; - case TX_STATUS_FAIL_FRAG_DROPPED: - priv->reply_tx_stats.frag_drop++; - break; - case TX_STATUS_FAIL_TID_DISABLE: - priv->reply_tx_stats.tid_disable++; - break; - case TX_STATUS_FAIL_FIFO_FLUSHED: - priv->reply_tx_stats.fifo_flush++; - break; - case TX_STATUS_FAIL_INSUFFICIENT_CF_POLL: - priv->reply_tx_stats.insuff_cf_poll++; - break; - case TX_STATUS_FAIL_PASSIVE_NO_RX: - priv->reply_tx_stats.fail_hw_drop++; - break; - case TX_STATUS_FAIL_NO_BEACON_ON_RADAR: - priv->reply_tx_stats.sta_color_mismatch++; - break; - default: - priv->reply_tx_stats.unknown++; - break; - } -} - -static void iwlagn_set_tx_status(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - struct iwlagn_tx_resp *tx_resp, - bool is_agg) -{ - u16 status = le16_to_cpu(tx_resp->status.status); - - info->status.rates[0].count = tx_resp->failure_frame + 1; - if (is_agg) - info->flags &= ~IEEE80211_TX_CTL_AMPDU; - info->flags |= iwl_tx_status_to_mac80211(status); - iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), - info); - if (!iwl_is_tx_success(status)) - iwlagn_count_tx_err_status(priv, status); -} - -static void iwl_check_abort_status(struct iwl_priv *priv, - u8 frame_count, u32 status) -{ - if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { - IWL_ERR(priv, "Tx flush command to flush out all frames\n"); - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) - queue_work(priv->workqueue, &priv->tx_flush); - } -} - -static int iwl_reclaim(struct iwl_priv *priv, int sta_id, int tid, - int txq_id, int ssn, struct sk_buff_head *skbs) -{ - if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE && - tid != IWL_TID_NON_QOS && - txq_id != priv->tid_data[sta_id][tid].agg.txq_id)) { - /* - * FIXME: this is a uCode bug which need to be addressed, - * log the information and return for now. - * Since it is can possibly happen very often and in order - * not to fill the syslog, don't use IWL_ERR or IWL_WARN - */ - IWL_DEBUG_TX_QUEUES(priv, - "Bad queue mapping txq_id=%d, agg_txq[sta:%d,tid:%d]=%d\n", - txq_id, sta_id, tid, - priv->tid_data[sta_id][tid].agg.txq_id); - return 1; - } - - iwl_trans_reclaim(priv->trans, txq_id, ssn, skbs); - return 0; -} - -int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - u16 sequence = le16_to_cpu(pkt->hdr.sequence); - int txq_id = SEQ_TO_QUEUE(sequence); - int cmd_index __maybe_unused = SEQ_TO_INDEX(sequence); - struct iwlagn_tx_resp *tx_resp = (void *)pkt->data; - struct ieee80211_hdr *hdr; - u32 status = le16_to_cpu(tx_resp->status.status); - u16 ssn = iwlagn_get_scd_ssn(tx_resp); - int tid; - int sta_id; - int freed; - struct ieee80211_tx_info *info; - struct sk_buff_head skbs; - struct sk_buff *skb; - struct iwl_rxon_context *ctx; - bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); - bool is_offchannel_skb; - - tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >> - IWLAGN_TX_RES_TID_POS; - sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> - IWLAGN_TX_RES_RA_POS; - - spin_lock(&priv->sta_lock); - - if (is_agg) - iwl_rx_reply_tx_agg(priv, tx_resp); - - __skb_queue_head_init(&skbs); - - is_offchannel_skb = false; - - if (tx_resp->frame_count == 1) { - u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); - next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); - - if (is_agg) { - /* If this is an aggregation queue, we can rely on the - * ssn since the wifi sequence number corresponds to - * the index in the TFD ring (%256). - * The seq_ctl is the sequence control of the packet - * to which this Tx response relates. But if there is a - * hole in the bitmap of the BA we received, this Tx - * response may allow to reclaim the hole and all the - * subsequent packets that were already acked. - * In that case, seq_ctl != ssn, and the next packet - * to be reclaimed will be ssn and not seq_ctl. - */ - next_reclaimed = ssn; - } - - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - - /*we can free until ssn % q.n_bd not inclusive */ - WARN_ON(iwl_reclaim(priv, sta_id, tid, txq_id, ssn, &skbs)); - iwlagn_check_ratid_empty(priv, sta_id, tid); - freed = 0; - - /* process frames */ - skb_queue_walk(&skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; - - if (!ieee80211_is_data_qos(hdr->frame_control)) - priv->last_seq_ctl = tx_resp->seq_ctl; - - info = IEEE80211_SKB_CB(skb); - ctx = info->driver_data[0]; - kmem_cache_free(iwl_tx_cmd_pool, - (info->driver_data[1])); - - memset(&info->status, 0, sizeof(info->status)); - - if (status == TX_STATUS_FAIL_PASSIVE_NO_RX && - iwl_is_associated_ctx(ctx) && ctx->vif && - ctx->vif->type == NL80211_IFTYPE_STATION) { - /* block and stop all queues */ - priv->passive_no_rx = true; - IWL_DEBUG_TX_QUEUES(priv, "stop all queues: " - "passive channel"); - ieee80211_stop_queues(priv->hw); - - IWL_DEBUG_TX_REPLY(priv, - "TXQ %d status %s (0x%08x) " - "rate_n_flags 0x%x retries %d\n", - txq_id, - iwl_get_tx_fail_reason(status), - status, - le32_to_cpu(tx_resp->rate_n_flags), - tx_resp->failure_frame); - - IWL_DEBUG_TX_REPLY(priv, - "FrameCnt = %d, idx=%d\n", - tx_resp->frame_count, cmd_index); - } - - /* check if BAR is needed */ - if (is_agg && !iwl_is_tx_success(status)) - info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; - iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), - tx_resp, is_agg); - if (!is_agg) - iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); - - is_offchannel_skb = - (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); - freed++; - } - - WARN_ON(!is_agg && freed != 1); - - /* - * An offchannel frame can be send only on the AUX queue, where - * there is no aggregation (and reordering) so it only is single - * skb is expected to be processed. - */ - WARN_ON(is_offchannel_skb && freed != 1); - } - - iwl_check_abort_status(priv, tx_resp->frame_count, status); - spin_unlock(&priv->sta_lock); - - while (!skb_queue_empty(&skbs)) { - skb = __skb_dequeue(&skbs); - ieee80211_tx_status(priv->hw, skb); - } - - if (is_offchannel_skb) - iwl_scan_offchannel_skb_status(priv); - - return 0; -} - -/** - * iwlagn_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA - * - * Handles block-acknowledge notification from device, which reports success - * of frames sent via aggregation. - */ -int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; - struct iwl_ht_agg *agg; - struct sk_buff_head reclaimed_skbs; - struct ieee80211_tx_info *info; - struct ieee80211_hdr *hdr; - struct sk_buff *skb; - int sta_id; - int tid; - int freed; - - /* "flow" corresponds to Tx queue */ - u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); - - /* "ssn" is start of block-ack Tx window, corresponds to index - * (in Tx queue's circular buffer) of first TFD/frame in window */ - u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); - - if (scd_flow >= priv->cfg->base_params->num_of_queues) { - IWL_ERR(priv, - "BUG_ON scd_flow is bigger than number of queues\n"); - return 0; - } - - sta_id = ba_resp->sta_id; - tid = ba_resp->tid; - agg = &priv->tid_data[sta_id][tid].agg; - - spin_lock(&priv->sta_lock); - - if (unlikely(!agg->wait_for_ba)) { - if (unlikely(ba_resp->bitmap)) - IWL_ERR(priv, "Received BA when not expected\n"); - spin_unlock(&priv->sta_lock); - return 0; - } - - __skb_queue_head_init(&reclaimed_skbs); - - /* Release all TFDs before the SSN, i.e. all TFDs in front of - * block-ack window (we assume that they've been successfully - * transmitted ... if not, it's too late anyway). */ - if (iwl_reclaim(priv, sta_id, tid, scd_flow, - ba_resp_scd_ssn, &reclaimed_skbs)) { - spin_unlock(&priv->sta_lock); - return 0; - } - - IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " - "sta_id = %d\n", - agg->wait_for_ba, - (u8 *) &ba_resp->sta_addr_lo32, - ba_resp->sta_id); - IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " - "scd_flow = %d, scd_ssn = %d sent:%d, acked:%d\n", - ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), - (unsigned long long)le64_to_cpu(ba_resp->bitmap), - scd_flow, ba_resp_scd_ssn, ba_resp->txed, - ba_resp->txed_2_done); - - /* Mark that the expected block-ack response arrived */ - agg->wait_for_ba = false; - - /* Sanity check values reported by uCode */ - if (ba_resp->txed_2_done > ba_resp->txed) { - IWL_DEBUG_TX_REPLY(priv, - "bogus sent(%d) and ack(%d) count\n", - ba_resp->txed, ba_resp->txed_2_done); - /* - * set txed_2_done = txed, - * so it won't impact rate scale - */ - ba_resp->txed = ba_resp->txed_2_done; - } - - priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; - - iwlagn_check_ratid_empty(priv, sta_id, tid); - freed = 0; - - skb_queue_walk(&reclaimed_skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; - - if (ieee80211_is_data_qos(hdr->frame_control)) - freed++; - else - WARN_ON_ONCE(1); - - info = IEEE80211_SKB_CB(skb); - kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1])); - - if (freed == 1) { - /* this is the first skb we deliver in this batch */ - /* put the rate scaling data there */ - info = IEEE80211_SKB_CB(skb); - memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; - info->flags |= IEEE80211_TX_STAT_AMPDU; - info->status.ampdu_ack_len = ba_resp->txed_2_done; - info->status.ampdu_len = ba_resp->txed; - iwlagn_hwrate_to_tx_control(priv, agg->rate_n_flags, - info); - } - } - - spin_unlock(&priv->sta_lock); - - while (!skb_queue_empty(&reclaimed_skbs)) { - skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status(priv->hw, skb); - } - - return 0; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c deleted file mode 100644 index fa007176bad2..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ /dev/null @@ -1,2370 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn-calib.h" -#include "iwl-agn.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-drv.h" -#include "iwl-modparams.h" - -/****************************************************************************** - * - * module boiler plate - * - ******************************************************************************/ - -/* - * module name, copyright, version, etc. - */ -#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" - -#ifdef CONFIG_IWLWIFI_DEBUG -#define VD "d" -#else -#define VD -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD - - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - -void iwl_update_chain_flags(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - - for_each_context(priv, ctx) { - iwlagn_set_rxon_chain(priv, ctx); - if (ctx->active.rx_chain != ctx->staging.rx_chain) - iwlagn_commit_rxon(priv, ctx); - } -} - -/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ -static void iwl_set_beacon_tim(struct iwl_priv *priv, - struct iwl_tx_beacon_cmd *tx_beacon_cmd, - u8 *beacon, u32 frame_size) -{ - u16 tim_idx; - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; - - /* - * The index is relative to frame start but we start looking at the - * variable-length part of the beacon. - */ - tim_idx = mgmt->u.beacon.variable - beacon; - - /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ - while ((tim_idx < (frame_size - 2)) && - (beacon[tim_idx] != WLAN_EID_TIM)) - tim_idx += beacon[tim_idx+1] + 2; - - /* If TIM field was found, set variables */ - if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { - tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); - tx_beacon_cmd->tim_size = beacon[tim_idx+1]; - } else - IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); -} - -int iwlagn_send_beacon_cmd(struct iwl_priv *priv) -{ - struct iwl_tx_beacon_cmd *tx_beacon_cmd; - struct iwl_host_cmd cmd = { - .id = REPLY_TX_BEACON, - .flags = CMD_SYNC, - }; - struct ieee80211_tx_info *info; - u32 frame_size; - u32 rate_flags; - u32 rate; - - /* - * We have to set up the TX command, the TX Beacon command, and the - * beacon contents. - */ - - lockdep_assert_held(&priv->mutex); - - if (!priv->beacon_ctx) { - IWL_ERR(priv, "trying to build beacon w/o beacon context!\n"); - return 0; - } - - if (WARN_ON(!priv->beacon_skb)) - return -EINVAL; - - /* Allocate beacon command */ - if (!priv->beacon_cmd) - priv->beacon_cmd = kzalloc(sizeof(*tx_beacon_cmd), GFP_KERNEL); - tx_beacon_cmd = priv->beacon_cmd; - if (!tx_beacon_cmd) - return -ENOMEM; - - frame_size = priv->beacon_skb->len; - - /* Set up TX command fields */ - tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); - tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | - TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; - - /* Set up TX beacon command fields */ - iwl_set_beacon_tim(priv, tx_beacon_cmd, priv->beacon_skb->data, - frame_size); - - /* Set up packet rate and flags */ - info = IEEE80211_SKB_CB(priv->beacon_skb); - - /* - * Let's set up the rate at least somewhat correctly; - * it will currently not actually be used by the uCode, - * it uses the broadcast station's rate instead. - */ - if (info->control.rates[0].idx < 0 || - info->control.rates[0].flags & IEEE80211_TX_RC_MCS) - rate = 0; - else - rate = info->control.rates[0].idx; - - priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - priv->hw_params.valid_tx_ant); - rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); - - /* In mac80211, rates for 5 GHz start at 0 */ - if (info->band == IEEE80211_BAND_5GHZ) - rate += IWL_FIRST_OFDM_RATE; - else if (rate >= IWL_FIRST_CCK_RATE && rate <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, rate_flags); - - /* Submit command */ - cmd.len[0] = sizeof(*tx_beacon_cmd); - cmd.data[0] = tx_beacon_cmd; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - cmd.len[1] = frame_size; - cmd.data[1] = priv->beacon_skb->data; - cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; - - return iwl_dvm_send_cmd(priv, &cmd); -} - -static void iwl_bg_beacon_update(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, beacon_update); - struct sk_buff *beacon; - - mutex_lock(&priv->mutex); - if (!priv->beacon_ctx) { - IWL_ERR(priv, "updating beacon w/o beacon context!\n"); - goto out; - } - - if (priv->beacon_ctx->vif->type != NL80211_IFTYPE_AP) { - /* - * The ucode will send beacon notifications even in - * IBSS mode, but we don't want to process them. But - * we need to defer the type check to here due to - * requiring locking around the beacon_ctx access. - */ - goto out; - } - - /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ - beacon = ieee80211_beacon_get(priv->hw, priv->beacon_ctx->vif); - if (!beacon) { - IWL_ERR(priv, "update beacon failed -- keeping old\n"); - goto out; - } - - /* new beacon skb is allocated every time; dispose previous.*/ - dev_kfree_skb(priv->beacon_skb); - - priv->beacon_skb = beacon; - - iwlagn_send_beacon_cmd(priv); - out: - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_bt_runtime_config(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, bt_runtime_config); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* dont send host command if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - return; - iwlagn_send_advance_bt_config(priv); -} - -static void iwl_bg_bt_full_concurrency(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, bt_full_concurrency); - struct iwl_rxon_context *ctx; - - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - goto out; - - /* dont send host command if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - goto out; - - IWL_DEBUG_INFO(priv, "BT coex in %s mode\n", - priv->bt_full_concurrent ? - "full concurrency" : "3-wire"); - - /* - * LQ & RXON updated cmds must be sent before BT Config cmd - * to avoid 3-wire collisions - */ - for_each_context(priv, ctx) { - iwlagn_set_rxon_chain(priv, ctx); - iwlagn_commit_rxon(priv, ctx); - } - - iwlagn_send_advance_bt_config(priv); -out: - mutex_unlock(&priv->mutex); -} - -int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) -{ - struct iwl_statistics_cmd statistics_cmd = { - .configuration_flags = - clear ? IWL_STATS_CONF_CLEAR_STATS : 0, - }; - - if (flags & CMD_ASYNC) - return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, - CMD_ASYNC, - sizeof(struct iwl_statistics_cmd), - &statistics_cmd); - else - return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, - CMD_SYNC, - sizeof(struct iwl_statistics_cmd), - &statistics_cmd); -} - -/** - * iwl_bg_statistics_periodic - Timer callback to queue statistics - * - * This callback is provided in order to send a statistics request. - * - * This timer function is continually reset to execute within - * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION - * was received. We need to ensure we receive the statistics in order - * to update the temperature used for calibrating the TXPOWER. - */ -static void iwl_bg_statistics_periodic(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* dont send host command if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - return; - - iwl_send_statistics_request(priv, CMD_ASYNC, false); -} - - -static void iwl_print_cont_event_trace(struct iwl_priv *priv, u32 base, - u32 start_idx, u32 num_events, - u32 capacity, u32 mode) -{ - u32 i; - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - unsigned long reg_flags; - - if (mode == 0) - ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); - else - ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); - - /* Make sure device is powered up for SRAM reads */ - spin_lock_irqsave(&priv->trans->reg_lock, reg_flags); - if (unlikely(!iwl_grab_nic_access(priv->trans))) { - spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags); - return; - } - - /* Set starting address; reads will auto-increment */ - iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, ptr); - - /* - * Refuse to read more than would have fit into the log from - * the current start_idx. This used to happen due to the race - * described below, but now WARN because the code below should - * prevent it from happening here. - */ - if (WARN_ON(num_events > capacity - start_idx)) - num_events = capacity - start_idx; - - /* - * "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. - */ - for (i = 0; i < num_events; i++) { - ev = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - time = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - if (mode == 0) { - trace_iwlwifi_dev_ucode_cont_event( - priv->trans->dev, 0, time, ev); - } else { - data = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - trace_iwlwifi_dev_ucode_cont_event( - priv->trans->dev, time, data, ev); - } - } - /* Allow device to power down */ - iwl_release_nic_access(priv->trans); - spin_unlock_irqrestore(&priv->trans->reg_lock, reg_flags); -} - -static void iwl_continuous_event_trace(struct iwl_priv *priv) -{ - u32 capacity; /* event log capacity in # entries */ - struct { - u32 capacity; - u32 mode; - u32 wrap_counter; - u32 write_counter; - } __packed read; - u32 base; /* SRAM byte address of event log header */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - - base = priv->device_pointers.log_event_table; - if (iwlagn_hw_valid_rtc_data_addr(base)) { - iwl_read_targ_mem_words(priv->trans, base, &read, sizeof(read)); - capacity = read.capacity; - mode = read.mode; - num_wraps = read.wrap_counter; - next_entry = read.write_counter; - } else - return; - - /* - * Unfortunately, the uCode doesn't use temporary variables. - * Therefore, it can happen that we read next_entry == capacity, - * which really means next_entry == 0. - */ - if (unlikely(next_entry == capacity)) - next_entry = 0; - /* - * Additionally, the uCode increases the write pointer before - * the wraps counter, so if the write pointer is smaller than - * the old write pointer (wrap occurred) but we read that no - * wrap occurred, we actually read between the next_entry and - * num_wraps update (this does happen in practice!!) -- take - * that into account by increasing num_wraps. - */ - if (unlikely(next_entry < priv->event_log.next_entry && - num_wraps == priv->event_log.num_wraps)) - num_wraps++; - - if (num_wraps == priv->event_log.num_wraps) { - iwl_print_cont_event_trace( - priv, base, priv->event_log.next_entry, - next_entry - priv->event_log.next_entry, - capacity, mode); - - priv->event_log.non_wraps_count++; - } else { - if (num_wraps - priv->event_log.num_wraps > 1) - priv->event_log.wraps_more_count++; - else - priv->event_log.wraps_once_count++; - - trace_iwlwifi_dev_ucode_wrap_event(priv->trans->dev, - num_wraps - priv->event_log.num_wraps, - next_entry, priv->event_log.next_entry); - - if (next_entry < priv->event_log.next_entry) { - iwl_print_cont_event_trace( - priv, base, priv->event_log.next_entry, - capacity - priv->event_log.next_entry, - capacity, mode); - - iwl_print_cont_event_trace( - priv, base, 0, next_entry, capacity, mode); - } else { - iwl_print_cont_event_trace( - priv, base, next_entry, - capacity - next_entry, - capacity, mode); - - iwl_print_cont_event_trace( - priv, base, 0, next_entry, capacity, mode); - } - } - - priv->event_log.num_wraps = num_wraps; - priv->event_log.next_entry = next_entry; -} - -/** - * iwl_bg_ucode_trace - Timer callback to log ucode event - * - * The timer is continually set to execute every - * UCODE_TRACE_PERIOD milliseconds after the last timer expired - * this function is to perform continuous uCode event logging operation - * if enabled - */ -static void iwl_bg_ucode_trace(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (priv->event_log.ucode_trace) { - iwl_continuous_event_trace(priv); - /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ - mod_timer(&priv->ucode_trace, - jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); - } -} - -static void iwl_bg_tx_flush(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, tx_flush); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* do nothing if rf-kill is on */ - if (!iwl_is_ready_rf(priv)) - return; - - IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); - iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); -} - -/* - * queue/FIFO/AC mapping definitions - */ - -#define IWL_TX_FIFO_BK 0 /* shared */ -#define IWL_TX_FIFO_BE 1 -#define IWL_TX_FIFO_VI 2 /* shared */ -#define IWL_TX_FIFO_VO 3 -#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK -#define IWL_TX_FIFO_BE_IPAN 4 -#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI -#define IWL_TX_FIFO_VO_IPAN 5 -/* re-uses the VO FIFO, uCode will properly flush/schedule */ -#define IWL_TX_FIFO_AUX 5 -#define IWL_TX_FIFO_UNUSED -1 - -#define IWLAGN_CMD_FIFO_NUM 7 - -/* - * This queue number is required for proper operation - * because the ucode will stop/start the scheduler as - * required. - */ -#define IWL_IPAN_MCAST_QUEUE 8 - -static const u8 iwlagn_default_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, - IWLAGN_CMD_FIFO_NUM, -}; - -static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, - IWL_TX_FIFO_BK_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_VI_IPAN, - IWL_TX_FIFO_VO_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWLAGN_CMD_FIFO_NUM, - IWL_TX_FIFO_AUX, -}; - -static const u8 iwlagn_bss_ac_to_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, -}; - -static const u8 iwlagn_bss_ac_to_queue[] = { - 0, 1, 2, 3, -}; - -static const u8 iwlagn_pan_ac_to_fifo[] = { - IWL_TX_FIFO_VO_IPAN, - IWL_TX_FIFO_VI_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_BK_IPAN, -}; - -static const u8 iwlagn_pan_ac_to_queue[] = { - 7, 6, 5, 4, -}; - -static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) -{ - int i; - - /* - * The default context is always valid, - * the PAN context depends on uCode. - */ - priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); - if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) - priv->valid_contexts |= BIT(IWL_RXON_CTX_PAN); - - for (i = 0; i < NUM_IWL_RXON_CTX; i++) - priv->contexts[i].ctxid = i; - - priv->contexts[IWL_RXON_CTX_BSS].always_active = true; - priv->contexts[IWL_RXON_CTX_BSS].is_active = true; - priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON; - priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING; - priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC; - priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM; - priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID; - priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY; - priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; - priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes = - BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MONITOR); - priv->contexts[IWL_RXON_CTX_BSS].interface_modes = - BIT(NL80211_IFTYPE_STATION); - priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP; - priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS; - priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS; - priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS; - memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue, - iwlagn_bss_ac_to_queue, sizeof(iwlagn_bss_ac_to_queue)); - memcpy(priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo, - iwlagn_bss_ac_to_fifo, sizeof(iwlagn_bss_ac_to_fifo)); - - priv->contexts[IWL_RXON_CTX_PAN].rxon_cmd = REPLY_WIPAN_RXON; - priv->contexts[IWL_RXON_CTX_PAN].rxon_timing_cmd = - REPLY_WIPAN_RXON_TIMING; - priv->contexts[IWL_RXON_CTX_PAN].rxon_assoc_cmd = - REPLY_WIPAN_RXON_ASSOC; - priv->contexts[IWL_RXON_CTX_PAN].qos_cmd = REPLY_WIPAN_QOS_PARAM; - priv->contexts[IWL_RXON_CTX_PAN].ap_sta_id = IWL_AP_ID_PAN; - priv->contexts[IWL_RXON_CTX_PAN].wep_key_cmd = REPLY_WIPAN_WEPKEY; - priv->contexts[IWL_RXON_CTX_PAN].bcast_sta_id = IWLAGN_PAN_BCAST_ID; - priv->contexts[IWL_RXON_CTX_PAN].station_flags = STA_FLG_PAN_STATION; - priv->contexts[IWL_RXON_CTX_PAN].interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); - - if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P) - priv->contexts[IWL_RXON_CTX_PAN].interface_modes |= - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); - - priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP; - priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA; - priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P; - memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_queue, - iwlagn_pan_ac_to_queue, sizeof(iwlagn_pan_ac_to_queue)); - memcpy(priv->contexts[IWL_RXON_CTX_PAN].ac_to_fifo, - iwlagn_pan_ac_to_fifo, sizeof(iwlagn_pan_ac_to_fifo)); - priv->contexts[IWL_RXON_CTX_PAN].mcast_queue = IWL_IPAN_MCAST_QUEUE; - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); -} - -static void iwl_rf_kill_ct_config(struct iwl_priv *priv) -{ - struct iwl_ct_kill_config cmd; - struct iwl_ct_kill_throttling_config adv_cmd; - int ret = 0; - - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - - priv->thermal_throttle.ct_kill_toggle = false; - - if (priv->cfg->base_params->support_ct_kill_exit) { - adv_cmd.critical_temperature_enter = - cpu_to_le32(priv->hw_params.ct_kill_threshold); - adv_cmd.critical_temperature_exit = - cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); - - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_CT_KILL_CONFIG_CMD, - CMD_SYNC, sizeof(adv_cmd), &adv_cmd); - if (ret) - IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); - else - IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " - "succeeded, critical temperature enter is %d," - "exit is %d\n", - priv->hw_params.ct_kill_threshold, - priv->hw_params.ct_kill_exit_threshold); - } else { - cmd.critical_temperature_R = - cpu_to_le32(priv->hw_params.ct_kill_threshold); - - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_CT_KILL_CONFIG_CMD, - CMD_SYNC, sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); - else - IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " - "succeeded, " - "critical temperature is %d\n", - priv->hw_params.ct_kill_threshold); - } -} - -static int iwlagn_send_calib_cfg_rt(struct iwl_priv *priv, u32 cfg) -{ - struct iwl_calib_cfg_cmd calib_cfg_cmd; - struct iwl_host_cmd cmd = { - .id = CALIBRATION_CFG_CMD, - .len = { sizeof(struct iwl_calib_cfg_cmd), }, - .data = { &calib_cfg_cmd, }, - }; - - memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); - calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_RT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.once.start = cpu_to_le32(cfg); - - return iwl_dvm_send_cmd(priv, &cmd); -} - - -static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) -{ - struct iwl_tx_ant_config_cmd tx_ant_cmd = { - .valid = cpu_to_le32(valid_tx_ant), - }; - - if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { - IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_dvm_send_cmd_pdu(priv, - TX_ANT_CONFIGURATION_CMD, - CMD_SYNC, - sizeof(struct iwl_tx_ant_config_cmd), - &tx_ant_cmd); - } else { - IWL_DEBUG_HC(priv, "TX_ANT_CONFIGURATION_CMD not supported\n"); - return -EOPNOTSUPP; - } -} - -static void iwl_send_bt_config(struct iwl_priv *priv) -{ - struct iwl_bt_cmd bt_cmd = { - .lead_time = BT_LEAD_TIME_DEF, - .max_kill = BT_MAX_KILL_DEF, - .kill_ack_mask = 0, - .kill_cts_mask = 0, - }; - - if (!iwlwifi_mod_params.bt_coex_active) - bt_cmd.flags = BT_COEX_DISABLE; - else - bt_cmd.flags = BT_COEX_ENABLE; - - priv->bt_enable_flag = bt_cmd.flags; - IWL_DEBUG_INFO(priv, "BT coex %s\n", - (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); - - if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd)) - IWL_ERR(priv, "failed to send BT Coex Config\n"); -} - -/** - * iwl_alive_start - called after REPLY_ALIVE notification received - * from protocol/runtime uCode (initialization uCode's - * Alive gets handled by iwl_init_alive_start()). - */ -int iwl_alive_start(struct iwl_priv *priv) -{ - int ret = 0; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); - - /* After the ALIVE response, we can send host commands to the uCode */ - set_bit(STATUS_ALIVE, &priv->status); - - if (iwl_is_rfkill(priv)) - return -ERFKILL; - - if (priv->event_log.ucode_trace) { - /* start collecting data now */ - mod_timer(&priv->ucode_trace, jiffies); - } - - /* download priority table before any calibration request */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - /* Configure Bluetooth device coexistence support */ - if (priv->cfg->bt_params->bt_sco_disable) - priv->bt_enable_pspoll = false; - else - priv->bt_enable_pspoll = true; - - priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; - priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; - priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; - iwlagn_send_advance_bt_config(priv); - priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS; - priv->cur_rssi_ctx = NULL; - - iwl_send_prio_tbl(priv); - - /* FIXME: w/a to force change uCode BT state machine */ - ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - } else { - /* - * default is 2-wire BT coexexistence support - */ - iwl_send_bt_config(priv); - } - - /* - * Perform runtime calibrations, including DC calibration. - */ - iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX); - - ieee80211_wake_queues(priv->hw); - - /* Configure Tx antenna selection based on H/W config */ - iwlagn_send_tx_ant_config(priv, priv->hw_params.valid_tx_ant); - - if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { - struct iwl_rxon_cmd *active_rxon = - (struct iwl_rxon_cmd *)&ctx->active; - /* apply any changes in staging */ - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - } else { - struct iwl_rxon_context *tmp; - /* Initialize our rx_config data */ - for_each_context(priv, tmp) - iwl_connection_init_rx_config(priv, tmp); - - iwlagn_set_rxon_chain(priv, ctx); - } - - if (!priv->wowlan) { - /* WoWLAN ucode will not reply in the same way, skip it */ - iwl_reset_run_time_calib(priv); - } - - set_bit(STATUS_READY, &priv->status); - - /* Configure the adapter for unassociated operation */ - ret = iwlagn_commit_rxon(priv, ctx); - if (ret) - return ret; - - /* At this point, the NIC is initialized and operational */ - iwl_rf_kill_ct_config(priv); - - IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); - - return iwl_power_update_mode(priv, true); -} - -/** - * iwl_clear_driver_stations - clear knowledge of all stations from driver - * @priv: iwl priv struct - * - * This is called during iwl_down() to make sure that in the case - * we're coming there from a hardware restart mac80211 will be - * able to reconfigure stations -- if we're getting there in the - * normal down flow then the stations will already be cleared. - */ -static void iwl_clear_driver_stations(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - - spin_lock_bh(&priv->sta_lock); - memset(priv->stations, 0, sizeof(priv->stations)); - priv->num_stations = 0; - - priv->ucode_key_table = 0; - - for_each_context(priv, ctx) { - /* - * Remove all key information that is not stored as part - * of station information since mac80211 may not have had - * a chance to remove all the keys. When device is - * reconfigured by mac80211 after an error all keys will - * be reconfigured. - */ - memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys)); - ctx->key_mapping_keys = 0; - } - - spin_unlock_bh(&priv->sta_lock); -} - -void iwl_down(struct iwl_priv *priv) -{ - int exit_pending; - - IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); - - lockdep_assert_held(&priv->mutex); - - iwl_scan_cancel_timeout(priv, 200); - - /* - * If active, scanning won't cancel it, so say it expired. - * No race since we hold the mutex here and a new one - * can't come in at this time. - */ - ieee80211_remain_on_channel_expired(priv->hw); - - exit_pending = - test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); - - iwl_clear_ucode_stations(priv, NULL); - iwl_dealloc_bcast_stations(priv); - iwl_clear_driver_stations(priv); - - /* reset BT coex data */ - priv->bt_status = 0; - priv->cur_rssi_ctx = NULL; - priv->bt_is_sco = 0; - if (priv->cfg->bt_params) - priv->bt_traffic_load = - priv->cfg->bt_params->bt_init_traffic_load; - else - priv->bt_traffic_load = 0; - priv->bt_full_concurrent = false; - priv->bt_ci_compliance = 0; - - /* Wipe out the EXIT_PENDING status bit if we are not actually - * exiting the module */ - if (!exit_pending) - clear_bit(STATUS_EXIT_PENDING, &priv->status); - - if (priv->mac80211_registered) - ieee80211_stop_queues(priv->hw); - - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - - /* Set num_aux_in_flight must be done after the transport is stopped */ - atomic_set(&priv->num_aux_in_flight, 0); - - /* Clear out all status bits but a few that are stable across reset */ - priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << - STATUS_RF_KILL_HW | - test_bit(STATUS_GEO_CONFIGURED, &priv->status) << - STATUS_GEO_CONFIGURED | - test_bit(STATUS_FW_ERROR, &priv->status) << - STATUS_FW_ERROR | - test_bit(STATUS_EXIT_PENDING, &priv->status) << - STATUS_EXIT_PENDING; - - dev_kfree_skb(priv->beacon_skb); - priv->beacon_skb = NULL; -} - -/***************************************************************************** - * - * Workqueue callbacks - * - *****************************************************************************/ - -static void iwl_bg_run_time_calib_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, - run_time_calib_work); - - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - test_bit(STATUS_SCANNING, &priv->status)) { - mutex_unlock(&priv->mutex); - return; - } - - if (priv->start_calib) { - iwl_chain_noise_calibration(priv); - iwl_sensitivity_calibration(priv); - } - - mutex_unlock(&priv->mutex); -} - -void iwlagn_prepare_restart(struct iwl_priv *priv) -{ - bool bt_full_concurrent; - u8 bt_ci_compliance; - u8 bt_load; - u8 bt_status; - bool bt_is_sco; - int i; - - lockdep_assert_held(&priv->mutex); - - priv->is_open = 0; - - /* - * __iwl_down() will clear the BT status variables, - * which is correct, but when we restart we really - * want to keep them so restore them afterwards. - * - * The restart process will later pick them up and - * re-configure the hw when we reconfigure the BT - * command. - */ - bt_full_concurrent = priv->bt_full_concurrent; - bt_ci_compliance = priv->bt_ci_compliance; - bt_load = priv->bt_traffic_load; - bt_status = priv->bt_status; - bt_is_sco = priv->bt_is_sco; - - iwl_down(priv); - - priv->bt_full_concurrent = bt_full_concurrent; - priv->bt_ci_compliance = bt_ci_compliance; - priv->bt_traffic_load = bt_load; - priv->bt_status = bt_status; - priv->bt_is_sco = bt_is_sco; - - /* reset aggregation queues */ - for (i = IWLAGN_FIRST_AMPDU_QUEUE; i < IWL_MAX_HW_QUEUES; i++) - priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; - /* and stop counts */ - for (i = 0; i < IWL_MAX_HW_QUEUES; i++) - atomic_set(&priv->queue_stop_count[i], 0); - - memset(priv->agg_q_alloc, 0, sizeof(priv->agg_q_alloc)); -} - -static void iwl_bg_restart(struct work_struct *data) -{ - struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) { - mutex_lock(&priv->mutex); - iwlagn_prepare_restart(priv); - mutex_unlock(&priv->mutex); - iwl_cancel_deferred_work(priv); - ieee80211_restart_hw(priv->hw); - } else { - WARN_ON(1); - } -} - - - - -void iwlagn_disable_roc(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; - - lockdep_assert_held(&priv->mutex); - - if (!priv->hw_roc_setup) - return; - - ctx->staging.dev_type = RXON_DEV_TYPE_P2P; - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - priv->hw_roc_channel = NULL; - - memset(ctx->staging.node_addr, 0, ETH_ALEN); - - iwlagn_commit_rxon(priv, ctx); - - ctx->is_active = false; - priv->hw_roc_setup = false; -} - -static void iwlagn_disable_roc_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, - hw_roc_disable_work.work); - - mutex_lock(&priv->mutex); - iwlagn_disable_roc(priv); - mutex_unlock(&priv->mutex); -} - -/***************************************************************************** - * - * driver setup and teardown - * - *****************************************************************************/ - -static void iwl_setup_deferred_work(struct iwl_priv *priv) -{ - priv->workqueue = create_singlethread_workqueue(DRV_NAME); - - INIT_WORK(&priv->restart, iwl_bg_restart); - INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); - INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); - INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush); - INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency); - INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config); - INIT_DELAYED_WORK(&priv->hw_roc_disable_work, - iwlagn_disable_roc_work); - - iwl_setup_scan_deferred_work(priv); - - if (priv->cfg->bt_params) - iwlagn_bt_setup_deferred_work(priv); - - init_timer(&priv->statistics_periodic); - priv->statistics_periodic.data = (unsigned long)priv; - priv->statistics_periodic.function = iwl_bg_statistics_periodic; - - init_timer(&priv->ucode_trace); - priv->ucode_trace.data = (unsigned long)priv; - priv->ucode_trace.function = iwl_bg_ucode_trace; -} - -void iwl_cancel_deferred_work(struct iwl_priv *priv) -{ - if (priv->cfg->bt_params) - iwlagn_bt_cancel_deferred_work(priv); - - cancel_work_sync(&priv->run_time_calib_work); - cancel_work_sync(&priv->beacon_update); - - iwl_cancel_scan_deferred_work(priv); - - cancel_work_sync(&priv->bt_full_concurrency); - cancel_work_sync(&priv->bt_runtime_config); - cancel_delayed_work_sync(&priv->hw_roc_disable_work); - - del_timer_sync(&priv->statistics_periodic); - del_timer_sync(&priv->ucode_trace); -} - -static void iwl_init_hw_rates(struct ieee80211_rate *rates) -{ - int i; - - for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) { - rates[i].bitrate = iwl_rates[i].ieee * 5; - rates[i].hw_value = i; /* Rate scaling will work on indexes */ - rates[i].hw_value_short = i; - rates[i].flags = 0; - if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) { - /* - * If CCK != 1M then set short preamble rate flag. - */ - rates[i].flags |= - (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ? - 0 : IEEE80211_RATE_SHORT_PREAMBLE; - } - } -} - -#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ -#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ -static void iwl_init_ht_hw_capab(const struct iwl_priv *priv, - struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band) -{ - u16 max_bit_rate = 0; - u8 rx_chains_num = priv->hw_params.rx_chains_num; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - - ht_info->cap = 0; - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - - ht_info->ht_supported = true; - - if (priv->cfg->ht_params && - priv->cfg->ht_params->ht_greenfield_support) - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (priv->hw_params.ht40_channel & BIT(band)) { - ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - ht_info->mcs.rx_mask[4] = 0x01; - max_bit_rate = MAX_BIT_RATE_40_MHZ; - } - - if (iwlwifi_mod_params.amsdu_size_8K) - ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; - - ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; - ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; - - ht_info->mcs.rx_mask[0] = 0xFF; - if (rx_chains_num >= 2) - ht_info->mcs.rx_mask[1] = 0xFF; - if (rx_chains_num >= 3) - ht_info->mcs.rx_mask[2] = 0xFF; - - /* Highest supported Rx data rate */ - max_bit_rate *= rx_chains_num; - WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); - ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); - - /* Tx MCS capabilities */ - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - if (tx_chains_num != rx_chains_num) { - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_chains_num - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } -} - -/** - * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom - */ -static int iwl_init_geos(struct iwl_priv *priv) -{ - struct iwl_channel_info *ch; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channels; - struct ieee80211_channel *geo_ch; - struct ieee80211_rate *rates; - int i = 0; - s8 max_tx_power = IWLAGN_TX_POWER_TARGET_POWER_MIN; - - if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates || - priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) { - IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n"); - set_bit(STATUS_GEO_CONFIGURED, &priv->status); - return 0; - } - - channels = kcalloc(priv->channel_count, - sizeof(struct ieee80211_channel), GFP_KERNEL); - if (!channels) - return -ENOMEM; - - rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate), - GFP_KERNEL); - if (!rates) { - kfree(channels); - return -ENOMEM; - } - - /* 5.2GHz channels start after the 2.4GHz channels */ - sband = &priv->bands[IEEE80211_BAND_5GHZ]; - sband->channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)]; - /* just OFDM */ - sband->bitrates = &rates[IWL_FIRST_OFDM_RATE]; - sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE; - - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) - iwl_init_ht_hw_capab(priv, &sband->ht_cap, - IEEE80211_BAND_5GHZ); - - sband = &priv->bands[IEEE80211_BAND_2GHZ]; - sband->channels = channels; - /* OFDM & CCK */ - sband->bitrates = rates; - sband->n_bitrates = IWL_RATE_COUNT_LEGACY; - - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) - iwl_init_ht_hw_capab(priv, &sband->ht_cap, - IEEE80211_BAND_2GHZ); - - priv->ieee_channels = channels; - priv->ieee_rates = rates; - - for (i = 0; i < priv->channel_count; i++) { - ch = &priv->channel_info[i]; - - /* FIXME: might be removed if scan is OK */ - if (!is_channel_valid(ch)) - continue; - - sband = &priv->bands[ch->band]; - - geo_ch = &sband->channels[sband->n_channels++]; - - geo_ch->center_freq = - ieee80211_channel_to_frequency(ch->channel, ch->band); - geo_ch->max_power = ch->max_power_avg; - geo_ch->max_antenna_gain = 0xff; - geo_ch->hw_value = ch->channel; - - if (is_channel_valid(ch)) { - if (!(ch->flags & EEPROM_CHANNEL_IBSS)) - geo_ch->flags |= IEEE80211_CHAN_NO_IBSS; - - if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) - geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN; - - if (ch->flags & EEPROM_CHANNEL_RADAR) - geo_ch->flags |= IEEE80211_CHAN_RADAR; - - geo_ch->flags |= ch->ht40_extension_channel; - - if (ch->max_power_avg > max_tx_power) - max_tx_power = ch->max_power_avg; - } else { - geo_ch->flags |= IEEE80211_CHAN_DISABLED; - } - - IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", - ch->channel, geo_ch->center_freq, - is_channel_a_band(ch) ? "5.2" : "2.4", - geo_ch->flags & IEEE80211_CHAN_DISABLED ? - "restricted" : "valid", - geo_ch->flags); - } - - priv->tx_power_device_lmt = max_tx_power; - priv->tx_power_user_lmt = max_tx_power; - priv->tx_power_next = max_tx_power; - - if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && - priv->hw_params.sku & EEPROM_SKU_CAP_BAND_52GHZ) { - IWL_INFO(priv, "Incorrectly detected BG card as ABG. " - "Please send your %s to maintainer.\n", - priv->trans->hw_id_str); - priv->hw_params.sku &= ~EEPROM_SKU_CAP_BAND_52GHZ; - } - - if (iwlwifi_mod_params.disable_5ghz) - priv->bands[IEEE80211_BAND_5GHZ].n_channels = 0; - - IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n", - priv->bands[IEEE80211_BAND_2GHZ].n_channels, - priv->bands[IEEE80211_BAND_5GHZ].n_channels); - - set_bit(STATUS_GEO_CONFIGURED, &priv->status); - - return 0; -} - -/* - * iwl_free_geos - undo allocations in iwl_init_geos - */ -static void iwl_free_geos(struct iwl_priv *priv) -{ - kfree(priv->ieee_channels); - kfree(priv->ieee_rates); - clear_bit(STATUS_GEO_CONFIGURED, &priv->status); -} - -static int iwl_init_drv(struct iwl_priv *priv) -{ - int ret; - - spin_lock_init(&priv->sta_lock); - - mutex_init(&priv->mutex); - - INIT_LIST_HEAD(&priv->calib_results); - - priv->ieee_channels = NULL; - priv->ieee_rates = NULL; - priv->band = IEEE80211_BAND_2GHZ; - - priv->plcp_delta_threshold = - priv->cfg->base_params->plcp_delta_threshold; - - priv->iw_mode = NL80211_IFTYPE_STATION; - priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; - priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; - priv->agg_tids_count = 0; - - priv->ucode_owner = IWL_OWNERSHIP_DRIVER; - - priv->rx_statistics_jiffies = jiffies; - - /* Choose which receivers/antennas to use */ - iwlagn_set_rxon_chain(priv, &priv->contexts[IWL_RXON_CTX_BSS]); - - iwl_init_scan_params(priv); - - /* init bt coex */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; - priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; - priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; - priv->bt_on_thresh = BT_ON_THRESHOLD_DEF; - priv->bt_duration = BT_DURATION_LIMIT_DEF; - priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; - } - - ret = iwl_init_channel_map(priv); - if (ret) { - IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); - goto err; - } - - ret = iwl_init_geos(priv); - if (ret) { - IWL_ERR(priv, "initializing geos failed: %d\n", ret); - goto err_free_channel_map; - } - iwl_init_hw_rates(priv->ieee_rates); - - return 0; - -err_free_channel_map: - iwl_free_channel_map(priv); -err: - return ret; -} - -static void iwl_uninit_drv(struct iwl_priv *priv) -{ - iwl_free_geos(priv); - iwl_free_channel_map(priv); - kfree(priv->scan_cmd); - kfree(priv->beacon_cmd); - kfree(rcu_dereference_raw(priv->noa_data)); - iwl_calib_free_results(priv); -#ifdef CONFIG_IWLWIFI_DEBUGFS - kfree(priv->wowlan_sram); -#endif -} - -static void iwl_set_hw_params(struct iwl_priv *priv) -{ - if (priv->cfg->ht_params) - priv->hw_params.use_rts_for_aggregation = - priv->cfg->ht_params->use_rts_for_aggregation; - - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) - priv->hw_params.sku &= ~EEPROM_SKU_CAP_11N_ENABLE; - - /* Device-specific setup */ - priv->lib->set_hw_params(priv); -} - - - -/* show what optional capabilities we have */ -static void iwl_option_config(struct iwl_priv *priv) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUG disabled\n"); -#endif - -#ifdef CONFIG_IWLWIFI_DEBUGFS - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEBUGFS disabled\n"); -#endif - -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n"); -#endif - -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TESTMODE disabled\n"); -#endif - -#ifdef CONFIG_IWLWIFI_P2P - IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n"); -#else - IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n"); -#endif -} - -static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, - const struct iwl_cfg *cfg, - const struct iwl_fw *fw) -{ - struct iwl_priv *priv; - struct ieee80211_hw *hw; - struct iwl_op_mode *op_mode; - u16 num_mac; - u32 ucode_flags; - struct iwl_trans_config trans_cfg; - static const u8 no_reclaim_cmds[] = { - REPLY_RX_PHY_CMD, - REPLY_RX, - REPLY_RX_MPDU_CMD, - REPLY_COMPRESSED_BA, - STATISTICS_NOTIFICATION, - REPLY_TX, - }; - int i; - - /************************ - * 1. Allocating HW data - ************************/ - hw = iwl_alloc_all(); - if (!hw) { - pr_err("%s: Cannot allocate network device\n", cfg->name); - goto out; - } - - op_mode = hw->priv; - op_mode->ops = &iwl_dvm_ops; - priv = IWL_OP_MODE_GET_DVM(op_mode); - priv->trans = trans; - priv->dev = trans->dev; - priv->cfg = cfg; - priv->fw = fw; - - switch (priv->cfg->device_family) { - case IWL_DEVICE_FAMILY_1000: - case IWL_DEVICE_FAMILY_100: - priv->lib = &iwl1000_lib; - break; - case IWL_DEVICE_FAMILY_2000: - case IWL_DEVICE_FAMILY_105: - priv->lib = &iwl2000_lib; - break; - case IWL_DEVICE_FAMILY_2030: - case IWL_DEVICE_FAMILY_135: - priv->lib = &iwl2030_lib; - break; - case IWL_DEVICE_FAMILY_5000: - priv->lib = &iwl5000_lib; - break; - case IWL_DEVICE_FAMILY_5150: - priv->lib = &iwl5150_lib; - break; - case IWL_DEVICE_FAMILY_6000: - case IWL_DEVICE_FAMILY_6005: - case IWL_DEVICE_FAMILY_6000i: - case IWL_DEVICE_FAMILY_6050: - case IWL_DEVICE_FAMILY_6150: - priv->lib = &iwl6000_lib; - break; - case IWL_DEVICE_FAMILY_6030: - priv->lib = &iwl6030_lib; - break; - default: - break; - } - - if (WARN_ON(!priv->lib)) - goto out_free_hw; - - /* - * Populate the state variables that the transport layer needs - * to know about. - */ - trans_cfg.op_mode = op_mode; - trans_cfg.no_reclaim_cmds = no_reclaim_cmds; - trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; - if (!iwlwifi_mod_params.wd_disable) - trans_cfg.queue_watchdog_timeout = - priv->cfg->base_params->wd_timeout; - else - trans_cfg.queue_watchdog_timeout = IWL_WATCHHDOG_DISABLED; - trans_cfg.command_names = iwl_dvm_cmd_strings; - - ucode_flags = fw->ucode_capa.flags; - -#ifndef CONFIG_IWLWIFI_P2P - ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; -#endif - - if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { - priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; - trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; - trans_cfg.queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo; - trans_cfg.n_queue_to_fifo = - ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); - } else { - priv->sta_key_max_num = STA_KEY_MAX_NUM; - trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo; - trans_cfg.n_queue_to_fifo = - ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); - } - - /* Configure transport layer */ - iwl_trans_configure(priv->trans, &trans_cfg); - - /* At this point both hw and priv are allocated. */ - - SET_IEEE80211_DEV(priv->hw, priv->trans->dev); - - iwl_option_config(priv); - - IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); - - /* is antenna coupling more than 35dB ? */ - priv->bt_ant_couple_ok = - (iwlwifi_mod_params.ant_coupling > - IWL_BT_ANTENNA_COUPLING_THRESHOLD) ? - true : false; - - /* enable/disable bt channel inhibition */ - priv->bt_ch_announce = iwlwifi_mod_params.bt_ch_announce; - IWL_DEBUG_INFO(priv, "BT channel inhibition is %s\n", - (priv->bt_ch_announce) ? "On" : "Off"); - - /* these spin locks will be used in apm_ops.init and EEPROM access - * we should init now - */ - spin_lock_init(&priv->statistics.lock); - - /*********************** - * 2. Read REV register - ***********************/ - IWL_INFO(priv, "Detected %s, REV=0x%X\n", - priv->cfg->name, priv->trans->hw_rev); - - if (iwl_trans_start_hw(priv->trans)) - goto out_free_hw; - - /* Read the EEPROM */ - if (iwl_eeprom_init(priv, priv->trans->hw_rev)) { - IWL_ERR(priv, "Unable to init EEPROM\n"); - goto out_free_hw; - } - /* Reset chip to save power until we load uCode during "up". */ - iwl_trans_stop_hw(priv->trans, false); - - if (iwl_eeprom_check_version(priv)) - goto out_free_eeprom; - - if (iwl_eeprom_init_hw_params(priv)) - goto out_free_eeprom; - - /* extract MAC Address */ - iwl_eeprom_get_mac(priv, priv->addresses[0].addr); - IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); - priv->hw->wiphy->addresses = priv->addresses; - priv->hw->wiphy->n_addresses = 1; - num_mac = iwl_eeprom_query16(priv, EEPROM_NUM_MAC_ADDRESS); - if (num_mac > 1) { - memcpy(priv->addresses[1].addr, priv->addresses[0].addr, - ETH_ALEN); - priv->addresses[1].addr[5]++; - priv->hw->wiphy->n_addresses++; - } - - /************************ - * 4. Setup HW constants - ************************/ - iwl_set_hw_params(priv); - - if (!(priv->hw_params.sku & EEPROM_SKU_CAP_IPAN_ENABLE)) { - IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN"); - ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; - /* - * if not PAN, then don't support P2P -- might be a uCode - * packaging bug or due to the eeprom check above - */ - ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; - priv->sta_key_max_num = STA_KEY_MAX_NUM; - trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo; - trans_cfg.n_queue_to_fifo = - ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); - - /* Configure transport layer again*/ - iwl_trans_configure(priv->trans, &trans_cfg); - } - - /******************* - * 5. Setup priv - *******************/ - for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { - priv->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; - if (i < IWLAGN_FIRST_AMPDU_QUEUE && - i != IWL_DEFAULT_CMD_QUEUE_NUM && - i != IWL_IPAN_CMD_QUEUE_NUM) - priv->queue_to_mac80211[i] = i; - atomic_set(&priv->queue_stop_count[i], 0); - } - - WARN_ON(trans_cfg.queue_to_fifo[trans_cfg.cmd_queue] != - IWLAGN_CMD_FIFO_NUM); - - if (iwl_init_drv(priv)) - goto out_free_eeprom; - - /* At this point both hw and priv are initialized. */ - - /******************** - * 6. Setup services - ********************/ - iwl_setup_deferred_work(priv); - iwl_setup_rx_handlers(priv); - iwl_testmode_init(priv); - - iwl_power_initialize(priv); - iwl_tt_initialize(priv); - - snprintf(priv->hw->wiphy->fw_version, - sizeof(priv->hw->wiphy->fw_version), - "%s", fw->fw_version); - - priv->new_scan_threshold_behaviour = - !!(ucode_flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); - - priv->phy_calib_chain_noise_reset_cmd = - fw->ucode_capa.standard_phy_calibration_size; - priv->phy_calib_chain_noise_gain_cmd = - fw->ucode_capa.standard_phy_calibration_size + 1; - - /* initialize all valid contexts */ - iwl_init_context(priv, ucode_flags); - - /************************************************** - * This is still part of probe() in a sense... - * - * 7. Setup and register with mac80211 and debugfs - **************************************************/ - if (iwlagn_mac_setup_register(priv, &fw->ucode_capa)) - goto out_destroy_workqueue; - - if (iwl_dbgfs_register(priv, DRV_NAME)) - IWL_ERR(priv, - "failed to create debugfs files. Ignoring error\n"); - - return op_mode; - -out_destroy_workqueue: - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - iwl_uninit_drv(priv); -out_free_eeprom: - iwl_eeprom_free(priv); -out_free_hw: - ieee80211_free_hw(priv->hw); -out: - op_mode = NULL; - return op_mode; -} - -static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); - - iwl_dbgfs_unregister(priv); - - iwl_testmode_cleanup(priv); - iwlagn_mac_unregister(priv); - - iwl_tt_exit(priv); - - /*This will stop the queues, move the device to low power state */ - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - - iwl_eeprom_free(priv); - - /*netif_stop_queue(dev); */ - flush_workqueue(priv->workqueue); - - /* ieee80211_unregister_hw calls iwlagn_mac_stop, which flushes - * priv->workqueue... so we can't take down the workqueue - * until now... */ - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - - iwl_uninit_drv(priv); - - dev_kfree_skb(priv->beacon_skb); - - iwl_trans_stop_hw(priv->trans, true); - ieee80211_free_hw(priv->hw); -} - -static const char * const desc_lookup_text[] = { - "OK", - "FAIL", - "BAD_PARAM", - "BAD_CHECKSUM", - "NMI_INTERRUPT_WDG", - "SYSASSERT", - "FATAL_ERROR", - "BAD_COMMAND", - "HW_ERROR_TUNE_LOCK", - "HW_ERROR_TEMPERATURE", - "ILLEGAL_CHAN_FREQ", - "VCC_NOT_STABLE", - "FH_ERROR", - "NMI_INTERRUPT_HOST", - "NMI_INTERRUPT_ACTION_PT", - "NMI_INTERRUPT_UNKNOWN", - "UCODE_VERSION_MISMATCH", - "HW_ERROR_ABS_LOCK", - "HW_ERROR_CAL_LOCK_FAIL", - "NMI_INTERRUPT_INST_ACTION_PT", - "NMI_INTERRUPT_DATA_ACTION_PT", - "NMI_TRM_HW_ER", - "NMI_INTERRUPT_TRM", - "NMI_INTERRUPT_BREAK_POINT", - "DEBUG_0", - "DEBUG_1", - "DEBUG_2", - "DEBUG_3", -}; - -static struct { char *name; u8 num; } advanced_lookup[] = { - { "NMI_INTERRUPT_WDG", 0x34 }, - { "SYSASSERT", 0x35 }, - { "UCODE_VERSION_MISMATCH", 0x37 }, - { "BAD_COMMAND", 0x38 }, - { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, - { "FATAL_ERROR", 0x3D }, - { "NMI_TRM_HW_ERR", 0x46 }, - { "NMI_INTERRUPT_TRM", 0x4C }, - { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, - { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, - { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, - { "NMI_INTERRUPT_HOST", 0x66 }, - { "NMI_INTERRUPT_ACTION_PT", 0x7C }, - { "NMI_INTERRUPT_UNKNOWN", 0x84 }, - { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, - { "ADVANCED_SYSASSERT", 0 }, -}; - -static const char *desc_lookup(u32 num) -{ - int i; - int max = ARRAY_SIZE(desc_lookup_text); - - if (num < max) - return desc_lookup_text[num]; - - max = ARRAY_SIZE(advanced_lookup) - 1; - for (i = 0; i < max; i++) { - if (advanced_lookup[i].num == num) - break; - } - return advanced_lookup[i].name; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -static void iwl_dump_nic_error_log(struct iwl_priv *priv) -{ - struct iwl_trans *trans = priv->trans; - u32 base; - struct iwl_error_event_table table; - - base = priv->device_pointers.error_event_table; - if (priv->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = priv->fw->init_errlog_ptr; - } else { - if (!base) - base = priv->fw->inst_errlog_ptr; - } - - if (!iwlagn_hw_valid_rtc_data_addr(base)) { - IWL_ERR(priv, - "Not valid error log pointer 0x%08X for %s uCode\n", - base, - (priv->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return; - } - - /*TODO: Update dbgfs with ISR error stats obtained below */ - iwl_read_targ_mem_words(trans, base, &table, sizeof(table)); - - if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - IWL_ERR(trans, "Start IWL Error Log Dump:\n"); - IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", - priv->status, table.valid); - } - - trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, - table.data1, table.data2, table.line, - table.blink1, table.blink2, table.ilink1, - table.ilink2, table.bcon_time, table.gp1, - table.gp2, table.gp3, table.ucode_ver, - table.hw_ver, table.brd_ver); - IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, - desc_lookup(table.error_id)); - IWL_ERR(priv, "0x%08X | uPc\n", table.pc); - IWL_ERR(priv, "0x%08X | branchlink1\n", table.blink1); - IWL_ERR(priv, "0x%08X | branchlink2\n", table.blink2); - IWL_ERR(priv, "0x%08X | interruptlink1\n", table.ilink1); - IWL_ERR(priv, "0x%08X | interruptlink2\n", table.ilink2); - IWL_ERR(priv, "0x%08X | data1\n", table.data1); - IWL_ERR(priv, "0x%08X | data2\n", table.data2); - IWL_ERR(priv, "0x%08X | line\n", table.line); - IWL_ERR(priv, "0x%08X | beacon time\n", table.bcon_time); - IWL_ERR(priv, "0x%08X | tsf low\n", table.tsf_low); - IWL_ERR(priv, "0x%08X | tsf hi\n", table.tsf_hi); - IWL_ERR(priv, "0x%08X | time gp1\n", table.gp1); - IWL_ERR(priv, "0x%08X | time gp2\n", table.gp2); - IWL_ERR(priv, "0x%08X | time gp3\n", table.gp3); - IWL_ERR(priv, "0x%08X | uCode version\n", table.ucode_ver); - IWL_ERR(priv, "0x%08X | hw version\n", table.hw_ver); - IWL_ERR(priv, "0x%08X | board version\n", table.brd_ver); - IWL_ERR(priv, "0x%08X | hcmd\n", table.hcmd); - IWL_ERR(priv, "0x%08X | isr0\n", table.isr0); - IWL_ERR(priv, "0x%08X | isr1\n", table.isr1); - IWL_ERR(priv, "0x%08X | isr2\n", table.isr2); - IWL_ERR(priv, "0x%08X | isr3\n", table.isr3); - IWL_ERR(priv, "0x%08X | isr4\n", table.isr4); - IWL_ERR(priv, "0x%08X | isr_pref\n", table.isr_pref); - IWL_ERR(priv, "0x%08X | wait_event\n", table.wait_event); - IWL_ERR(priv, "0x%08X | l2p_control\n", table.l2p_control); - IWL_ERR(priv, "0x%08X | l2p_duration\n", table.l2p_duration); - IWL_ERR(priv, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); - IWL_ERR(priv, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); - IWL_ERR(priv, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); - IWL_ERR(priv, "0x%08X | timestamp\n", table.u_timestamp); - IWL_ERR(priv, "0x%08X | flow_handler\n", table.flow_handler); -} - -#define EVENT_START_OFFSET (4 * sizeof(u32)) - -/** - * iwl_print_event_log - Dump error event log to syslog - * - */ -static int iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode, - int pos, char **buf, size_t bufsz) -{ - u32 i; - u32 base; /* SRAM byte address of event log header */ - u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - unsigned long reg_flags; - - struct iwl_trans *trans = priv->trans; - - if (num_events == 0) - return pos; - - base = priv->device_pointers.log_event_table; - if (priv->cur_ucode == IWL_UCODE_INIT) { - if (!base) - base = priv->fw->init_evtlog_ptr; - } else { - if (!base) - base = priv->fw->inst_evtlog_ptr; - } - - if (mode == 0) - event_size = 2 * sizeof(u32); - else - event_size = 3 * sizeof(u32); - - ptr = base + EVENT_START_OFFSET + (start_idx * event_size); - - /* Make sure device is powered up for SRAM reads */ - spin_lock_irqsave(&trans->reg_lock, reg_flags); - if (unlikely(!iwl_grab_nic_access(trans))) - goto out_unlock; - - /* Set starting address; reads will auto-increment */ - iwl_write32(trans, HBUS_TARG_MEM_RADDR, ptr); - - /* "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. */ - for (i = 0; i < num_events; i++) { - ev = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - time = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - if (mode == 0) { - /* data, ev */ - if (bufsz) { - pos += scnprintf(*buf + pos, bufsz - pos, - "EVT_LOG:0x%08x:%04u\n", - time, ev); - } else { - trace_iwlwifi_dev_ucode_event(trans->dev, 0, - time, ev); - IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", - time, ev); - } - } else { - data = iwl_read32(trans, HBUS_TARG_MEM_RDAT); - if (bufsz) { - pos += scnprintf(*buf + pos, bufsz - pos, - "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - } else { - IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - trace_iwlwifi_dev_ucode_event(trans->dev, time, - data, ev); - } - } - } - - /* Allow device to power down */ - iwl_release_nic_access(trans); -out_unlock: - spin_unlock_irqrestore(&trans->reg_lock, reg_flags); - return pos; -} - -/** - * iwl_print_last_event_logs - Dump the newest # of event log to syslog - */ -static int iwl_print_last_event_logs(struct iwl_priv *priv, u32 capacity, - u32 num_wraps, u32 next_entry, - u32 size, u32 mode, - int pos, char **buf, size_t bufsz) -{ - /* - * display the newest DEFAULT_LOG_ENTRIES entries - * i.e the entries just before the next ont that uCode would fill. - */ - if (num_wraps) { - if (next_entry < size) { - pos = iwl_print_event_log(priv, - capacity - (size - next_entry), - size - next_entry, mode, - pos, buf, bufsz); - pos = iwl_print_event_log(priv, 0, - next_entry, mode, - pos, buf, bufsz); - } else - pos = iwl_print_event_log(priv, next_entry - size, - size, mode, pos, buf, bufsz); - } else { - if (next_entry < size) { - pos = iwl_print_event_log(priv, 0, next_entry, - mode, pos, buf, bufsz); - } else { - pos = iwl_print_event_log(priv, next_entry - size, - size, mode, pos, buf, bufsz); - } - } - return pos; -} - -#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) - -int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, - char **buf, bool display) -{ - u32 base; /* SRAM byte address of event log header */ - u32 capacity; /* event log capacity in # entries */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - u32 size; /* # entries that we'll print */ - u32 logsize; - int pos = 0; - size_t bufsz = 0; - struct iwl_trans *trans = priv->trans; - - base = priv->device_pointers.log_event_table; - if (priv->cur_ucode == IWL_UCODE_INIT) { - logsize = priv->fw->init_evtlog_size; - if (!base) - base = priv->fw->init_evtlog_ptr; - } else { - logsize = priv->fw->inst_evtlog_size; - if (!base) - base = priv->fw->inst_evtlog_ptr; - } - - if (!iwlagn_hw_valid_rtc_data_addr(base)) { - IWL_ERR(priv, - "Invalid event log pointer 0x%08X for %s uCode\n", - base, - (priv->cur_ucode == IWL_UCODE_INIT) - ? "Init" : "RT"); - return -EINVAL; - } - - /* event log header */ - capacity = iwl_read_targ_mem(trans, base); - mode = iwl_read_targ_mem(trans, base + (1 * sizeof(u32))); - num_wraps = iwl_read_targ_mem(trans, base + (2 * sizeof(u32))); - next_entry = iwl_read_targ_mem(trans, base + (3 * sizeof(u32))); - - if (capacity > logsize) { - IWL_ERR(priv, "Log capacity %d is bogus, limit to %d " - "entries\n", capacity, logsize); - capacity = logsize; - } - - if (next_entry > logsize) { - IWL_ERR(priv, "Log write index %d is bogus, limit to %d\n", - next_entry, logsize); - next_entry = logsize; - } - - size = num_wraps ? capacity : next_entry; - - /* bail out if nothing in log */ - if (size == 0) { - IWL_ERR(trans, "Start IWL Event Log Dump: nothing in log\n"); - return pos; - } - -#ifdef CONFIG_IWLWIFI_DEBUG - if (!(iwl_have_debug_level(IWL_DL_FW_ERRORS)) && !full_log) - size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) - ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; -#else - size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) - ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; -#endif - IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", - size); - -#ifdef CONFIG_IWLWIFI_DEBUG - if (display) { - if (full_log) - bufsz = capacity * 48; - else - bufsz = size * 48; - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - } - if (iwl_have_debug_level(IWL_DL_FW_ERRORS) || full_log) { - /* - * if uCode has wrapped back to top of log, - * start at the oldest entry, - * i.e the next one that uCode would fill. - */ - if (num_wraps) - pos = iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode, - pos, buf, bufsz); - /* (then/else) start at top of log */ - pos = iwl_print_event_log(priv, 0, - next_entry, mode, pos, buf, bufsz); - } else - pos = iwl_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode, - pos, buf, bufsz); -#else - pos = iwl_print_last_event_logs(priv, capacity, num_wraps, - next_entry, size, mode, - pos, buf, bufsz); -#endif - return pos; -} - -static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) -{ - unsigned int reload_msec; - unsigned long reload_jiffies; - -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_have_debug_level(IWL_DL_FW_ERRORS)) - iwl_print_rx_config_cmd(priv, IWL_RXON_CTX_BSS); -#endif - - /* uCode is no longer loaded. */ - priv->ucode_loaded = false; - - /* Set the FW error flag -- cleared on iwl_down */ - set_bit(STATUS_FW_ERROR, &priv->status); - - iwl_abort_notification_waits(&priv->notif_wait); - - /* Keep the restart process from trying to send host - * commands by clearing the ready bit */ - clear_bit(STATUS_READY, &priv->status); - - wake_up(&priv->trans->wait_command_queue); - - if (!ondemand) { - /* - * If firmware keep reloading, then it indicate something - * serious wrong and firmware having problem to recover - * from it. Instead of keep trying which will fill the syslog - * and hang the system, let's just stop it - */ - reload_jiffies = jiffies; - reload_msec = jiffies_to_msecs((long) reload_jiffies - - (long) priv->reload_jiffies); - priv->reload_jiffies = reload_jiffies; - if (reload_msec <= IWL_MIN_RELOAD_DURATION) { - priv->reload_count++; - if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) { - IWL_ERR(priv, "BUG_ON, Stop restarting\n"); - return; - } - } else - priv->reload_count = 0; - } - - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { - if (iwlwifi_mod_params.restart_fw) { - IWL_DEBUG_FW_ERRORS(priv, - "Restarting adapter due to uCode error.\n"); - queue_work(priv->workqueue, &priv->restart); - } else - IWL_DEBUG_FW_ERRORS(priv, - "Detected FW error, but not restarting\n"); - } -} - -static void iwl_nic_error(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - IWL_ERR(priv, "Loaded firmware version: %s\n", - priv->fw->fw_version); - - iwl_dump_nic_error_log(priv); - iwl_dump_nic_event_log(priv, false, NULL, false); - - iwlagn_fw_error(priv, false); -} - -static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - if (!iwl_check_for_ct_kill(priv)) { - IWL_ERR(priv, "Restarting adapter queue is full\n"); - iwlagn_fw_error(priv, false); - } -} - -static void iwl_nic_config(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - priv->lib->nic_config(priv); -} - -static void iwl_wimax_active(struct iwl_op_mode *op_mode) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - clear_bit(STATUS_READY, &priv->status); - IWL_ERR(priv, "RF is used by WiMAX\n"); -} - -static void iwl_stop_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - int mq = priv->queue_to_mac80211[queue]; - - if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) - return; - - if (atomic_inc_return(&priv->queue_stop_count[mq]) > 1) { - IWL_DEBUG_TX_QUEUES(priv, - "queue %d (mac80211 %d) already stopped\n", - queue, mq); - return; - } - - set_bit(mq, &priv->transport_queue_stop); - ieee80211_stop_queue(priv->hw, mq); -} - -static void iwl_wake_sw_queue(struct iwl_op_mode *op_mode, int queue) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - int mq = priv->queue_to_mac80211[queue]; - - if (WARN_ON_ONCE(mq == IWL_INVALID_MAC80211_QUEUE)) - return; - - if (atomic_dec_return(&priv->queue_stop_count[mq]) > 0) { - IWL_DEBUG_TX_QUEUES(priv, - "queue %d (mac80211 %d) already awake\n", - queue, mq); - return; - } - - clear_bit(mq, &priv->transport_queue_stop); - - if (!priv->passive_no_rx) - ieee80211_wake_queue(priv->hw, mq); -} - -void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) -{ - int mq; - - if (!priv->passive_no_rx) - return; - - for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { - if (!test_bit(mq, &priv->transport_queue_stop)) { - IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq); - ieee80211_wake_queue(priv->hw, mq); - } else { - IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq); - } - } - - priv->passive_no_rx = false; -} - -static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info; - - info = IEEE80211_SKB_CB(skb); - kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1])); - dev_kfree_skb_any(skb); -} - -static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) -{ - struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - - if (state) - set_bit(STATUS_RF_KILL_HW, &priv->status); - else - clear_bit(STATUS_RF_KILL_HW, &priv->status); - - wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); -} - -const struct iwl_op_mode_ops iwl_dvm_ops = { - .start = iwl_op_mode_dvm_start, - .stop = iwl_op_mode_dvm_stop, - .rx = iwl_rx_dispatch, - .queue_full = iwl_stop_sw_queue, - .queue_not_full = iwl_wake_sw_queue, - .hw_rf_kill = iwl_set_hw_rfkill_state, - .free_skb = iwl_free_skb, - .nic_error = iwl_nic_error, - .cmd_queue_full = iwl_cmd_queue_full, - .nic_config = iwl_nic_config, - .wimax_active = iwl_wimax_active, -}; - -/***************************************************************************** - * - * driver and module entry point - * - *****************************************************************************/ - -struct kmem_cache *iwl_tx_cmd_pool; - -static int __init iwl_init(void) -{ - - int ret; - pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); - pr_info(DRV_COPYRIGHT "\n"); - - iwl_tx_cmd_pool = kmem_cache_create("iwl_dev_cmd", - sizeof(struct iwl_device_cmd), - sizeof(void *), 0, NULL); - if (!iwl_tx_cmd_pool) - return -ENOMEM; - - ret = iwlagn_rate_control_register(); - if (ret) { - pr_err("Unable to register rate control algorithm: %d\n", ret); - goto error_rc_register; - } - - ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); - if (ret) { - pr_err("Unable to register op_mode: %d\n", ret); - goto error_opmode_register; - } - return ret; - -error_opmode_register: - iwlagn_rate_control_unregister(); -error_rc_register: - kmem_cache_destroy(iwl_tx_cmd_pool); - return ret; -} -module_init(iwl_init); - -static void __exit iwl_exit(void) -{ - iwl_opmode_deregister("iwldvm"); - iwlagn_rate_control_unregister(); - kmem_cache_destroy(iwl_tx_cmd_pool); -} -module_exit(iwl_exit); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h deleted file mode 100644 index 3f3607fd7dad..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ /dev/null @@ -1,526 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_agn_h__ -#define __iwl_agn_h__ - -#include "iwl-dev.h" -#include "iwl-config.h" - -/* The first 11 queues (0-10) are used otherwise */ -#define IWLAGN_FIRST_AMPDU_QUEUE 11 - -/* AUX (TX during scan dwell) queue */ -#define IWL_AUX_QUEUE 10 - -/* device operations */ -extern struct iwl_lib_ops iwl1000_lib; -extern struct iwl_lib_ops iwl2000_lib; -extern struct iwl_lib_ops iwl2030_lib; -extern struct iwl_lib_ops iwl5000_lib; -extern struct iwl_lib_ops iwl5150_lib; -extern struct iwl_lib_ops iwl6000_lib; -extern struct iwl_lib_ops iwl6030_lib; - - -#define TIME_UNIT 1024 - -/***************************************************** -* DRIVER STATUS FUNCTIONS -******************************************************/ -#define STATUS_RF_KILL_HW 0 -#define STATUS_CT_KILL 1 -#define STATUS_ALIVE 2 -#define STATUS_READY 3 -#define STATUS_GEO_CONFIGURED 4 -#define STATUS_EXIT_PENDING 5 -#define STATUS_STATISTICS 6 -#define STATUS_SCANNING 7 -#define STATUS_SCAN_ABORTING 8 -#define STATUS_SCAN_HW 9 -#define STATUS_FW_ERROR 10 -#define STATUS_CHANNEL_SWITCH_PENDING 11 -#define STATUS_SCAN_COMPLETE 12 -#define STATUS_POWER_PMI 13 -#define STATUS_SCAN_ROC_EXPIRED 14 - -struct iwl_ucode_capabilities; - -extern struct ieee80211_ops iwlagn_hw_ops; - -static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) -{ - hdr->op_code = cmd; - hdr->first_group = 0; - hdr->groups_num = 1; - hdr->data_valid = 1; -} - -void iwl_down(struct iwl_priv *priv); -void iwl_cancel_deferred_work(struct iwl_priv *priv); -void iwlagn_prepare_restart(struct iwl_priv *priv); -int __must_check iwl_rx_dispatch(struct iwl_op_mode *op_mode, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); - -bool iwl_check_for_ct_kill(struct iwl_priv *priv); - -void iwlagn_lift_passive_no_rx(struct iwl_priv *priv); - -/* MAC80211 */ -struct ieee80211_hw *iwl_alloc_all(void); -int iwlagn_mac_setup_register(struct iwl_priv *priv, - const struct iwl_ucode_capabilities *capa); -void iwlagn_mac_unregister(struct iwl_priv *priv); - -/* commands */ -int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); -int iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, - u32 flags, u16 len, const void *data); - -/* RXON */ -void iwl_connection_init_rx_config(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwlagn_set_pan_params(struct iwl_priv *priv); -int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed); -void iwlagn_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes); -void iwlagn_config_ht40(struct ieee80211_conf *conf, - struct iwl_rxon_context *ctx); -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf); -void iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, - struct iwl_rxon_context *ctx); -void iwl_set_flags_for_band(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - enum ieee80211_band band, - struct ieee80211_vif *vif); - -/* uCode */ -int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); -void iwl_send_prio_tbl(struct iwl_priv *priv); -int iwl_init_alive_start(struct iwl_priv *priv); -int iwl_run_init_ucode(struct iwl_priv *priv); -int iwl_load_ucode_wait_alive(struct iwl_priv *priv, - enum iwl_ucode_type ucode_type); -int iwl_send_calib_results(struct iwl_priv *priv); -int iwl_calib_set(struct iwl_priv *priv, - const struct iwl_calib_hdr *cmd, int len); -void iwl_calib_free_results(struct iwl_priv *priv); -int iwl_dump_nic_event_log(struct iwl_priv *priv, bool full_log, - char **buf, bool display); -int iwlagn_hw_valid_rtc_data_addr(u32 addr); - -/* lib */ -int iwlagn_send_tx_power(struct iwl_priv *priv); -void iwlagn_temperature(struct iwl_priv *priv); -int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); -int iwlagn_send_beacon_cmd(struct iwl_priv *priv); -int iwl_send_statistics_request(struct iwl_priv *priv, - u8 flags, bool clear); - -static inline const struct ieee80211_supported_band *iwl_get_hw_mode( - struct iwl_priv *priv, enum ieee80211_band band) -{ - return priv->hw->wiphy->bands[band]; -} - -#ifdef CONFIG_PM_SLEEP -int iwlagn_send_patterns(struct iwl_priv *priv, - struct cfg80211_wowlan *wowlan); -int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan); -#endif - -/* rx */ -int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); -void iwl_setup_rx_handlers(struct iwl_priv *priv); -void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); - - -/* tx */ -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); -int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u16 *ssn); -int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid, u8 buf_size); -int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid); -int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); -int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); - -static inline u32 iwl_tx_status_to_mac80211(u32 status) -{ - status &= TX_STATUS_MSK; - - switch (status) { - case TX_STATUS_SUCCESS: - case TX_STATUS_DIRECT_DONE: - return IEEE80211_TX_STAT_ACK; - case TX_STATUS_FAIL_DEST_PS: - case TX_STATUS_FAIL_PASSIVE_NO_RX: - return IEEE80211_TX_STAT_TX_FILTERED; - default: - return 0; - } -} - -static inline bool iwl_is_tx_success(u32 status) -{ - status &= TX_STATUS_MSK; - return (status == TX_STATUS_SUCCESS) || - (status == TX_STATUS_DIRECT_DONE); -} - -u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); - -/* scan */ -void iwlagn_post_scan(struct iwl_priv *priv); -void iwlagn_disable_roc(struct iwl_priv *priv); -int iwl_force_rf_reset(struct iwl_priv *priv, bool external); -void iwl_init_scan_params(struct iwl_priv *priv); -int iwl_scan_cancel(struct iwl_priv *priv); -void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); -void iwl_force_scan_end(struct iwl_priv *priv); -void iwl_internal_short_hw_scan(struct iwl_priv *priv); -void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); -void iwl_setup_scan_deferred_work(struct iwl_priv *priv); -void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); -int __must_check iwl_scan_initiate(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum iwl_scan_type scan_type, - enum ieee80211_band band); - -void iwl_scan_roc_expired(struct iwl_priv *priv); -void iwl_scan_offchannel_skb(struct iwl_priv *priv); -void iwl_scan_offchannel_skb_status(struct iwl_priv *priv); - -/* For faster active scanning, scan will move to the next channel if fewer than - * PLCP_QUIET_THRESH packets are heard on this channel within - * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell - * time if it's a quiet channel (nothing responded to our probe, and there's - * no other traffic). - * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ -#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ -#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ - -#define IWL_SCAN_CHECK_WATCHDOG (HZ * 7) - - -/* bt coex */ -void iwlagn_send_advance_bt_config(struct iwl_priv *priv); -int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); -void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv); -void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv); -void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv); -void iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv); -void iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena); - -static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) -{ - return priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist; -} - -#ifdef CONFIG_IWLWIFI_DEBUG -const char *iwl_get_tx_fail_reason(u32 status); -const char *iwl_get_agg_tx_fail_reason(u16 status); -#else -static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } -static inline const char *iwl_get_agg_tx_fail_reason(u16 status) { return ""; } -#endif - - -/* station management */ -int iwlagn_manage_ibss_station(struct iwl_priv *priv, - struct ieee80211_vif *vif, bool add); -#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ -#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ -#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of - being activated */ -#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211; - (this is for the IBSS BSSID stations) */ -#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */ - - -void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx); -void iwl_clear_ucode_stations(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -void iwl_dealloc_bcast_stations(struct iwl_priv *priv); -int iwl_get_free_ucode_key_offset(struct iwl_priv *priv); -int iwl_send_add_sta(struct iwl_priv *priv, - struct iwl_addsta_cmd *sta, u8 flags); -int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, - struct ieee80211_sta *sta, u8 *sta_id_r); -int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr); -void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, - const u8 *addr); -u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, bool is_ap, struct ieee80211_sta *sta); - -int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct iwl_link_quality_cmd *lq, u8 flags, bool init); -int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); -int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_sta *sta); - -bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_sta_ht_cap *ht_cap); - -static inline int iwl_sta_id(struct ieee80211_sta *sta) -{ - if (WARN_ON(!sta)) - return IWL_INVALID_STATION; - - return ((struct iwl_station_priv *)sta->drv_priv)->sta_id; -} - -int iwlagn_alloc_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwlagn_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - const u8 *addr, u8 *sta_id_r); -int iwl_remove_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key); -int iwl_set_default_wep_key(struct iwl_priv *priv, - struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key); -int iwl_restore_default_wep_keys(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwl_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key, - struct ieee80211_sta *sta); -int iwl_remove_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, - struct ieee80211_key_conf *key, - struct ieee80211_sta *sta); -void iwl_update_tkip_key(struct iwl_priv *priv, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); -int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); -int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid, u16 ssn); -int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, - int tid); -void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); -int iwl_update_bcast_station(struct iwl_priv *priv, - struct iwl_rxon_context *ctx); -int iwl_update_bcast_stations(struct iwl_priv *priv); - -/* rate */ -static inline u32 iwl_ant_idx_to_flags(u8 ant_idx) -{ - return BIT(ant_idx) << RATE_MCS_ANT_POS; -} - -static inline u8 iwl_hw_get_rate(__le32 rate_n_flags) -{ - return le32_to_cpu(rate_n_flags) & RATE_MCS_RATE_MSK; -} - -static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) -{ - return cpu_to_le32(flags|(u32)rate); -} - -extern int iwl_alive_start(struct iwl_priv *priv); -/* svtool */ -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE -extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, - int len); -extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct netlink_callback *cb, - void *data, int len); -extern void iwl_testmode_init(struct iwl_priv *priv); -extern void iwl_testmode_cleanup(struct iwl_priv *priv); -#else -static inline -int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) -{ - return -ENOSYS; -} -static inline -int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, - struct netlink_callback *cb, - void *data, int len) -{ - return -ENOSYS; -} -static inline -void iwl_testmode_init(struct iwl_priv *priv) -{ -} -static inline -void iwl_testmode_cleanup(struct iwl_priv *priv) -{ -} -#endif - -#ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid); -#else -static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) -{ -} -#endif - -/* status checks */ - -static inline int iwl_is_ready(struct iwl_priv *priv) -{ - /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are - * set but EXIT_PENDING is not */ - return test_bit(STATUS_READY, &priv->status) && - test_bit(STATUS_GEO_CONFIGURED, &priv->status) && - !test_bit(STATUS_EXIT_PENDING, &priv->status); -} - -static inline int iwl_is_alive(struct iwl_priv *priv) -{ - return test_bit(STATUS_ALIVE, &priv->status); -} - -static inline int iwl_is_rfkill(struct iwl_priv *priv) -{ - return test_bit(STATUS_RF_KILL_HW, &priv->status); -} - -static inline int iwl_is_ctkill(struct iwl_priv *priv) -{ - return test_bit(STATUS_CT_KILL, &priv->status); -} - -static inline int iwl_is_ready_rf(struct iwl_priv *priv) -{ - if (iwl_is_rfkill(priv)) - return 0; - - return iwl_is_ready(priv); -} - -static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) -{ - if (state) - set_bit(STATUS_POWER_PMI, &priv->status); - else - clear_bit(STATUS_POWER_PMI, &priv->status); - iwl_trans_set_pmi(priv->trans, state); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_dbgfs_register(struct iwl_priv *priv, const char *name); -void iwl_dbgfs_unregister(struct iwl_priv *priv); -#else -static inline int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) -{ - return 0; -} -static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) -{ -} -#endif /* CONFIG_IWLWIFI_DEBUGFS */ - -#ifdef CONFIG_IWLWIFI_DEBUG -#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ -do { \ - if (!iwl_is_rfkill((m))) \ - IWL_ERR(m, fmt, ##args); \ - else \ - __iwl_err((m)->dev, true, \ - !iwl_have_debug_level(IWL_DL_RADIO), \ - fmt, ##args); \ -} while (0) -#else -#define IWL_DEBUG_QUIET_RFKILL(m, fmt, args...) \ -do { \ - if (!iwl_is_rfkill((m))) \ - IWL_ERR(m, fmt, ##args); \ - else \ - __iwl_err((m)->dev, true, true, fmt, ##args); \ -} while (0) -#endif /* CONFIG_IWLWIFI_DEBUG */ - -extern const char *iwl_dvm_cmd_strings[REPLY_MAX]; - -static inline const char *iwl_dvm_get_cmd_string(u8 cmd) -{ - const char *s = iwl_dvm_cmd_strings[cmd]; - if (s) - return s; - return "UNKNOWN"; -} -#endif /* __iwl_agn_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h deleted file mode 100644 index b9f7361d2426..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ /dev/null @@ -1,3958 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -/* - * Please use this file (iwl-commands.h) only for uCode API definitions. - * Please use iwl-xxxx-hw.h for hardware-related definitions. - * Please use iwl-dev.h for driver implementation definitions. - */ - -#ifndef __iwl_commands_h__ -#define __iwl_commands_h__ - -#include -#include - - -enum { - REPLY_ALIVE = 0x1, - REPLY_ERROR = 0x2, - REPLY_ECHO = 0x3, /* test command */ - - /* RXON and QOS commands */ - REPLY_RXON = 0x10, - REPLY_RXON_ASSOC = 0x11, - REPLY_QOS_PARAM = 0x13, - REPLY_RXON_TIMING = 0x14, - - /* Multi-Station support */ - REPLY_ADD_STA = 0x18, - REPLY_REMOVE_STA = 0x19, - REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ - REPLY_TXFIFO_FLUSH = 0x1e, - - /* Security */ - REPLY_WEPKEY = 0x20, - - /* RX, TX, LEDs */ - REPLY_TX = 0x1c, - REPLY_LEDS_CMD = 0x48, - REPLY_TX_LINK_QUALITY_CMD = 0x4e, - - /* WiMAX coexistence */ - COEX_PRIORITY_TABLE_CMD = 0x5a, - COEX_MEDIUM_NOTIFICATION = 0x5b, - COEX_EVENT_CMD = 0x5c, - - /* Calibration */ - TEMPERATURE_NOTIFICATION = 0x62, - CALIBRATION_CFG_CMD = 0x65, - CALIBRATION_RES_NOTIFICATION = 0x66, - CALIBRATION_COMPLETE_NOTIFICATION = 0x67, - - /* 802.11h related */ - REPLY_QUIET_CMD = 0x71, /* not used */ - REPLY_CHANNEL_SWITCH = 0x72, - CHANNEL_SWITCH_NOTIFICATION = 0x73, - REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74, - SPECTRUM_MEASURE_NOTIFICATION = 0x75, - - /* Power Management */ - POWER_TABLE_CMD = 0x77, - PM_SLEEP_NOTIFICATION = 0x7A, - PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, - - /* Scan commands and notifications */ - REPLY_SCAN_CMD = 0x80, - REPLY_SCAN_ABORT_CMD = 0x81, - SCAN_START_NOTIFICATION = 0x82, - SCAN_RESULTS_NOTIFICATION = 0x83, - SCAN_COMPLETE_NOTIFICATION = 0x84, - - /* IBSS/AP commands */ - BEACON_NOTIFICATION = 0x90, - REPLY_TX_BEACON = 0x91, - WHO_IS_AWAKE_NOTIFICATION = 0x94, /* not used */ - - /* Miscellaneous commands */ - REPLY_TX_POWER_DBM_CMD = 0x95, - QUIET_NOTIFICATION = 0x96, /* not used */ - REPLY_TX_PWR_TABLE_CMD = 0x97, - REPLY_TX_POWER_DBM_CMD_V1 = 0x98, /* old version of API */ - TX_ANT_CONFIGURATION_CMD = 0x98, - MEASURE_ABORT_NOTIFICATION = 0x99, /* not used */ - - /* Bluetooth device coexistence config command */ - REPLY_BT_CONFIG = 0x9b, - - /* Statistics */ - REPLY_STATISTICS_CMD = 0x9c, - STATISTICS_NOTIFICATION = 0x9d, - - /* RF-KILL commands and notifications */ - REPLY_CARD_STATE_CMD = 0xa0, - CARD_STATE_NOTIFICATION = 0xa1, - - /* Missed beacons notification */ - MISSED_BEACONS_NOTIFICATION = 0xa2, - - REPLY_CT_KILL_CONFIG_CMD = 0xa4, - SENSITIVITY_CMD = 0xa8, - REPLY_PHY_CALIBRATION_CMD = 0xb0, - REPLY_RX_PHY_CMD = 0xc0, - REPLY_RX_MPDU_CMD = 0xc1, - REPLY_RX = 0xc3, - REPLY_COMPRESSED_BA = 0xc5, - - /* BT Coex */ - REPLY_BT_COEX_PRIO_TABLE = 0xcc, - REPLY_BT_COEX_PROT_ENV = 0xcd, - REPLY_BT_COEX_PROFILE_NOTIF = 0xce, - - /* PAN commands */ - REPLY_WIPAN_PARAMS = 0xb2, - REPLY_WIPAN_RXON = 0xb3, /* use REPLY_RXON structure */ - REPLY_WIPAN_RXON_TIMING = 0xb4, /* use REPLY_RXON_TIMING structure */ - REPLY_WIPAN_RXON_ASSOC = 0xb6, /* use REPLY_RXON_ASSOC structure */ - REPLY_WIPAN_QOS_PARAM = 0xb7, /* use REPLY_QOS_PARAM structure */ - REPLY_WIPAN_WEPKEY = 0xb8, /* use REPLY_WEPKEY structure */ - REPLY_WIPAN_P2P_CHANNEL_SWITCH = 0xb9, - REPLY_WIPAN_NOA_NOTIFICATION = 0xbc, - REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd, - - REPLY_WOWLAN_PATTERNS = 0xe0, - REPLY_WOWLAN_WAKEUP_FILTER = 0xe1, - REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2, - REPLY_WOWLAN_TKIP_PARAMS = 0xe3, - REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, - REPLY_WOWLAN_GET_STATUS = 0xe5, - REPLY_D3_CONFIG = 0xd3, - - REPLY_MAX = 0xff -}; - -/****************************************************************************** - * (0) - * Commonly used structures and definitions: - * Command header, rate_n_flags, txpower - * - *****************************************************************************/ - -/** - * iwlagn rate_n_flags bit fields - * - * rate_n_flags format is used in following iwlagn commands: - * REPLY_RX (response only) - * REPLY_RX_MPDU (response only) - * REPLY_TX (both command and response) - * REPLY_TX_LINK_QUALITY_CMD - * - * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): - * 2-0: 0) 6 Mbps - * 1) 12 Mbps - * 2) 18 Mbps - * 3) 24 Mbps - * 4) 36 Mbps - * 5) 48 Mbps - * 6) 54 Mbps - * 7) 60 Mbps - * - * 4-3: 0) Single stream (SISO) - * 1) Dual stream (MIMO) - * 2) Triple stream (MIMO) - * - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data - * - * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): - * 3-0: 0xD) 6 Mbps - * 0xF) 9 Mbps - * 0x5) 12 Mbps - * 0x7) 18 Mbps - * 0x9) 24 Mbps - * 0xB) 36 Mbps - * 0x1) 48 Mbps - * 0x3) 54 Mbps - * - * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): - * 6-0: 10) 1 Mbps - * 20) 2 Mbps - * 55) 5.5 Mbps - * 110) 11 Mbps - */ -#define RATE_MCS_CODE_MSK 0x7 -#define RATE_MCS_SPATIAL_POS 3 -#define RATE_MCS_SPATIAL_MSK 0x18 -#define RATE_MCS_HT_DUP_POS 5 -#define RATE_MCS_HT_DUP_MSK 0x20 -/* Both legacy and HT use bits 7:0 as the CCK/OFDM rate or HT MCS */ -#define RATE_MCS_RATE_MSK 0xff - -/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ -#define RATE_MCS_FLAGS_POS 8 -#define RATE_MCS_HT_POS 8 -#define RATE_MCS_HT_MSK 0x100 - -/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ -#define RATE_MCS_CCK_POS 9 -#define RATE_MCS_CCK_MSK 0x200 - -/* Bit 10: (1) Use Green Field preamble */ -#define RATE_MCS_GF_POS 10 -#define RATE_MCS_GF_MSK 0x400 - -/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ -#define RATE_MCS_HT40_POS 11 -#define RATE_MCS_HT40_MSK 0x800 - -/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ -#define RATE_MCS_DUP_POS 12 -#define RATE_MCS_DUP_MSK 0x1000 - -/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ -#define RATE_MCS_SGI_POS 13 -#define RATE_MCS_SGI_MSK 0x2000 - -/** - * rate_n_flags Tx antenna masks - * 4965 has 2 transmitters - * 5100 has 1 transmitter B - * 5150 has 1 transmitter A - * 5300 has 3 transmitters - * 5350 has 3 transmitters - * bit14:16 - */ -#define RATE_MCS_ANT_POS 14 -#define RATE_MCS_ANT_A_MSK 0x04000 -#define RATE_MCS_ANT_B_MSK 0x08000 -#define RATE_MCS_ANT_C_MSK 0x10000 -#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) -#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) -#define RATE_ANT_NUM 3 - -#define POWER_TABLE_NUM_ENTRIES 33 -#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 -#define POWER_TABLE_CCK_ENTRY 32 - -#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 -#define IWL_PWR_CCK_ENTRIES 2 - -/** - * struct tx_power_dual_stream - * - * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH - * - * Same format as iwl_tx_power_dual_stream, but __le32 - */ -struct tx_power_dual_stream { - __le32 dw; -} __packed; - -/** - * Command REPLY_TX_POWER_DBM_CMD = 0x98 - * struct iwlagn_tx_power_dbm_cmd - */ -#define IWLAGN_TX_POWER_AUTO 0x7f -#define IWLAGN_TX_POWER_NO_CLOSED (0x1 << 6) - -struct iwlagn_tx_power_dbm_cmd { - s8 global_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ - u8 flags; - s8 srv_chan_lmt; /*in half-dBm (e.g. 30 = 15 dBm) */ - u8 reserved; -} __packed; - -/** - * Command TX_ANT_CONFIGURATION_CMD = 0x98 - * This command is used to configure valid Tx antenna. - * By default uCode concludes the valid antenna according to the radio flavor. - * This command enables the driver to override/modify this conclusion. - */ -struct iwl_tx_ant_config_cmd { - __le32 valid; -} __packed; - -/****************************************************************************** - * (0a) - * Alive and Error Commands & Responses: - * - *****************************************************************************/ - -#define UCODE_VALID_OK cpu_to_le32(0x1) - -/** - * REPLY_ALIVE = 0x1 (response only, not a command) - * - * uCode issues this "alive" notification once the runtime image is ready - * to receive commands from the driver. This is the *second* "alive" - * notification that the driver will receive after rebooting uCode; - * this "alive" is indicated by subtype field != 9. - * - * See comments documenting "BSM" (bootstrap state machine). - * - * This response includes two pointers to structures within the device's - * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: - * - * 1) log_event_table_ptr indicates base of the event log. This traces - * a 256-entry history of uCode execution within a circular buffer. - * Its header format is: - * - * __le32 log_size; log capacity (in number of entries) - * __le32 type; (1) timestamp with each entry, (0) no timestamp - * __le32 wraps; # times uCode has wrapped to top of circular buffer - * __le32 write_index; next circular buffer entry that uCode would fill - * - * The header is followed by the circular buffer of log entries. Entries - * with timestamps have the following format: - * - * __le32 event_id; range 0 - 1500 - * __le32 timestamp; low 32 bits of TSF (of network, if associated) - * __le32 data; event_id-specific data value - * - * Entries without timestamps contain only event_id and data. - * - * - * 2) error_event_table_ptr indicates base of the error log. This contains - * information about any uCode error that occurs. For agn, the format - * of the error log is defined by struct iwl_error_event_table. - * - * The Linux driver can print both logs to the system log when a uCode error - * occurs. - */ - -/* - * Note: This structure is read from the device with IO accesses, - * and the reading already does the endian conversion. As it is - * read with u32-sized accesses, any members with a different size - * need to be ordered correctly though! - */ -struct iwl_error_event_table { - u32 valid; /* (nonzero) valid, (0) log is empty */ - u32 error_id; /* type of error */ - u32 pc; /* program counter */ - u32 blink1; /* branch link */ - u32 blink2; /* branch link */ - u32 ilink1; /* interrupt link */ - u32 ilink2; /* interrupt link */ - u32 data1; /* error-specific data */ - u32 data2; /* error-specific data */ - u32 line; /* source code line of error */ - u32 bcon_time; /* beacon timer */ - u32 tsf_low; /* network timestamp function timer */ - u32 tsf_hi; /* network timestamp function timer */ - u32 gp1; /* GP1 timer register */ - u32 gp2; /* GP2 timer register */ - u32 gp3; /* GP3 timer register */ - u32 ucode_ver; /* uCode version */ - u32 hw_ver; /* HW Silicon version */ - u32 brd_ver; /* HW board version */ - u32 log_pc; /* log program counter */ - u32 frame_ptr; /* frame pointer */ - u32 stack_ptr; /* stack pointer */ - u32 hcmd; /* last host command header */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: - * rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: - * host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: - * enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: - * time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: - * wico interrupt */ - u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ - u32 wait_event; /* wait event() caller address */ - u32 l2p_control; /* L2pControlField */ - u32 l2p_duration; /* L2pDurationField */ - u32 l2p_mhvalid; /* L2pMhValidBits */ - u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on - * (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the - * compilation */ - u32 flow_handler; /* FH read/write pointers, RX credit */ -} __packed; - -struct iwl_alive_resp { - u8 ucode_minor; - u8 ucode_major; - __le16 reserved1; - u8 sw_rev[8]; - u8 ver_type; - u8 ver_subtype; /* not "9" for runtime alive */ - __le16 reserved2; - __le32 log_event_table_ptr; /* SRAM address for event log */ - __le32 error_event_table_ptr; /* SRAM address for error log */ - __le32 timestamp; - __le32 is_valid; -} __packed; - -/* - * REPLY_ERROR = 0x2 (response only, not a command) - */ -struct iwl_error_resp { - __le32 error_type; - u8 cmd_id; - u8 reserved1; - __le16 bad_cmd_seq_num; - __le32 error_info; - __le64 timestamp; -} __packed; - -/****************************************************************************** - * (1) - * RXON Commands & Responses: - * - *****************************************************************************/ - -/* - * Rx config defines & structure - */ -/* rx_config device types */ -enum { - RXON_DEV_TYPE_AP = 1, - RXON_DEV_TYPE_ESS = 3, - RXON_DEV_TYPE_IBSS = 4, - RXON_DEV_TYPE_SNIFFER = 6, - RXON_DEV_TYPE_CP = 7, - RXON_DEV_TYPE_2STA = 8, - RXON_DEV_TYPE_P2P = 9, -}; - - -#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) -#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) -#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) -#define RXON_RX_CHAIN_VALID_POS (1) -#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) -#define RXON_RX_CHAIN_FORCE_SEL_POS (4) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) -#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) -#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) -#define RXON_RX_CHAIN_CNT_POS (10) -#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) -#define RXON_RX_CHAIN_MIMO_CNT_POS (12) -#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) -#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) - -/* rx_config flags */ -/* band & modulation selection */ -#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) -#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) -/* auto detection enable */ -#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) -/* TGg protection when tx */ -#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) -/* cck short slot & preamble */ -#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) -#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) -/* antenna selection */ -#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) -#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) -#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) -#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) -/* radar detection enable */ -#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) -#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) -/* rx response to host with 8-byte TSF -* (according to ON_AIR deassertion) */ -#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) - - -/* HT flags */ -#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) -#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) - -#define RXON_FLG_HT_OPERATING_MODE_POS (23) - -#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) -#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) - -#define RXON_FLG_CHANNEL_MODE_POS (25) -#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) - -/* channel mode */ -enum { - CHANNEL_MODE_LEGACY = 0, - CHANNEL_MODE_PURE_40 = 1, - CHANNEL_MODE_MIXED = 2, - CHANNEL_MODE_RESERVED = 3, -}; -#define RXON_FLG_CHANNEL_MODE_LEGACY cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) -#define RXON_FLG_CHANNEL_MODE_PURE_40 cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) -#define RXON_FLG_CHANNEL_MODE_MIXED cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) - -/* CTS to self (if spec allows) flag */ -#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) - -/* rx_config filter flags */ -/* accept all data frames */ -#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) -/* pass control & management to host */ -#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) -/* accept multi-cast */ -#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) -/* don't decrypt uni-cast frames */ -#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) -/* don't decrypt multi-cast frames */ -#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) -/* STA is associated */ -#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) -/* transfer to host non bssid beacons in associated state */ -#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) - -/** - * REPLY_RXON = 0x10 (command, has simple generic response) - * - * RXON tunes the radio tuner to a service channel, and sets up a number - * of parameters that are used primarily for Rx, but also for Tx operations. - * - * NOTE: When tuning to a new channel, driver must set the - * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent - * info within the device, including the station tables, tx retry - * rate tables, and txpower tables. Driver must build a new station - * table and txpower table before transmitting anything on the RXON - * channel. - * - * NOTE: All RXONs wipe clean the internal txpower table. Driver must - * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), - * regardless of whether RXON_FILTER_ASSOC_MSK is set. - */ - -struct iwl_rxon_cmd { - u8 node_addr[6]; - __le16 reserved1; - u8 bssid_addr[6]; - __le16 reserved2; - u8 wlap_bssid_addr[6]; - __le16 reserved3; - u8 dev_type; - u8 air_propagation; - __le16 rx_chain; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 assoc_id; - __le32 flags; - __le32 filter_flags; - __le16 channel; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; - u8 ofdm_ht_triple_stream_basic_rates; - u8 reserved5; - __le16 acquisition_data; - __le16 reserved6; -} __packed; - -/* - * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) - */ -struct iwl_rxon_assoc_cmd { - __le32 flags; - __le32 filter_flags; - u8 ofdm_basic_rates; - u8 cck_basic_rates; - __le16 reserved1; - u8 ofdm_ht_single_stream_basic_rates; - u8 ofdm_ht_dual_stream_basic_rates; - u8 ofdm_ht_triple_stream_basic_rates; - u8 reserved2; - __le16 rx_chain_select_flags; - __le16 acquisition_data; - __le32 reserved3; -} __packed; - -#define IWL_CONN_MAX_LISTEN_INTERVAL 10 -#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ - -/* - * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) - */ -struct iwl_rxon_time_cmd { - __le64 timestamp; - __le16 beacon_interval; - __le16 atim_window; - __le32 beacon_init_val; - __le16 listen_interval; - u8 dtim_period; - u8 delta_cp_bss_tbtts; -} __packed; - -/* - * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) - */ -/** - * struct iwl5000_channel_switch_cmd - * @band: 0- 5.2GHz, 1- 2.4GHz - * @expect_beacon: 0- resume transmits after channel switch - * 1- wait for beacon to resume transmits - * @channel: new channel number - * @rxon_flags: Rx on flags - * @rxon_filter_flags: filtering parameters - * @switch_time: switch time in extended beacon format - * @reserved: reserved bytes - */ -struct iwl5000_channel_switch_cmd { - u8 band; - u8 expect_beacon; - __le16 channel; - __le32 rxon_flags; - __le32 rxon_filter_flags; - __le32 switch_time; - __le32 reserved[2][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; -} __packed; - -/** - * struct iwl6000_channel_switch_cmd - * @band: 0- 5.2GHz, 1- 2.4GHz - * @expect_beacon: 0- resume transmits after channel switch - * 1- wait for beacon to resume transmits - * @channel: new channel number - * @rxon_flags: Rx on flags - * @rxon_filter_flags: filtering parameters - * @switch_time: switch time in extended beacon format - * @reserved: reserved bytes - */ -struct iwl6000_channel_switch_cmd { - u8 band; - u8 expect_beacon; - __le16 channel; - __le32 rxon_flags; - __le32 rxon_filter_flags; - __le32 switch_time; - __le32 reserved[3][IWL_PWR_NUM_HT_OFDM_ENTRIES + IWL_PWR_CCK_ENTRIES]; -} __packed; - -/* - * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) - */ -struct iwl_csa_notification { - __le16 band; - __le16 channel; - __le32 status; /* 0 - OK, 1 - fail */ -} __packed; - -/****************************************************************************** - * (2) - * Quality-of-Service (QOS) Commands & Responses: - * - *****************************************************************************/ - -/** - * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM - * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd - * - * @cw_min: Contention window, start value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x0f. - * @cw_max: Contention window, max value in numbers of slots. - * Should be a power-of-2, minus 1. Device's default is 0x3f. - * @aifsn: Number of slots in Arbitration Interframe Space (before - * performing random backoff timing prior to Tx). Device default 1. - * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. - * - * Device will automatically increase contention window by (2*CW) + 1 for each - * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW - * value, to cap the CW value. - */ -struct iwl_ac_qos { - __le16 cw_min; - __le16 cw_max; - u8 aifsn; - u8 reserved1; - __le16 edca_txop; -} __packed; - -/* QoS flags defines */ -#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) -#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) -#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) - -/* Number of Access Categories (AC) (EDCA), queues 0..3 */ -#define AC_NUM 4 - -/* - * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) - * - * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs - * 0: Background, 1: Best Effort, 2: Video, 3: Voice. - */ -struct iwl_qosparam_cmd { - __le32 qos_flags; - struct iwl_ac_qos ac[AC_NUM]; -} __packed; - -/****************************************************************************** - * (3) - * Add/Modify Stations Commands & Responses: - * - *****************************************************************************/ -/* - * Multi station support - */ - -/* Special, dedicated locations within device's station table */ -#define IWL_AP_ID 0 -#define IWL_AP_ID_PAN 1 -#define IWL_STA_ID 2 -#define IWLAGN_PAN_BCAST_ID 14 -#define IWLAGN_BROADCAST_ID 15 -#define IWLAGN_STATION_COUNT 16 - -#define IWL_INVALID_STATION 255 -#define IWL_MAX_TID_COUNT 8 -#define IWL_TID_NON_QOS IWL_MAX_TID_COUNT - -#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) -#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) -#define STA_FLG_PAN_STATION cpu_to_le32(1 << 13) -#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) -#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) -#define STA_FLG_MAX_AGG_SIZE_POS (19) -#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) -#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) -#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) -#define STA_FLG_AGG_MPDU_DENSITY_POS (23) -#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) - -/* Use in mode field. 1: modify existing entry, 0: add new station entry */ -#define STA_CONTROL_MODIFY_MSK 0x01 - -/* key flags __le16*/ -#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) -#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) -#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) -#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) -#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) - -#define STA_KEY_FLG_KEYID_POS 8 -#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) -/* wep key is either from global key (0) or from station info array (1) */ -#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) - -/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ -#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) -#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) -#define STA_KEY_MAX_NUM 8 -#define STA_KEY_MAX_NUM_PAN 16 -/* must not match WEP_INVALID_OFFSET */ -#define IWLAGN_HW_KEY_DEFAULT 0xfe - -/* Flags indicate whether to modify vs. don't change various station params */ -#define STA_MODIFY_KEY_MASK 0x01 -#define STA_MODIFY_TID_DISABLE_TX 0x02 -#define STA_MODIFY_TX_RATE_MSK 0x04 -#define STA_MODIFY_ADDBA_TID_MSK 0x08 -#define STA_MODIFY_DELBA_TID_MSK 0x10 -#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 - -/* Receiver address (actually, Rx station's index into station table), - * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ -#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) - -/* agn */ -struct iwl_keyinfo { - __le16 key_flags; - u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ - u8 reserved1; - __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ - u8 key_offset; - u8 reserved2; - u8 key[16]; /* 16-byte unicast decryption key */ - __le64 tx_secur_seq_cnt; - __le64 hw_tkip_mic_rx_key; - __le64 hw_tkip_mic_tx_key; -} __packed; - -/** - * struct sta_id_modify - * @addr[ETH_ALEN]: station's MAC address - * @sta_id: index of station in uCode's station table - * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change - * - * Driver selects unused table index when adding new station, - * or the index to a pre-existing station entry when modifying that station. - * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). - * - * modify_mask flags select which parameters to modify vs. leave alone. - */ -struct sta_id_modify { - u8 addr[ETH_ALEN]; - __le16 reserved1; - u8 sta_id; - u8 modify_mask; - __le16 reserved2; -} __packed; - -/* - * REPLY_ADD_STA = 0x18 (command) - * - * The device contains an internal table of per-station information, - * with info on security keys, aggregation parameters, and Tx rates for - * initial Tx attempt and any retries (agn devices uses - * REPLY_TX_LINK_QUALITY_CMD, - * - * REPLY_ADD_STA sets up the table entry for one station, either creating - * a new entry, or modifying a pre-existing one. - * - * NOTE: RXON command (without "associated" bit set) wipes the station table - * clean. Moving into RF_KILL state does this also. Driver must set up - * new station table before transmitting anything on the RXON channel - * (except active scans or active measurements; those commands carry - * their own txpower/rate setup data). - * - * When getting started on a new channel, driver must set up the - * IWL_BROADCAST_ID entry (last entry in the table). For a client - * station in a BSS, once an AP is selected, driver sets up the AP STA - * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP - * are all that are needed for a BSS client station. If the device is - * used as AP, or in an IBSS network, driver must set up station table - * entries for all STAs in network, starting with index IWL_STA_ID. - */ - -struct iwl_addsta_cmd { - u8 mode; /* 1: modify existing, 0: add new station */ - u8 reserved[3]; - struct sta_id_modify sta; - struct iwl_keyinfo key; - __le32 station_flags; /* STA_FLG_* */ - __le32 station_flags_msk; /* STA_FLG_* */ - - /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) - * corresponding to bit (e.g. bit 5 controls TID 5). - * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ - __le16 tid_disable_tx; - __le16 legacy_reserved; - - /* TID for which to add block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - u8 add_immediate_ba_tid; - - /* TID for which to remove block-ack support. - * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ - u8 remove_immediate_ba_tid; - - /* Starting Sequence Number for added block-ack support. - * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ - __le16 add_immediate_ba_ssn; - - /* - * Number of packets OK to transmit to station even though - * it is asleep -- used to synchronise PS-poll and u-APSD - * responses while ucode keeps track of STA sleep state. - */ - __le16 sleep_tx_count; - - __le16 reserved2; -} __packed; - - -#define ADD_STA_SUCCESS_MSK 0x1 -#define ADD_STA_NO_ROOM_IN_TABLE 0x2 -#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 -#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 -/* - * REPLY_ADD_STA = 0x18 (response) - */ -struct iwl_add_sta_resp { - u8 status; /* ADD_STA_* */ -} __packed; - -#define REM_STA_SUCCESS_MSK 0x1 -/* - * REPLY_REM_STA = 0x19 (response) - */ -struct iwl_rem_sta_resp { - u8 status; -} __packed; - -/* - * REPLY_REM_STA = 0x19 (command) - */ -struct iwl_rem_sta_cmd { - u8 num_sta; /* number of removed stations */ - u8 reserved[3]; - u8 addr[ETH_ALEN]; /* MAC addr of the first station */ - u8 reserved2[2]; -} __packed; - - -/* WiFi queues mask */ -#define IWL_SCD_BK_MSK cpu_to_le32(BIT(0)) -#define IWL_SCD_BE_MSK cpu_to_le32(BIT(1)) -#define IWL_SCD_VI_MSK cpu_to_le32(BIT(2)) -#define IWL_SCD_VO_MSK cpu_to_le32(BIT(3)) -#define IWL_SCD_MGMT_MSK cpu_to_le32(BIT(3)) - -/* PAN queues mask */ -#define IWL_PAN_SCD_BK_MSK cpu_to_le32(BIT(4)) -#define IWL_PAN_SCD_BE_MSK cpu_to_le32(BIT(5)) -#define IWL_PAN_SCD_VI_MSK cpu_to_le32(BIT(6)) -#define IWL_PAN_SCD_VO_MSK cpu_to_le32(BIT(7)) -#define IWL_PAN_SCD_MGMT_MSK cpu_to_le32(BIT(7)) -#define IWL_PAN_SCD_MULTICAST_MSK cpu_to_le32(BIT(8)) - -#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) - -#define IWL_DROP_SINGLE 0 -#define IWL_DROP_ALL (BIT(IWL_RXON_CTX_BSS) | BIT(IWL_RXON_CTX_PAN)) - -/* - * REPLY_TXFIFO_FLUSH = 0x1e(command and response) - * - * When using full FIFO flush this command checks the scheduler HW block WR/RD - * pointers to check if all the frames were transferred by DMA into the - * relevant TX FIFO queue. Only when the DMA is finished and the queue is - * empty the command can finish. - * This command is used to flush the TXFIFO from transmit commands, it may - * operate on single or multiple queues, the command queue can't be flushed by - * this command. The command response is returned when all the queue flush - * operations are done. Each TX command flushed return response with the FLUSH - * status set in the TX response status. When FIFO flush operation is used, - * the flush operation ends when both the scheduler DMA done and TXFIFO empty - * are set. - * - * @fifo_control: bit mask for which queues to flush - * @flush_control: flush controls - * 0: Dump single MSDU - * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. - * 2: Dump all FIFO - */ -struct iwl_txfifo_flush_cmd { - __le32 fifo_control; - __le16 flush_control; - __le16 reserved; -} __packed; - -/* - * REPLY_WEP_KEY = 0x20 - */ -struct iwl_wep_key { - u8 key_index; - u8 key_offset; - u8 reserved1[2]; - u8 key_size; - u8 reserved2[3]; - u8 key[16]; -} __packed; - -struct iwl_wep_cmd { - u8 num_keys; - u8 global_key_type; - u8 flags; - u8 reserved; - struct iwl_wep_key key[0]; -} __packed; - -#define WEP_KEY_WEP_TYPE 1 -#define WEP_KEYS_MAX 4 -#define WEP_INVALID_OFFSET 0xff -#define WEP_KEY_LEN_64 5 -#define WEP_KEY_LEN_128 13 - -/****************************************************************************** - * (4) - * Rx Responses: - * - *****************************************************************************/ - -#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) -#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) - -#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) -#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) -#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) -#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) -#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 -#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 - -#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) -#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) -#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) -#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) -#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) -#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) - -#define RX_RES_STATUS_STATION_FOUND (1<<6) -#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) - -#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) -#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) -#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) -#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) -#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) - -#define RX_MPDU_RES_STATUS_ICV_OK (0x20) -#define RX_MPDU_RES_STATUS_MIC_OK (0x40) -#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) -#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) - - -#define IWLAGN_RX_RES_PHY_CNT 8 -#define IWLAGN_RX_RES_AGC_IDX 1 -#define IWLAGN_RX_RES_RSSI_AB_IDX 2 -#define IWLAGN_RX_RES_RSSI_C_IDX 3 -#define IWLAGN_OFDM_AGC_MSK 0xfe00 -#define IWLAGN_OFDM_AGC_BIT_POS 9 -#define IWLAGN_OFDM_RSSI_INBAND_A_BITMSK 0x00ff -#define IWLAGN_OFDM_RSSI_ALLBAND_A_BITMSK 0xff00 -#define IWLAGN_OFDM_RSSI_A_BIT_POS 0 -#define IWLAGN_OFDM_RSSI_INBAND_B_BITMSK 0xff0000 -#define IWLAGN_OFDM_RSSI_ALLBAND_B_BITMSK 0xff000000 -#define IWLAGN_OFDM_RSSI_B_BIT_POS 16 -#define IWLAGN_OFDM_RSSI_INBAND_C_BITMSK 0x00ff -#define IWLAGN_OFDM_RSSI_ALLBAND_C_BITMSK 0xff00 -#define IWLAGN_OFDM_RSSI_C_BIT_POS 0 - -struct iwlagn_non_cfg_phy { - __le32 non_cfg_phy[IWLAGN_RX_RES_PHY_CNT]; /* up to 8 phy entries */ -} __packed; - - -/* - * REPLY_RX = 0xc3 (response only, not a command) - * Used only for legacy (non 11n) frames. - */ -struct iwl_rx_phy_res { - u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ - u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ - u8 stat_id; /* configurable DSP phy data set ID */ - u8 reserved1; - __le64 timestamp; /* TSF at on air rise */ - __le32 beacon_time_stamp; /* beacon at on-air rise */ - __le16 phy_flags; /* general phy flags: band, modulation, ... */ - __le16 channel; /* channel number */ - u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ - __le32 rate_n_flags; /* RATE_MCS_* */ - __le16 byte_count; /* frame's byte-count */ - __le16 frame_time; /* frame's time on the air */ -} __packed; - -struct iwl_rx_mpdu_res_start { - __le16 byte_count; - __le16 reserved; -} __packed; - - -/****************************************************************************** - * (5) - * Tx Commands & Responses: - * - * Driver must place each REPLY_TX command into one of the prioritized Tx - * queues in host DRAM, shared between driver and device (see comments for - * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode - * are preparing to transmit, the device pulls the Tx command over the PCI - * bus via one of the device's Tx DMA channels, to fill an internal FIFO - * from which data will be transmitted. - * - * uCode handles all timing and protocol related to control frames - * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler - * handle reception of block-acks; uCode updates the host driver via - * REPLY_COMPRESSED_BA. - * - * uCode handles retrying Tx when an ACK is expected but not received. - * This includes trying lower data rates than the one requested in the Tx - * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn). - * - * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. - * This command must be executed after every RXON command, before Tx can occur. - *****************************************************************************/ - -/* REPLY_TX Tx flags field */ - -/* - * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it - * before this frame. if CTS-to-self required check - * RXON_FLG_SELF_CTS_EN status. - */ -#define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0) - -/* 1: Expect ACK from receiving station - * 0: Don't expect ACK (MAC header's duration field s/b 0) - * Set this for unicast frames, but not broadcast/multicast. */ -#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) - -/* For agn devices: - * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). - * Tx command's initial_rate_index indicates first rate to try; - * uCode walks through table for additional Tx attempts. - * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. - * This rate will be used for all Tx attempts; it will not be scaled. */ -#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) - -/* 1: Expect immediate block-ack. - * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ -#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) - -/* Tx antenna selection field; reserved (0) for agn devices. */ -#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) - -/* 1: Ignore Bluetooth priority for this frame. - * 0: Delay Tx until Bluetooth device is done (normal usage). */ -#define TX_CMD_FLG_IGNORE_BT cpu_to_le32(1 << 12) - -/* 1: uCode overrides sequence control field in MAC header. - * 0: Driver provides sequence control field in MAC header. - * Set this for management frames, non-QOS data frames, non-unicast frames, - * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ -#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) - -/* 1: This frame is non-last MPDU; more fragments are coming. - * 0: Last fragment, or not using fragmentation. */ -#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) - -/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. - * 0: No TSF required in outgoing frame. - * Set this for transmitting beacons and probe responses. */ -#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) - -/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword - * alignment of frame's payload data field. - * 0: No pad - * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 - * field (but not both). Driver must align frame data (i.e. data following - * MAC header) to DWORD boundary. */ -#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) - -/* accelerate aggregation support - * 0 - no CCMP encryption; 1 - CCMP encryption */ -#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) - -/* HCCA-AP - disable duration overwriting. */ -#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) - - -/* - * TX command security control - */ -#define TX_CMD_SEC_WEP 0x01 -#define TX_CMD_SEC_CCM 0x02 -#define TX_CMD_SEC_TKIP 0x03 -#define TX_CMD_SEC_MSK 0x03 -#define TX_CMD_SEC_SHIFT 6 -#define TX_CMD_SEC_KEY128 0x08 - -/* - * security overhead sizes - */ -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 -#define CCMP_MIC_LEN 8 -#define TKIP_ICV_LEN 4 - -/* - * REPLY_TX = 0x1c (command) - */ - -/* - * 4965 uCode updates these Tx attempt count values in host DRAM. - * Used for managing Tx retries when expecting block-acks. - * Driver should set these fields to 0. - */ -struct iwl_dram_scratch { - u8 try_cnt; /* Tx attempts */ - u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ - __le16 reserved; -} __packed; - -struct iwl_tx_cmd { - /* - * MPDU byte count: - * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * NOTE: Does not include Tx command bytes, post-MAC pad bytes, - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i - * Range: 14-2342 bytes. - */ - __le16 len; - - /* - * MPDU or MSDU byte count for next frame. - * Used for fragmentation and bursting, but not 11n aggregation. - * Same as "len", but for next frame. Set to 0 if not applicable. - */ - __le16 next_frame_len; - - __le32 tx_flags; /* TX_CMD_FLG_* */ - - /* uCode may modify this field of the Tx command (in host DRAM!). - * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ - struct iwl_dram_scratch scratch; - - /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* Index of destination station in uCode's station table */ - u8 sta_id; - - /* Type of security encryption: CCM or TKIP */ - u8 sec_ctl; /* TX_CMD_SEC_* */ - - /* - * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial - * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for - * data frames, this field may be used to selectively reduce initial - * rate (via non-0 value) for special frames (e.g. management), while - * still supporting rate scaling for all frames. - */ - u8 initial_rate_index; - u8 reserved; - u8 key[16]; - __le16 next_frame_flags; - __le16 reserved2; - union { - __le32 life_time; - __le32 attempt; - } stop_time; - - /* Host DRAM physical address pointer to "scratch" in this command. - * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - - u8 rts_retry_limit; /*byte 50 */ - u8 data_retry_limit; /*byte 51 */ - u8 tid_tspec; - union { - __le16 pm_frame_timeout; - __le16 attempt_duration; - } timeout; - - /* - * Duration of EDCA burst Tx Opportunity, in 32-usec units. - * Set this if txop time is not specified by HCCA protocol (e.g. by AP). - */ - __le16 driver_txop; - - /* - * MAC header goes here, followed by 2 bytes padding if MAC header - * length is 26 or 30 bytes, followed by payload data - */ - u8 payload[0]; - struct ieee80211_hdr hdr[0]; -} __packed; - -/* - * TX command response is sent after *agn* transmission attempts. - * - * both postpone and abort status are expected behavior from uCode. there is - * no special operation required from driver; except for RFKILL_FLUSH, - * which required tx flush host command to flush all the tx frames in queues - */ -enum { - TX_STATUS_SUCCESS = 0x01, - TX_STATUS_DIRECT_DONE = 0x02, - /* postpone TX */ - TX_STATUS_POSTPONE_DELAY = 0x40, - TX_STATUS_POSTPONE_FEW_BYTES = 0x41, - TX_STATUS_POSTPONE_BT_PRIO = 0x42, - TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, - TX_STATUS_POSTPONE_CALC_TTAK = 0x44, - /* abort TX */ - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, - TX_STATUS_FAIL_SHORT_LIMIT = 0x82, - TX_STATUS_FAIL_LONG_LIMIT = 0x83, - TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, - TX_STATUS_FAIL_DRAIN_FLOW = 0x85, - TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, - TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, - TX_STATUS_FAIL_DEST_PS = 0x88, - TX_STATUS_FAIL_HOST_ABORTED = 0x89, - TX_STATUS_FAIL_BT_RETRY = 0x8a, - TX_STATUS_FAIL_STA_INVALID = 0x8b, - TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, - TX_STATUS_FAIL_TID_DISABLE = 0x8d, - TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, - TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, - TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, - TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, -}; - -#define TX_PACKET_MODE_REGULAR 0x0000 -#define TX_PACKET_MODE_BURST_SEQ 0x0100 -#define TX_PACKET_MODE_BURST_FIRST 0x0200 - -enum { - TX_POWER_PA_NOT_ACTIVE = 0x0, -}; - -enum { - TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ - TX_STATUS_DELAY_MSK = 0x00000040, - TX_STATUS_ABORT_MSK = 0x00000080, - TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ - TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ - TX_RESERVED = 0x00780000, /* bits 19:22 */ - TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ - TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ -}; - -/* ******************************* - * TX aggregation status - ******************************* */ - -enum { - AGG_TX_STATE_TRANSMITTED = 0x00, - AGG_TX_STATE_UNDERRUN_MSK = 0x01, - AGG_TX_STATE_BT_PRIO_MSK = 0x02, - AGG_TX_STATE_FEW_BYTES_MSK = 0x04, - AGG_TX_STATE_ABORT_MSK = 0x08, - AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, - AGG_TX_STATE_LAST_SENT_BT_KILL_MSK = 0x40, - AGG_TX_STATE_SCD_QUERY_MSK = 0x80, - AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, - AGG_TX_STATE_RESPONSE_MSK = 0x1ff, - AGG_TX_STATE_DUMP_TX_MSK = 0x200, - AGG_TX_STATE_DELAY_TX_MSK = 0x400 -}; - -#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ -#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ - -#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK | \ - AGG_TX_STATE_LAST_SENT_BT_KILL_MSK) - -/* # tx attempts for first frame in aggregation */ -#define AGG_TX_STATE_TRY_CNT_POS 12 -#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 - -/* Command ID and sequence number of Tx command for this frame */ -#define AGG_TX_STATE_SEQ_NUM_POS 16 -#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 - -/* - * REPLY_TX = 0x1c (response) - * - * This response may be in one of two slightly different formats, indicated - * by the frame_count field: - * - * 1) No aggregation (frame_count == 1). This reports Tx results for - * a single frame. Multiple attempts, at various bit rates, may have - * been made for this frame. - * - * 2) Aggregation (frame_count > 1). This reports Tx results for - * 2 or more frames that used block-acknowledge. All frames were - * transmitted at same rate. Rate scaling may have been used if first - * frame in this new agg block failed in previous agg block(s). - * - * Note that, for aggregation, ACK (block-ack) status is not delivered here; - * block-ack has not been received by the time the agn device records - * this status. - * This status relates to reasons the tx might have been blocked or aborted - * within the sending station (this agn device), rather than whether it was - * received successfully by the destination station. - */ -struct agg_tx_status { - __le16 status; - __le16 sequence; -} __packed; - -/* - * definitions for initial rate index field - * bits [3:0] initial rate index - * bits [6:4] rate table color, used for the initial rate - * bit-7 invalid rate indication - * i.e. rate was not chosen from rate table - * or rate table color was changed during frame retries - * refer tlc rate info - */ - -#define IWL50_TX_RES_INIT_RATE_INDEX_POS 0 -#define IWL50_TX_RES_INIT_RATE_INDEX_MSK 0x0f -#define IWL50_TX_RES_RATE_TABLE_COLOR_POS 4 -#define IWL50_TX_RES_RATE_TABLE_COLOR_MSK 0x70 -#define IWL50_TX_RES_INV_RATE_INDEX_MSK 0x80 - -/* refer to ra_tid */ -#define IWLAGN_TX_RES_TID_POS 0 -#define IWLAGN_TX_RES_TID_MSK 0x0f -#define IWLAGN_TX_RES_RA_POS 4 -#define IWLAGN_TX_RES_RA_MSK 0xf0 - -struct iwlagn_tx_resp { - u8 frame_count; /* 1 no aggregation, >1 aggregation */ - u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ - u8 failure_rts; /* # failures due to unsuccessful RTS */ - u8 failure_frame; /* # failures due to no ACK (unused for agg) */ - - /* For non-agg: Rate at which frame was successful. - * For agg: Rate at which all frames were transmitted. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* For non-agg: RTS + CTS + frame tx attempts time + ACK. - * For agg: RTS + CTS + aggregation tx time + block-ack time. */ - __le16 wireless_media_time; /* uSecs */ - - u8 pa_status; /* RF power amplifier measurement (not used) */ - u8 pa_integ_res_a[3]; - u8 pa_integ_res_b[3]; - u8 pa_integ_res_C[3]; - - __le32 tfd_info; - __le16 seq_ctl; - __le16 byte_cnt; - u8 tlc_info; - u8 ra_tid; /* tid (0:3), sta_id (4:7) */ - __le16 frame_ctrl; - /* - * For non-agg: frame status TX_STATUS_* - * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status - * fields follow this one, up to frame_count. - * Bit fields: - * 11- 0: AGG_TX_STATE_* status code - * 15-12: Retry count for 1st frame in aggregation (retries - * occur if tx failed for this frame when it was a - * member of a previous aggregation block). If rate - * scaling is used, retry count indicates the rate - * table entry used for all frames in the new agg. - * 31-16: Sequence # for this frame's Tx cmd (not SSN!) - */ - struct agg_tx_status status; /* TX status (in aggregation - - * status of 1st frame) */ -} __packed; -/* - * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) - * - * Reports Block-Acknowledge from recipient station - */ -struct iwl_compressed_ba_resp { - __le32 sta_addr_lo32; - __le16 sta_addr_hi16; - __le16 reserved; - - /* Index of recipient (BA-sending) station in uCode's station table */ - u8 sta_id; - u8 tid; - __le16 seq_ctl; - __le64 bitmap; - __le16 scd_flow; - __le16 scd_ssn; - u8 txed; /* number of frames sent */ - u8 txed_2_done; /* number of frames acked */ -} __packed; - -/* - * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) - * - */ - -/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ -#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) - -/* # of EDCA prioritized tx fifos */ -#define LINK_QUAL_AC_NUM AC_NUM - -/* # entries in rate scale table to support Tx retries */ -#define LINK_QUAL_MAX_RETRY_NUM 16 - -/* Tx antenna selection values */ -#define LINK_QUAL_ANT_A_MSK (1 << 0) -#define LINK_QUAL_ANT_B_MSK (1 << 1) -#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) - - -/** - * struct iwl_link_qual_general_params - * - * Used in REPLY_TX_LINK_QUALITY_CMD - */ -struct iwl_link_qual_general_params { - u8 flags; - - /* No entries at or above this (driver chosen) index contain MIMO */ - u8 mimo_delimiter; - - /* Best single antenna to use for single stream (legacy, SISO). */ - u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ - - /* Best antennas to use for MIMO (unused for 4965, assumes both). */ - u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ - - /* - * If driver needs to use different initial rates for different - * EDCA QOS access categories (as implemented by tx fifos 0-3), - * this table will set that up, by indicating the indexes in the - * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. - * Otherwise, driver should set all entries to 0. - * - * Entry usage: - * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice - * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. - */ - u8 start_rate_index[LINK_QUAL_AC_NUM]; -} __packed; - -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ -#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) -#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) - -#define LINK_QUAL_AGG_DISABLE_START_DEF (3) -#define LINK_QUAL_AGG_DISABLE_START_MAX (255) -#define LINK_QUAL_AGG_DISABLE_START_MIN (0) - -#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) -#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) - -/** - * struct iwl_link_qual_agg_params - * - * Used in REPLY_TX_LINK_QUALITY_CMD - */ -struct iwl_link_qual_agg_params { - - /* - *Maximum number of uSec in aggregation. - * default set to 4000 (4 milliseconds) if not configured in .cfg - */ - __le16 agg_time_limit; - - /* - * Number of Tx retries allowed for a frame, before that frame will - * no longer be considered for the start of an aggregation sequence - * (scheduler will then try to tx it as single frame). - * Driver should set this to 3. - */ - u8 agg_dis_start_th; - - /* - * Maximum number of frames in aggregation. - * 0 = no limit (default). 1 = no aggregation. - * Other values = max # frames in aggregation. - */ - u8 agg_frame_cnt_limit; - - __le32 reserved; -} __packed; - -/* - * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) - * - * For agn devices - * - * Each station in the agn device's internal station table has its own table - * of 16 - * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when - * an ACK is not received. This command replaces the entire table for - * one station. - * - * NOTE: Station must already be in agn device's station table. - * Use REPLY_ADD_STA. - * - * The rate scaling procedures described below work well. Of course, other - * procedures are possible, and may work better for particular environments. - * - * - * FILLING THE RATE TABLE - * - * Given a particular initial rate and mode, as determined by the rate - * scaling algorithm described below, the Linux driver uses the following - * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the - * Link Quality command: - * - * - * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: - * a) Use this same initial rate for first 3 entries. - * b) Find next lower available rate using same mode (SISO or MIMO), - * use for next 3 entries. If no lower rate available, switch to - * legacy mode (no HT40 channel, no MIMO, no short guard interval). - * c) If using MIMO, set command's mimo_delimiter to number of entries - * using MIMO (3 or 6). - * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, - * no MIMO, no short guard interval), at the next lower bit rate - * (e.g. if second HT bit rate was 54, try 48 legacy), and follow - * legacy procedure for remaining table entries. - * - * 2) If using legacy initial rate: - * a) Use the initial rate for only one entry. - * b) For each following entry, reduce the rate to next lower available - * rate, until reaching the lowest available rate. - * c) When reducing rate, also switch antenna selection. - * d) Once lowest available rate is reached, repeat this rate until - * rate table is filled (16 entries), switching antenna each entry. - * - * - * ACCUMULATING HISTORY - * - * The rate scaling algorithm for agn devices, as implemented in Linux driver, - * uses two sets of frame Tx success history: One for the current/active - * modulation mode, and one for a speculative/search mode that is being - * attempted. If the speculative mode turns out to be more effective (i.e. - * actual transfer rate is better), then the driver continues to use the - * speculative mode as the new current active mode. - * - * Each history set contains, separately for each possible rate, data for a - * sliding window of the 62 most recent tx attempts at that rate. The data - * includes a shifting bitmap of success(1)/failure(0), and sums of successful - * and attempted frames, from which the driver can additionally calculate a - * success ratio (success / attempted) and number of failures - * (attempted - success), and control the size of the window (attempted). - * The driver uses the bit map to remove successes from the success sum, as - * the oldest tx attempts fall out of the window. - * - * When the agn device makes multiple tx attempts for a given frame, each - * attempt might be at a different rate, and have different modulation - * characteristics (e.g. antenna, fat channel, short guard interval), as set - * up in the rate scaling table in the Link Quality command. The driver must - * determine which rate table entry was used for each tx attempt, to determine - * which rate-specific history to update, and record only those attempts that - * match the modulation characteristics of the history set. - * - * When using block-ack (aggregation), all frames are transmitted at the same - * rate, since there is no per-attempt acknowledgment from the destination - * station. The Tx response struct iwl_tx_resp indicates the Tx rate in - * rate_n_flags field. After receiving a block-ack, the driver can update - * history for the entire block all at once. - * - * - * FINDING BEST STARTING RATE: - * - * When working with a selected initial modulation mode (see below), the - * driver attempts to find a best initial rate. The initial rate is the - * first entry in the Link Quality command's rate table. - * - * 1) Calculate actual throughput (success ratio * expected throughput, see - * table below) for current initial rate. Do this only if enough frames - * have been attempted to make the value meaningful: at least 6 failed - * tx attempts, or at least 8 successes. If not enough, don't try rate - * scaling yet. - * - * 2) Find available rates adjacent to current initial rate. Available means: - * a) supported by hardware && - * b) supported by association && - * c) within any constraints selected by user - * - * 3) Gather measured throughputs for adjacent rates. These might not have - * enough history to calculate a throughput. That's okay, we might try - * using one of them anyway! - * - * 4) Try decreasing rate if, for current rate: - * a) success ratio is < 15% || - * b) lower adjacent rate has better measured throughput || - * c) higher adjacent rate has worse throughput, and lower is unmeasured - * - * As a sanity check, if decrease was determined above, leave rate - * unchanged if: - * a) lower rate unavailable - * b) success ratio at current rate > 85% (very good) - * c) current measured throughput is better than expected throughput - * of lower rate (under perfect 100% tx conditions, see table below) - * - * 5) Try increasing rate if, for current rate: - * a) success ratio is < 15% || - * b) both adjacent rates' throughputs are unmeasured (try it!) || - * b) higher adjacent rate has better measured throughput || - * c) lower adjacent rate has worse throughput, and higher is unmeasured - * - * As a sanity check, if increase was determined above, leave rate - * unchanged if: - * a) success ratio at current rate < 70%. This is not particularly - * good performance; higher rate is sure to have poorer success. - * - * 6) Re-evaluate the rate after each tx frame. If working with block- - * acknowledge, history and statistics may be calculated for the entire - * block (including prior history that fits within the history windows), - * before re-evaluation. - * - * FINDING BEST STARTING MODULATION MODE: - * - * After working with a modulation mode for a "while" (and doing rate scaling), - * the driver searches for a new initial mode in an attempt to improve - * throughput. The "while" is measured by numbers of attempted frames: - * - * For legacy mode, search for new mode after: - * 480 successful frames, or 160 failed frames - * For high-throughput modes (SISO or MIMO), search for new mode after: - * 4500 successful frames, or 400 failed frames - * - * Mode switch possibilities are (3 for each mode): - * - * For legacy: - * Change antenna, try SISO (if HT association), try MIMO (if HT association) - * For SISO: - * Change antenna, try MIMO, try shortened guard interval (SGI) - * For MIMO: - * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) - * - * When trying a new mode, use the same bit rate as the old/current mode when - * trying antenna switches and shortened guard interval. When switching to - * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate - * for which the expected throughput (under perfect conditions) is about the - * same or slightly better than the actual measured throughput delivered by - * the old/current mode. - * - * Actual throughput can be estimated by multiplying the expected throughput - * by the success ratio (successful / attempted tx frames). Frame size is - * not considered in this calculation; it assumes that frame size will average - * out to be fairly consistent over several samples. The following are - * metric values for expected throughput assuming 100% success ratio. - * Only G band has support for CCK rates: - * - * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 - * - * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 - * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 - * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 - * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 - * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 - * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 - * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 - * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 - * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 - * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 - * - * After the new mode has been tried for a short while (minimum of 6 failed - * frames or 8 successful frames), compare success ratio and actual throughput - * estimate of the new mode with the old. If either is better with the new - * mode, continue to use the new mode. - * - * Continue comparing modes until all 3 possibilities have been tried. - * If moving from legacy to HT, try all 3 possibilities from the new HT - * mode. After trying all 3, a best mode is found. Continue to use this mode - * for the longer "while" described above (e.g. 480 successful frames for - * legacy), and then repeat the search process. - * - */ -struct iwl_link_quality_cmd { - - /* Index of destination/recipient station in uCode's station table */ - u8 sta_id; - u8 reserved1; - __le16 control; /* not used */ - struct iwl_link_qual_general_params general_params; - struct iwl_link_qual_agg_params agg_params; - - /* - * Rate info; when using rate-scaling, Tx command's initial_rate_index - * specifies 1st Tx rate attempted, via index into this table. - * agn devices works its way through table when retrying Tx. - */ - struct { - __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ - } rs_table[LINK_QUAL_MAX_RETRY_NUM]; - __le32 reserved2; -} __packed; - -/* - * BT configuration enable flags: - * bit 0 - 1: BT channel announcement enabled - * 0: disable - * bit 1 - 1: priority of BT device enabled - * 0: disable - * bit 2 - 1: BT 2 wire support enabled - * 0: disable - */ -#define BT_COEX_DISABLE (0x0) -#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) -#define BT_ENABLE_PRIORITY BIT(1) -#define BT_ENABLE_2_WIRE BIT(2) - -#define BT_COEX_DISABLE (0x0) -#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) - -#define BT_LEAD_TIME_MIN (0x0) -#define BT_LEAD_TIME_DEF (0x1E) -#define BT_LEAD_TIME_MAX (0xFF) - -#define BT_MAX_KILL_MIN (0x1) -#define BT_MAX_KILL_DEF (0x5) -#define BT_MAX_KILL_MAX (0xFF) - -#define BT_DURATION_LIMIT_DEF 625 -#define BT_DURATION_LIMIT_MAX 1250 -#define BT_DURATION_LIMIT_MIN 625 - -#define BT_ON_THRESHOLD_DEF 4 -#define BT_ON_THRESHOLD_MAX 1000 -#define BT_ON_THRESHOLD_MIN 1 - -#define BT_FRAG_THRESHOLD_DEF 0 -#define BT_FRAG_THRESHOLD_MAX 0 -#define BT_FRAG_THRESHOLD_MIN 0 - -#define BT_AGG_THRESHOLD_DEF 1200 -#define BT_AGG_THRESHOLD_MAX 8000 -#define BT_AGG_THRESHOLD_MIN 400 - -/* - * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) - * - * agn devices support hardware handshake with Bluetooth device on - * same platform. Bluetooth device alerts wireless device when it will Tx; - * wireless device can delay or kill its own Tx to accommodate. - */ -struct iwl_bt_cmd { - u8 flags; - u8 lead_time; - u8 max_kill; - u8 reserved; - __le32 kill_ack_mask; - __le32 kill_cts_mask; -} __packed; - -#define IWLAGN_BT_FLAG_CHANNEL_INHIBITION BIT(0) - -#define IWLAGN_BT_FLAG_COEX_MODE_MASK (BIT(3)|BIT(4)|BIT(5)) -#define IWLAGN_BT_FLAG_COEX_MODE_SHIFT 3 -#define IWLAGN_BT_FLAG_COEX_MODE_DISABLED 0 -#define IWLAGN_BT_FLAG_COEX_MODE_LEGACY_2W 1 -#define IWLAGN_BT_FLAG_COEX_MODE_3W 2 -#define IWLAGN_BT_FLAG_COEX_MODE_4W 3 - -#define IWLAGN_BT_FLAG_UCODE_DEFAULT BIT(6) -/* Disable Sync PSPoll on SCO/eSCO */ -#define IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE BIT(7) - -#define IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD -75 /* dBm */ -#define IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD -65 /* dBm */ - -#define IWLAGN_BT_PRIO_BOOST_MAX 0xFF -#define IWLAGN_BT_PRIO_BOOST_MIN 0x00 -#define IWLAGN_BT_PRIO_BOOST_DEFAULT 0xF0 - -#define IWLAGN_BT_MAX_KILL_DEFAULT 5 - -#define IWLAGN_BT3_T7_DEFAULT 1 - -enum iwl_bt_kill_idx { - IWL_BT_KILL_DEFAULT = 0, - IWL_BT_KILL_OVERRIDE = 1, - IWL_BT_KILL_REDUCE = 2, -}; - -#define IWLAGN_BT_KILL_ACK_MASK_DEFAULT cpu_to_le32(0xffff0000) -#define IWLAGN_BT_KILL_CTS_MASK_DEFAULT cpu_to_le32(0xffff0000) -#define IWLAGN_BT_KILL_ACK_CTS_MASK_SCO cpu_to_le32(0xffffffff) -#define IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE cpu_to_le32(0) - -#define IWLAGN_BT3_PRIO_SAMPLE_DEFAULT 2 - -#define IWLAGN_BT3_T2_DEFAULT 0xc - -#define IWLAGN_BT_VALID_ENABLE_FLAGS cpu_to_le16(BIT(0)) -#define IWLAGN_BT_VALID_BOOST cpu_to_le16(BIT(1)) -#define IWLAGN_BT_VALID_MAX_KILL cpu_to_le16(BIT(2)) -#define IWLAGN_BT_VALID_3W_TIMERS cpu_to_le16(BIT(3)) -#define IWLAGN_BT_VALID_KILL_ACK_MASK cpu_to_le16(BIT(4)) -#define IWLAGN_BT_VALID_KILL_CTS_MASK cpu_to_le16(BIT(5)) -#define IWLAGN_BT_VALID_REDUCED_TX_PWR cpu_to_le16(BIT(6)) -#define IWLAGN_BT_VALID_3W_LUT cpu_to_le16(BIT(7)) - -#define IWLAGN_BT_ALL_VALID_MSK (IWLAGN_BT_VALID_ENABLE_FLAGS | \ - IWLAGN_BT_VALID_BOOST | \ - IWLAGN_BT_VALID_MAX_KILL | \ - IWLAGN_BT_VALID_3W_TIMERS | \ - IWLAGN_BT_VALID_KILL_ACK_MASK | \ - IWLAGN_BT_VALID_KILL_CTS_MASK | \ - IWLAGN_BT_VALID_REDUCED_TX_PWR | \ - IWLAGN_BT_VALID_3W_LUT) - -#define IWLAGN_BT_REDUCED_TX_PWR BIT(0) - -#define IWLAGN_BT_DECISION_LUT_SIZE 12 - -struct iwl_basic_bt_cmd { - u8 flags; - u8 ledtime; /* unused */ - u8 max_kill; - u8 bt3_timer_t7_value; - __le32 kill_ack_mask; - __le32 kill_cts_mask; - u8 bt3_prio_sample_time; - u8 bt3_timer_t2_value; - __le16 bt4_reaction_time; /* unused */ - __le32 bt3_lookup_table[IWLAGN_BT_DECISION_LUT_SIZE]; - /* - * bit 0: use reduced tx power for control frame - * bit 1 - 7: reserved - */ - u8 reduce_txpower; - u8 reserved; - __le16 valid; -}; - -struct iwl_bt_cmd_v1 { - struct iwl_basic_bt_cmd basic; - u8 prio_boost; - /* - * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask - * if configure the following patterns - */ - u8 tx_prio_boost; /* SW boost of WiFi tx priority */ - __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ -}; - -struct iwl_bt_cmd_v2 { - struct iwl_basic_bt_cmd basic; - __le32 prio_boost; - /* - * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask - * if configure the following patterns - */ - u8 reserved; - u8 tx_prio_boost; /* SW boost of WiFi tx priority */ - __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ -}; - -#define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0)) - -struct iwlagn_bt_sco_cmd { - __le32 flags; -}; - -/****************************************************************************** - * (6) - * Spectrum Management (802.11h) Commands, Responses, Notifications: - * - *****************************************************************************/ - -/* - * Spectrum Management - */ -#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ - RXON_FILTER_CTL2HOST_MSK | \ - RXON_FILTER_ACCEPT_GRP_MSK | \ - RXON_FILTER_DIS_DECRYPT_MSK | \ - RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ - RXON_FILTER_ASSOC_MSK | \ - RXON_FILTER_BCON_AWARE_MSK) - -struct iwl_measure_channel { - __le32 duration; /* measurement duration in extended beacon - * format */ - u8 channel; /* channel to measure */ - u8 type; /* see enum iwl_measure_type */ - __le16 reserved; -} __packed; - -/* - * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) - */ -struct iwl_spectrum_cmd { - __le16 len; /* number of bytes starting from token */ - u8 token; /* token id */ - u8 id; /* measurement id -- 0 or 1 */ - u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ - u8 periodic; /* 1 = periodic */ - __le16 path_loss_timeout; - __le32 start_time; /* start time in extended beacon format */ - __le32 reserved2; - __le32 flags; /* rxon flags */ - __le32 filter_flags; /* rxon filter flags */ - __le16 channel_count; /* minimum 1, maximum 10 */ - __le16 reserved3; - struct iwl_measure_channel channels[10]; -} __packed; - -/* - * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) - */ -struct iwl_spectrum_resp { - u8 token; - u8 id; /* id of the prior command replaced, or 0xff */ - __le16 status; /* 0 - command will be handled - * 1 - cannot handle (conflicts with another - * measurement) */ -} __packed; - -enum iwl_measurement_state { - IWL_MEASUREMENT_START = 0, - IWL_MEASUREMENT_STOP = 1, -}; - -enum iwl_measurement_status { - IWL_MEASUREMENT_OK = 0, - IWL_MEASUREMENT_CONCURRENT = 1, - IWL_MEASUREMENT_CSA_CONFLICT = 2, - IWL_MEASUREMENT_TGH_CONFLICT = 3, - /* 4-5 reserved */ - IWL_MEASUREMENT_STOPPED = 6, - IWL_MEASUREMENT_TIMEOUT = 7, - IWL_MEASUREMENT_PERIODIC_FAILED = 8, -}; - -#define NUM_ELEMENTS_IN_HISTOGRAM 8 - -struct iwl_measurement_histogram { - __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ - __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ -} __packed; - -/* clear channel availability counters */ -struct iwl_measurement_cca_counters { - __le32 ofdm; - __le32 cck; -} __packed; - -enum iwl_measure_type { - IWL_MEASURE_BASIC = (1 << 0), - IWL_MEASURE_CHANNEL_LOAD = (1 << 1), - IWL_MEASURE_HISTOGRAM_RPI = (1 << 2), - IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3), - IWL_MEASURE_FRAME = (1 << 4), - /* bits 5:6 are reserved */ - IWL_MEASURE_IDLE = (1 << 7), -}; - -/* - * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command) - */ -struct iwl_spectrum_notification { - u8 id; /* measurement id -- 0 or 1 */ - u8 token; - u8 channel_index; /* index in measurement channel list */ - u8 state; /* 0 - start, 1 - stop */ - __le32 start_time; /* lower 32-bits of TSF */ - u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ - u8 channel; - u8 type; /* see enum iwl_measurement_type */ - u8 reserved1; - /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only - * valid if applicable for measurement type requested. */ - __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ - __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ - __le32 cca_time; /* channel load time in usecs */ - u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - - * unidentified */ - u8 reserved2[3]; - struct iwl_measurement_histogram histogram; - __le32 stop_time; /* lower 32-bits of TSF */ - __le32 status; /* see iwl_measurement_status */ -} __packed; - -/****************************************************************************** - * (7) - * Power Management Commands, Responses, Notifications: - * - *****************************************************************************/ - -/** - * struct iwl_powertable_cmd - Power Table Command - * @flags: See below: - * - * POWER_TABLE_CMD = 0x77 (command, has simple generic response) - * - * PM allow: - * bit 0 - '0' Driver not allow power management - * '1' Driver allow PM (use rest of parameters) - * - * uCode send sleep notifications: - * bit 1 - '0' Don't send sleep notification - * '1' send sleep notification (SEND_PM_NOTIFICATION) - * - * Sleep over DTIM - * bit 2 - '0' PM have to walk up every DTIM - * '1' PM could sleep over DTIM till listen Interval. - * - * PCI power managed - * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) - * '1' !(PCI_CFG_LINK_CTRL & 0x1) - * - * Fast PD - * bit 4 - '1' Put radio to sleep when receiving frame for others - * - * Force sleep Modes - * bit 31/30- '00' use both mac/xtal sleeps - * '01' force Mac sleep - * '10' force xtal sleep - * '11' Illegal set - * - * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then - * ucode assume sleep over DTIM is allowed and we don't need to wake up - * for every DTIM. - */ -#define IWL_POWER_VEC_SIZE 5 - -#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) -#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0)) -#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1)) -#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) -#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) -#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) -#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5)) -#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6)) -#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7)) -#define IWL_POWER_BT_SCO_ENA cpu_to_le16(BIT(8)) -#define IWL_POWER_ADVANCE_PM_ENA_MSK cpu_to_le16(BIT(9)) - -struct iwl_powertable_cmd { - __le16 flags; - u8 keep_alive_seconds; - u8 debug_flags; - __le32 rx_data_timeout; - __le32 tx_data_timeout; - __le32 sleep_interval[IWL_POWER_VEC_SIZE]; - __le32 keep_alive_beacons; -} __packed; - -/* - * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) - * all devices identical. - */ -struct iwl_sleep_notification { - u8 pm_sleep_mode; - u8 pm_wakeup_src; - __le16 reserved; - __le32 sleep_time; - __le32 tsf_low; - __le32 bcon_timer; -} __packed; - -/* Sleep states. all devices identical. */ -enum { - IWL_PM_NO_SLEEP = 0, - IWL_PM_SLP_MAC = 1, - IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, - IWL_PM_SLP_FULL_MAC_CARD_STATE = 3, - IWL_PM_SLP_PHY = 4, - IWL_PM_SLP_REPENT = 5, - IWL_PM_WAKEUP_BY_TIMER = 6, - IWL_PM_WAKEUP_BY_DRIVER = 7, - IWL_PM_WAKEUP_BY_RFKILL = 8, - /* 3 reserved */ - IWL_PM_NUM_OF_MODES = 12, -}; - -/* - * REPLY_CARD_STATE_CMD = 0xa0 (command, has simple generic response) - */ -#define CARD_STATE_CMD_DISABLE 0x00 /* Put card to sleep */ -#define CARD_STATE_CMD_ENABLE 0x01 /* Wake up card */ -#define CARD_STATE_CMD_HALT 0x02 /* Power down permanently */ -struct iwl_card_state_cmd { - __le32 status; /* CARD_STATE_CMD_* request new power state */ -} __packed; - -/* - * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) - */ -struct iwl_card_state_notif { - __le32 flags; -} __packed; - -#define HW_CARD_DISABLED 0x01 -#define SW_CARD_DISABLED 0x02 -#define CT_CARD_DISABLED 0x04 -#define RXON_CARD_DISABLED 0x10 - -struct iwl_ct_kill_config { - __le32 reserved; - __le32 critical_temperature_M; - __le32 critical_temperature_R; -} __packed; - -/* 1000, and 6x00 */ -struct iwl_ct_kill_throttling_config { - __le32 critical_temperature_exit; - __le32 reserved; - __le32 critical_temperature_enter; -} __packed; - -/****************************************************************************** - * (8) - * Scan Commands, Responses, Notifications: - * - *****************************************************************************/ - -#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) -#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) - -/** - * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table - * - * One for each channel in the scan list. - * Each channel can independently select: - * 1) SSID for directed active scans - * 2) Txpower setting (for rate specified within Tx command) - * 3) How long to stay on-channel (behavior may be modified by quiet_time, - * quiet_plcp_th, good_CRC_th) - * - * To avoid uCode errors, make sure the following are true (see comments - * under struct iwl_scan_cmd about max_out_time and quiet_time): - * 1) If using passive_dwell (i.e. passive_dwell != 0): - * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) - * 2) quiet_time <= active_dwell - * 3) If restricting off-channel time (i.e. max_out_time !=0): - * passive_dwell < max_out_time - * active_dwell < max_out_time - */ - -struct iwl_scan_channel { - /* - * type is defined as: - * 0:0 1 = active, 0 = passive - * 1:20 SSID direct bit map; if a bit is set, then corresponding - * SSID IE is transmitted in probe request. - * 21:31 reserved - */ - __le32 type; - __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */ - u8 tx_gain; /* gain for analog radio */ - u8 dsp_atten; /* gain for DSP */ - __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ - __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ -} __packed; - -/* set number of direct probes __le32 type */ -#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) - -/** - * struct iwl_ssid_ie - directed scan network information element - * - * Up to 20 of these may appear in REPLY_SCAN_CMD, - * selected by "type" bit field in struct iwl_scan_channel; - * each channel may select different ssids from among the 20 entries. - * SSID IEs get transmitted in reverse order of entry. - */ -struct iwl_ssid_ie { - u8 id; - u8 len; - u8 ssid[32]; -} __packed; - -#define PROBE_OPTION_MAX 20 -#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) -#define IWL_GOOD_CRC_TH_DISABLED 0 -#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) -#define IWL_MAX_CMD_SIZE 4096 - -/* - * REPLY_SCAN_CMD = 0x80 (command) - * - * The hardware scan command is very powerful; the driver can set it up to - * maintain (relatively) normal network traffic while doing a scan in the - * background. The max_out_time and suspend_time control the ratio of how - * long the device stays on an associated network channel ("service channel") - * vs. how long it's away from the service channel, i.e. tuned to other channels - * for scanning. - * - * max_out_time is the max time off-channel (in usec), and suspend_time - * is how long (in "extended beacon" format) that the scan is "suspended" - * after returning to the service channel. That is, suspend_time is the - * time that we stay on the service channel, doing normal work, between - * scan segments. The driver may set these parameters differently to support - * scanning when associated vs. not associated, and light vs. heavy traffic - * loads when associated. - * - * After receiving this command, the device's scan engine does the following; - * - * 1) Sends SCAN_START notification to driver - * 2) Checks to see if it has time to do scan for one channel - * 3) Sends NULL packet, with power-save (PS) bit set to 1, - * to tell AP that we're going off-channel - * 4) Tunes to first channel in scan list, does active or passive scan - * 5) Sends SCAN_RESULT notification to driver - * 6) Checks to see if it has time to do scan on *next* channel in list - * 7) Repeats 4-6 until it no longer has time to scan the next channel - * before max_out_time expires - * 8) Returns to service channel - * 9) Sends NULL packet with PS=0 to tell AP that we're back - * 10) Stays on service channel until suspend_time expires - * 11) Repeats entire process 2-10 until list is complete - * 12) Sends SCAN_COMPLETE notification - * - * For fast, efficient scans, the scan command also has support for staying on - * a channel for just a short time, if doing active scanning and getting no - * responses to the transmitted probe request. This time is controlled by - * quiet_time, and the number of received packets below which a channel is - * considered "quiet" is controlled by quiet_plcp_threshold. - * - * For active scanning on channels that have regulatory restrictions against - * blindly transmitting, the scan can listen before transmitting, to make sure - * that there is already legitimate activity on the channel. If enough - * packets are cleanly received on the channel (controlled by good_CRC_th, - * typical value 1), the scan engine starts transmitting probe requests. - * - * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. - * - * To avoid uCode errors, see timing restrictions described under - * struct iwl_scan_channel. - */ - -enum iwl_scan_flags { - /* BIT(0) currently unused */ - IWL_SCAN_FLAGS_ACTION_FRAME_TX = BIT(1), - /* bits 2-7 reserved */ -}; - -struct iwl_scan_cmd { - __le16 len; - u8 scan_flags; /* scan flags: see enum iwl_scan_flags */ - u8 channel_count; /* # channels in channel list */ - __le16 quiet_time; /* dwell only this # millisecs on quiet channel - * (only for active scan) */ - __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ - __le16 good_CRC_th; /* passive -> active promotion threshold */ - __le16 rx_chain; /* RXON_RX_CHAIN_* */ - __le32 max_out_time; /* max usec to be away from associated (service) - * channel */ - __le32 suspend_time; /* pause scan this long (in "extended beacon - * format") when returning to service chnl: - */ - __le32 flags; /* RXON_FLG_* */ - __le32 filter_flags; /* RXON_FILTER_* */ - - /* For active scans (set to all-0s for passive scans). - * Does not include payload. Must specify Tx rate; no rate scaling. */ - struct iwl_tx_cmd tx_cmd; - - /* For directed active scans (set to all-0s otherwise) */ - struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; - - /* - * Probe request frame, followed by channel list. - * - * Size of probe request frame is specified by byte count in tx_cmd. - * Channel list follows immediately after probe request frame. - * Number of channels in list is specified by channel_count. - * Each channel in list is of type: - * - * struct iwl_scan_channel channels[0]; - * - * NOTE: Only one band of channels can be scanned per pass. You - * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait - * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) - * before requesting another scan. - */ - u8 data[0]; -} __packed; - -/* Can abort will notify by complete notification with abort status. */ -#define CAN_ABORT_STATUS cpu_to_le32(0x1) -/* complete notification statuses */ -#define ABORT_STATUS 0x2 - -/* - * REPLY_SCAN_CMD = 0x80 (response) - */ -struct iwl_scanreq_notification { - __le32 status; /* 1: okay, 2: cannot fulfill request */ -} __packed; - -/* - * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) - */ -struct iwl_scanstart_notification { - __le32 tsf_low; - __le32 tsf_high; - __le32 beacon_timer; - u8 channel; - u8 band; - u8 reserved[2]; - __le32 status; -} __packed; - -#define SCAN_OWNER_STATUS 0x1 -#define MEASURE_OWNER_STATUS 0x2 - -#define IWL_PROBE_STATUS_OK 0 -#define IWL_PROBE_STATUS_TX_FAILED BIT(0) -/* error statuses combined with TX_FAILED */ -#define IWL_PROBE_STATUS_FAIL_TTL BIT(1) -#define IWL_PROBE_STATUS_FAIL_BT BIT(2) - -#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */ -/* - * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command) - */ -struct iwl_scanresults_notification { - u8 channel; - u8 band; - u8 probe_status; - u8 num_probe_not_sent; /* not enough time to send */ - __le32 tsf_low; - __le32 tsf_high; - __le32 statistics[NUMBER_OF_STATISTICS]; -} __packed; - -/* - * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command) - */ -struct iwl_scancomplete_notification { - u8 scanned_channels; - u8 status; - u8 bt_status; /* BT On/Off status */ - u8 last_channel; - __le32 tsf_low; - __le32 tsf_high; -} __packed; - - -/****************************************************************************** - * (9) - * IBSS/AP Commands and Notifications: - * - *****************************************************************************/ - -enum iwl_ibss_manager { - IWL_NOT_IBSS_MANAGER = 0, - IWL_IBSS_MANAGER = 1, -}; - -/* - * BEACON_NOTIFICATION = 0x90 (notification only, not a command) - */ - -struct iwlagn_beacon_notif { - struct iwlagn_tx_resp beacon_notify_hdr; - __le32 low_tsf; - __le32 high_tsf; - __le32 ibss_mgr_status; -} __packed; - -/* - * REPLY_TX_BEACON = 0x91 (command, has simple generic response) - */ - -struct iwl_tx_beacon_cmd { - struct iwl_tx_cmd tx; - __le16 tim_idx; - u8 tim_size; - u8 reserved1; - struct ieee80211_hdr frame[0]; /* beacon frame */ -} __packed; - -/****************************************************************************** - * (10) - * Statistics Commands and Notifications: - * - *****************************************************************************/ - -#define IWL_TEMP_CONVERT 260 - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -/* Used for passing to driver number of successes and failures per rate */ -struct rate_histogram { - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } success; - union { - __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; - __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; - __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; - } failed; -} __packed; - -/* statistics command response */ - -struct statistics_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - __le32 reserved[3]; -} __packed; - -struct statistics_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_limit_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved3; -} __packed; - -struct statistics_rx_ht_phy { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; - -#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) - -struct statistics_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time;/* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; -} __packed; - -struct statistics_rx_non_phy_bt { - struct statistics_rx_non_phy common; - /* additional stats for bt */ - __le32 num_bt_kills; - __le32 reserved[2]; -} __packed; - -struct statistics_rx { - struct statistics_rx_phy ofdm; - struct statistics_rx_phy cck; - struct statistics_rx_non_phy general; - struct statistics_rx_ht_phy ofdm_ht; -} __packed; - -struct statistics_rx_bt { - struct statistics_rx_phy ofdm; - struct statistics_rx_phy cck; - struct statistics_rx_non_phy_bt general; - struct statistics_rx_ht_phy ofdm_ht; -} __packed; - -/** - * struct statistics_tx_power - current tx power - * - * @ant_a: current tx power on chain a in 1/2 dB step - * @ant_b: current tx power on chain b in 1/2 dB step - * @ant_c: current tx power on chain c in 1/2 dB step - */ -struct statistics_tx_power { - u8 ant_a; - u8 ant_b; - u8 ant_c; - u8 reserved; -} __packed; - -struct statistics_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; -} __packed; - -struct statistics_tx { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; - struct statistics_tx_non_phy_agg agg; - /* - * "tx_power" are optional parameters provided by uCode, - * 6000 series is the only device provide the information, - * Those are reserved fields for all the other devices - */ - struct statistics_tx_power tx_power; - __le32 reserved1; -} __packed; - - -struct statistics_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 reserved1; - __le32 reserved2; -} __packed; - -struct statistics_general_common { - __le32 temperature; /* radio temperature */ - __le32 temperature_m; /* radio voltage */ - struct statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct statistics_div div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; -} __packed; - -struct statistics_bt_activity { - /* Tx statistics */ - __le32 hi_priority_tx_req_cnt; - __le32 hi_priority_tx_denied_cnt; - __le32 lo_priority_tx_req_cnt; - __le32 lo_priority_tx_denied_cnt; - /* Rx statistics */ - __le32 hi_priority_rx_req_cnt; - __le32 hi_priority_rx_denied_cnt; - __le32 lo_priority_rx_req_cnt; - __le32 lo_priority_rx_denied_cnt; -} __packed; - -struct statistics_general { - struct statistics_general_common common; - __le32 reserved2; - __le32 reserved3; -} __packed; - -struct statistics_general_bt { - struct statistics_general_common common; - struct statistics_bt_activity activity; - __le32 reserved2; - __le32 reserved3; -} __packed; - -#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) -#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) -#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) - -/* - * REPLY_STATISTICS_CMD = 0x9c, - * all devices identical. - * - * This command triggers an immediate response containing uCode statistics. - * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below. - * - * If the CLEAR_STATS configuration flag is set, uCode will clear its - * internal copy of the statistics (counters) after issuing the response. - * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below). - * - * If the DISABLE_NOTIF configuration flag is set, uCode will not issue - * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag - * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself. - */ -#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ -#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */ -struct iwl_statistics_cmd { - __le32 configuration_flags; /* IWL_STATS_CONF_* */ -} __packed; - -/* - * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * REPLY_STATISTICS_CMD 0x9c, above. - * - * Statistics counters continue to increment beacon after beacon, but are - * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD - * 0x9c with CLEAR_STATS bit set (see above). - * - * uCode also issues this notification during scans. uCode clears statistics - * appropriately so that each notification contains statistics for only the - * one channel that has just been scanned. - */ -#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) -#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) - -struct iwl_notif_statistics { - __le32 flag; - struct statistics_rx rx; - struct statistics_tx tx; - struct statistics_general general; -} __packed; - -struct iwl_bt_notif_statistics { - __le32 flag; - struct statistics_rx_bt rx; - struct statistics_tx tx; - struct statistics_general_bt general; -} __packed; - -/* - * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) - * - * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed - * in regardless of how many missed beacons, which mean when driver receive the - * notification, inside the command, it can find all the beacons information - * which include number of total missed beacons, number of consecutive missed - * beacons, number of beacons received and number of beacons expected to - * receive. - * - * If uCode detected consecutive_missed_beacons > 5, it will reset the radio - * in order to bring the radio/PHY back to working state; which has no relation - * to when driver will perform sensitivity calibration. - * - * Driver should set it own missed_beacon_threshold to decide when to perform - * sensitivity calibration based on number of consecutive missed beacons in - * order to improve overall performance, especially in noisy environment. - * - */ - -#define IWL_MISSED_BEACON_THRESHOLD_MIN (1) -#define IWL_MISSED_BEACON_THRESHOLD_DEF (5) -#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF - -struct iwl_missed_beacon_notif { - __le32 consecutive_missed_beacons; - __le32 total_missed_becons; - __le32 num_expected_beacons; - __le32 num_recvd_beacons; -} __packed; - - -/****************************************************************************** - * (11) - * Rx Calibration Commands: - * - * With the uCode used for open source drivers, most Tx calibration (except - * for Tx Power) and most Rx calibration is done by uCode during the - * "initialize" phase of uCode boot. Driver must calibrate only: - * - * 1) Tx power (depends on temperature), described elsewhere - * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) - * 3) Receiver sensitivity (to optimize signal detection) - * - *****************************************************************************/ - -/** - * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) - * - * This command sets up the Rx signal detector for a sensitivity level that - * is high enough to lock onto all signals within the associated network, - * but low enough to ignore signals that are below a certain threshold, so as - * not to have too many "false alarms". False alarms are signals that the - * Rx DSP tries to lock onto, but then discards after determining that they - * are noise. - * - * The optimum number of false alarms is between 5 and 50 per 200 TUs - * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. - * time listening, not transmitting). Driver must adjust sensitivity so that - * the ratio of actual false alarms to actual Rx time falls within this range. - * - * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each - * received beacon. These provide information to the driver to analyze the - * sensitivity. Don't analyze statistics that come in from scanning, or any - * other non-associated-network source. Pertinent statistics include: - * - * From "general" statistics (struct statistics_rx_non_phy): - * - * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) - * Measure of energy of desired signal. Used for establishing a level - * below which the device does not detect signals. - * - * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) - * Measure of background noise in silent period after beacon. - * - * channel_load - * uSecs of actual Rx time during beacon period (varies according to - * how much time was spent transmitting). - * - * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: - * - * false_alarm_cnt - * Signal locks abandoned early (before phy-level header). - * - * plcp_err - * Signal locks abandoned late (during phy-level header). - * - * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from - * beacon to beacon, i.e. each value is an accumulation of all errors - * before and including the latest beacon. Values will wrap around to 0 - * after counting up to 2^32 - 1. Driver must differentiate vs. - * previous beacon's values to determine # false alarms in the current - * beacon period. - * - * Total number of false alarms = false_alarms + plcp_errs - * - * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd - * (notice that the start points for OFDM are at or close to settings for - * maximum sensitivity): - * - * START / MIN / MAX - * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 - * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 - * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 - * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 - * - * If actual rate of OFDM false alarms (+ plcp_errors) is too high - * (greater than 50 for each 204.8 msecs listening), reduce sensitivity - * by *adding* 1 to all 4 of the table entries above, up to the max for - * each entry. Conversely, if false alarm rate is too low (less than 5 - * for each 204.8 msecs listening), *subtract* 1 from each entry to - * increase sensitivity. - * - * For CCK sensitivity, keep track of the following: - * - * 1). 20-beacon history of maximum background noise, indicated by - * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the - * 3 receivers. For any given beacon, the "silence reference" is - * the maximum of last 60 samples (20 beacons * 3 receivers). - * - * 2). 10-beacon history of strongest signal level, as indicated - * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, - * i.e. the strength of the signal through the best receiver at the - * moment. These measurements are "upside down", with lower values - * for stronger signals, so max energy will be *minimum* value. - * - * Then for any given beacon, the driver must determine the *weakest* - * of the strongest signals; this is the minimum level that needs to be - * successfully detected, when using the best receiver at the moment. - * "Max cck energy" is the maximum (higher value means lower energy!) - * of the last 10 minima. Once this is determined, driver must add - * a little margin by adding "6" to it. - * - * 3). Number of consecutive beacon periods with too few false alarms. - * Reset this to 0 at the first beacon period that falls within the - * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). - * - * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd - * (notice that the start points for CCK are at maximum sensitivity): - * - * START / MIN / MAX - * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 - * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 - * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 - * - * If actual rate of CCK false alarms (+ plcp_errors) is too high - * (greater than 50 for each 204.8 msecs listening), method for reducing - * sensitivity is: - * - * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, - * up to max 400. - * - * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, - * sensitivity has been reduced a significant amount; bring it up to - * a moderate 161. Otherwise, *add* 3, up to max 200. - * - * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, - * sensitivity has been reduced only a moderate or small amount; - * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, - * down to min 0. Otherwise (if gain has been significantly reduced), - * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. - * - * b) Save a snapshot of the "silence reference". - * - * If actual rate of CCK false alarms (+ plcp_errors) is too low - * (less than 5 for each 204.8 msecs listening), method for increasing - * sensitivity is used only if: - * - * 1a) Previous beacon did not have too many false alarms - * 1b) AND difference between previous "silence reference" and current - * "silence reference" (prev - current) is 2 or more, - * OR 2) 100 or more consecutive beacon periods have had rate of - * less than 5 false alarms per 204.8 milliseconds rx time. - * - * Method for increasing sensitivity: - * - * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, - * down to min 125. - * - * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, - * down to min 200. - * - * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. - * - * If actual rate of CCK false alarms (+ plcp_errors) is within good range - * (between 5 and 50 for each 204.8 msecs listening): - * - * 1) Save a snapshot of the silence reference. - * - * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), - * give some extra margin to energy threshold by *subtracting* 8 - * from value in HD_MIN_ENERGY_CCK_DET_INDEX. - * - * For all cases (too few, too many, good range), make sure that the CCK - * detection threshold (energy) is below the energy level for robust - * detection over the past 10 beacon periods, the "Max cck energy". - * Lower values mean higher energy; this means making sure that the value - * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". - * - */ - -/* - * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd) - */ -#define HD_TABLE_SIZE (11) /* number of entries */ -#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ -#define HD_MIN_ENERGY_OFDM_DET_INDEX (1) -#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2) -#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3) -#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4) -#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5) -#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6) -#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7) -#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8) -#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) -#define HD_OFDM_ENERGY_TH_IN_INDEX (10) - -/* - * Additional table entries in enhance SENSITIVITY_CMD - */ -#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11) -#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12) -#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17) -#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19) -#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21) -#define HD_RESERVED (22) - -/* number of entries for enhanced tbl */ -#define ENHANCE_HD_TABLE_SIZE (23) - -/* number of additional entries for enhanced tbl */ -#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE) - -#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V1 cpu_to_le16(0) -#define HD_INA_NON_SQUARE_DET_CCK_DATA_V1 cpu_to_le16(0) -#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V1 cpu_to_le16(0) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(668) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(486) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(37) -#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V1 cpu_to_le16(853) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V1 cpu_to_le16(4) -#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V1 cpu_to_le16(476) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V1 cpu_to_le16(99) - -#define HD_INA_NON_SQUARE_DET_OFDM_DATA_V2 cpu_to_le16(1) -#define HD_INA_NON_SQUARE_DET_CCK_DATA_V2 cpu_to_le16(1) -#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA_V2 cpu_to_le16(1) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(600) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(40) -#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(486) -#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(45) -#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA_V2 cpu_to_le16(853) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA_V2 cpu_to_le16(60) -#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA_V2 cpu_to_le16(476) -#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA_V2 cpu_to_le16(99) - - -/* Control field in struct iwl_sensitivity_cmd */ -#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) -#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) - -/** - * struct iwl_sensitivity_cmd - * @control: (1) updates working table, (0) updates default table - * @table: energy threshold values, use HD_* as index into table - * - * Always use "1" in "control" to update uCode's working table and DSP. - */ -struct iwl_sensitivity_cmd { - __le16 control; /* always use "1" */ - __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ -} __packed; - -/* - * - */ -struct iwl_enhance_sensitivity_cmd { - __le16 control; /* always use "1" */ - __le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */ -} __packed; - - -/** - * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) - * - * This command sets the relative gains of agn device's 3 radio receiver chains. - * - * After the first association, driver should accumulate signal and noise - * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 - * beacons from the associated network (don't collect statistics that come - * in from scanning, or any other non-network source). - * - * DISCONNECTED ANTENNA: - * - * Driver should determine which antennas are actually connected, by comparing - * average beacon signal levels for the 3 Rx chains. Accumulate (add) the - * following values over 20 beacons, one accumulator for each of the chains - * a/b/c, from struct statistics_rx_non_phy: - * - * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) - * - * Find the strongest signal from among a/b/c. Compare the other two to the - * strongest. If any signal is more than 15 dB (times 20, unless you - * divide the accumulated values by 20) below the strongest, the driver - * considers that antenna to be disconnected, and should not try to use that - * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, - * driver should declare the stronger one as connected, and attempt to use it - * (A and B are the only 2 Tx chains!). - * - * - * RX BALANCE: - * - * Driver should balance the 3 receivers (but just the ones that are connected - * to antennas, see above) for gain, by comparing the average signal levels - * detected during the silence after each beacon (background noise). - * Accumulate (add) the following values over 20 beacons, one accumulator for - * each of the chains a/b/c, from struct statistics_rx_non_phy: - * - * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) - * - * Find the weakest background noise level from among a/b/c. This Rx chain - * will be the reference, with 0 gain adjustment. Attenuate other channels by - * finding noise difference: - * - * (accum_noise[i] - accum_noise[reference]) / 30 - * - * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. - * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the - * driver should limit the difference results to a range of 0-3 (0-4.5 dB), - * and set bit 2 to indicate "reduce gain". The value for the reference - * (weakest) chain should be "0". - * - * diff_gain_[abc] bit fields: - * 2: (1) reduce gain, (0) increase gain - * 1-0: amount of gain, units of 1.5 dB - */ - -/* Phy calibration command for series */ -enum { - IWL_PHY_CALIBRATE_DC_CMD = 8, - IWL_PHY_CALIBRATE_LO_CMD = 9, - IWL_PHY_CALIBRATE_TX_IQ_CMD = 11, - IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, - IWL_PHY_CALIBRATE_BASE_BAND_CMD = 16, - IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD = 17, - IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD = 18, -}; - -/* This enum defines the bitmap of various calibrations to enable in both - * init ucode and runtime ucode through CALIBRATION_CFG_CMD. - */ -enum iwl_ucode_calib_cfg { - IWL_CALIB_CFG_RX_BB_IDX = BIT(0), - IWL_CALIB_CFG_DC_IDX = BIT(1), - IWL_CALIB_CFG_LO_IDX = BIT(2), - IWL_CALIB_CFG_TX_IQ_IDX = BIT(3), - IWL_CALIB_CFG_RX_IQ_IDX = BIT(4), - IWL_CALIB_CFG_NOISE_IDX = BIT(5), - IWL_CALIB_CFG_CRYSTAL_IDX = BIT(6), - IWL_CALIB_CFG_TEMPERATURE_IDX = BIT(7), - IWL_CALIB_CFG_PAPD_IDX = BIT(8), - IWL_CALIB_CFG_SENSITIVITY_IDX = BIT(9), - IWL_CALIB_CFG_TX_PWR_IDX = BIT(10), -}; - -#define IWL_CALIB_INIT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ - IWL_CALIB_CFG_DC_IDX | \ - IWL_CALIB_CFG_LO_IDX | \ - IWL_CALIB_CFG_TX_IQ_IDX | \ - IWL_CALIB_CFG_RX_IQ_IDX | \ - IWL_CALIB_CFG_CRYSTAL_IDX) - -#define IWL_CALIB_RT_CFG_ALL cpu_to_le32(IWL_CALIB_CFG_RX_BB_IDX | \ - IWL_CALIB_CFG_DC_IDX | \ - IWL_CALIB_CFG_LO_IDX | \ - IWL_CALIB_CFG_TX_IQ_IDX | \ - IWL_CALIB_CFG_RX_IQ_IDX | \ - IWL_CALIB_CFG_TEMPERATURE_IDX | \ - IWL_CALIB_CFG_PAPD_IDX | \ - IWL_CALIB_CFG_TX_PWR_IDX | \ - IWL_CALIB_CFG_CRYSTAL_IDX) - -#define IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK cpu_to_le32(BIT(0)) - -struct iwl_calib_cfg_elmnt_s { - __le32 is_enable; - __le32 start; - __le32 send_res; - __le32 apply_res; - __le32 reserved; -} __packed; - -struct iwl_calib_cfg_status_s { - struct iwl_calib_cfg_elmnt_s once; - struct iwl_calib_cfg_elmnt_s perd; - __le32 flags; -} __packed; - -struct iwl_calib_cfg_cmd { - struct iwl_calib_cfg_status_s ucd_calib_cfg; - struct iwl_calib_cfg_status_s drv_calib_cfg; - __le32 reserved1; -} __packed; - -struct iwl_calib_hdr { - u8 op_code; - u8 first_group; - u8 groups_num; - u8 data_valid; -} __packed; - -struct iwl_calib_cmd { - struct iwl_calib_hdr hdr; - u8 data[0]; -} __packed; - -struct iwl_calib_xtal_freq_cmd { - struct iwl_calib_hdr hdr; - u8 cap_pin1; - u8 cap_pin2; - u8 pad[2]; -} __packed; - -#define DEFAULT_RADIO_SENSOR_OFFSET cpu_to_le16(2700) -struct iwl_calib_temperature_offset_cmd { - struct iwl_calib_hdr hdr; - __le16 radio_sensor_offset; - __le16 reserved; -} __packed; - -struct iwl_calib_temperature_offset_v2_cmd { - struct iwl_calib_hdr hdr; - __le16 radio_sensor_offset_high; - __le16 radio_sensor_offset_low; - __le16 burntVoltageRef; - __le16 reserved; -} __packed; - -/* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ -struct iwl_calib_chain_noise_reset_cmd { - struct iwl_calib_hdr hdr; - u8 data[0]; -}; - -/* IWL_PHY_CALIBRATE_CHAIN_NOISE_GAIN_CMD */ -struct iwl_calib_chain_noise_gain_cmd { - struct iwl_calib_hdr hdr; - u8 delta_gain_1; - u8 delta_gain_2; - u8 pad[2]; -} __packed; - -/****************************************************************************** - * (12) - * Miscellaneous Commands: - * - *****************************************************************************/ - -/* - * LEDs Command & Response - * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) - * - * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), - * this command turns it on or off, or sets up a periodic blinking cycle. - */ -struct iwl_led_cmd { - __le32 interval; /* "interval" in uSec */ - u8 id; /* 1: Activity, 2: Link, 3: Tech */ - u8 off; /* # intervals off while blinking; - * "0", with >0 "on" value, turns LED on */ - u8 on; /* # intervals on while blinking; - * "0", regardless of "off", turns LED off */ - u8 reserved; -} __packed; - -/* - * station priority table entries - * also used as potential "events" value for both - * COEX_MEDIUM_NOTIFICATION and COEX_EVENT_CMD - */ - -/* - * COEX events entry flag masks - * RP - Requested Priority - * WP - Win Medium Priority: priority assigned when the contention has been won - */ -#define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG (0x1) -#define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG (0x2) -#define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG (0x4) - -#define COEX_CU_UNASSOC_IDLE_RP 4 -#define COEX_CU_UNASSOC_MANUAL_SCAN_RP 4 -#define COEX_CU_UNASSOC_AUTO_SCAN_RP 4 -#define COEX_CU_CALIBRATION_RP 4 -#define COEX_CU_PERIODIC_CALIBRATION_RP 4 -#define COEX_CU_CONNECTION_ESTAB_RP 4 -#define COEX_CU_ASSOCIATED_IDLE_RP 4 -#define COEX_CU_ASSOC_MANUAL_SCAN_RP 4 -#define COEX_CU_ASSOC_AUTO_SCAN_RP 4 -#define COEX_CU_ASSOC_ACTIVE_LEVEL_RP 4 -#define COEX_CU_RF_ON_RP 6 -#define COEX_CU_RF_OFF_RP 4 -#define COEX_CU_STAND_ALONE_DEBUG_RP 6 -#define COEX_CU_IPAN_ASSOC_LEVEL_RP 4 -#define COEX_CU_RSRVD1_RP 4 -#define COEX_CU_RSRVD2_RP 4 - -#define COEX_CU_UNASSOC_IDLE_WP 3 -#define COEX_CU_UNASSOC_MANUAL_SCAN_WP 3 -#define COEX_CU_UNASSOC_AUTO_SCAN_WP 3 -#define COEX_CU_CALIBRATION_WP 3 -#define COEX_CU_PERIODIC_CALIBRATION_WP 3 -#define COEX_CU_CONNECTION_ESTAB_WP 3 -#define COEX_CU_ASSOCIATED_IDLE_WP 3 -#define COEX_CU_ASSOC_MANUAL_SCAN_WP 3 -#define COEX_CU_ASSOC_AUTO_SCAN_WP 3 -#define COEX_CU_ASSOC_ACTIVE_LEVEL_WP 3 -#define COEX_CU_RF_ON_WP 3 -#define COEX_CU_RF_OFF_WP 3 -#define COEX_CU_STAND_ALONE_DEBUG_WP 6 -#define COEX_CU_IPAN_ASSOC_LEVEL_WP 3 -#define COEX_CU_RSRVD1_WP 3 -#define COEX_CU_RSRVD2_WP 3 - -#define COEX_UNASSOC_IDLE_FLAGS 0 -#define COEX_UNASSOC_MANUAL_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_UNASSOC_AUTO_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_CALIBRATION_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_PERIODIC_CALIBRATION_FLAGS 0 -/* - * COEX_CONNECTION_ESTAB: - * we need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. - */ -#define COEX_CONNECTION_ESTAB_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ - COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) -#define COEX_ASSOCIATED_IDLE_FLAGS 0 -#define COEX_ASSOC_MANUAL_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_ASSOC_AUTO_SCAN_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 -#define COEX_RF_ON_FLAGS 0 -#define COEX_RF_OFF_FLAGS 0 -#define COEX_STAND_ALONE_DEBUG_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG) -#define COEX_IPAN_ASSOC_LEVEL_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ - COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) -#define COEX_RSRVD1_FLAGS 0 -#define COEX_RSRVD2_FLAGS 0 -/* - * COEX_CU_RF_ON is the event wrapping all radio ownership. - * We need DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. - */ -#define COEX_CU_RF_ON_FLAGS \ - (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_FLG | \ - COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_FLG | \ - COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_FLG) - - -enum { - /* un-association part */ - COEX_UNASSOC_IDLE = 0, - COEX_UNASSOC_MANUAL_SCAN = 1, - COEX_UNASSOC_AUTO_SCAN = 2, - /* calibration */ - COEX_CALIBRATION = 3, - COEX_PERIODIC_CALIBRATION = 4, - /* connection */ - COEX_CONNECTION_ESTAB = 5, - /* association part */ - COEX_ASSOCIATED_IDLE = 6, - COEX_ASSOC_MANUAL_SCAN = 7, - COEX_ASSOC_AUTO_SCAN = 8, - COEX_ASSOC_ACTIVE_LEVEL = 9, - /* RF ON/OFF */ - COEX_RF_ON = 10, - COEX_RF_OFF = 11, - COEX_STAND_ALONE_DEBUG = 12, - /* IPAN */ - COEX_IPAN_ASSOC_LEVEL = 13, - /* reserved */ - COEX_RSRVD1 = 14, - COEX_RSRVD2 = 15, - COEX_NUM_OF_EVENTS = 16 -}; - -/* - * Coexistence WIFI/WIMAX Command - * COEX_PRIORITY_TABLE_CMD = 0x5a - * - */ -struct iwl_wimax_coex_event_entry { - u8 request_prio; - u8 win_medium_prio; - u8 reserved; - u8 flags; -} __packed; - -/* COEX flag masks */ - -/* Station table is valid */ -#define COEX_FLAGS_STA_TABLE_VALID_MSK (0x1) -/* UnMask wake up src at unassociated sleep */ -#define COEX_FLAGS_UNASSOC_WA_UNMASK_MSK (0x4) -/* UnMask wake up src at associated sleep */ -#define COEX_FLAGS_ASSOC_WA_UNMASK_MSK (0x8) -/* Enable CoEx feature. */ -#define COEX_FLAGS_COEX_ENABLE_MSK (0x80) - -struct iwl_wimax_coex_cmd { - u8 flags; - u8 reserved[3]; - struct iwl_wimax_coex_event_entry sta_prio[COEX_NUM_OF_EVENTS]; -} __packed; - -/* - * Coexistence MEDIUM NOTIFICATION - * COEX_MEDIUM_NOTIFICATION = 0x5b - * - * notification from uCode to host to indicate medium changes - * - */ -/* - * status field - * bit 0 - 2: medium status - * bit 3: medium change indication - * bit 4 - 31: reserved - */ -/* status option values, (0 - 2 bits) */ -#define COEX_MEDIUM_BUSY (0x0) /* radio belongs to WiMAX */ -#define COEX_MEDIUM_ACTIVE (0x1) /* radio belongs to WiFi */ -#define COEX_MEDIUM_PRE_RELEASE (0x2) /* received radio release */ -#define COEX_MEDIUM_MSK (0x7) - -/* send notification status (1 bit) */ -#define COEX_MEDIUM_CHANGED (0x8) -#define COEX_MEDIUM_CHANGED_MSK (0x8) -#define COEX_MEDIUM_SHIFT (3) - -struct iwl_coex_medium_notification { - __le32 status; - __le32 events; -} __packed; - -/* - * Coexistence EVENT Command - * COEX_EVENT_CMD = 0x5c - * - * send from host to uCode for coex event request. - */ -/* flags options */ -#define COEX_EVENT_REQUEST_MSK (0x1) - -struct iwl_coex_event_cmd { - u8 flags; - u8 event; - __le16 reserved; -} __packed; - -struct iwl_coex_event_resp { - __le32 status; -} __packed; - - -/****************************************************************************** - * Bluetooth Coexistence commands - * - *****************************************************************************/ - -/* - * BT Status notification - * REPLY_BT_COEX_PROFILE_NOTIF = 0xce - */ -enum iwl_bt_coex_profile_traffic_load { - IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, - IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, - IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, - IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, -/* - * There are no more even though below is a u8, the - * indication from the BT device only has two bits. - */ -}; - -#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1 -#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2 - -/* BT UART message - Share Part (BT -> WiFi) */ -#define BT_UART_MSG_FRAME1MSGTYPE_POS (0) -#define BT_UART_MSG_FRAME1MSGTYPE_MSK \ - (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS) -#define BT_UART_MSG_FRAME1SSN_POS (3) -#define BT_UART_MSG_FRAME1SSN_MSK \ - (0x3 << BT_UART_MSG_FRAME1SSN_POS) -#define BT_UART_MSG_FRAME1UPDATEREQ_POS (5) -#define BT_UART_MSG_FRAME1UPDATEREQ_MSK \ - (0x1 << BT_UART_MSG_FRAME1UPDATEREQ_POS) -#define BT_UART_MSG_FRAME1RESERVED_POS (6) -#define BT_UART_MSG_FRAME1RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME1RESERVED_POS) - -#define BT_UART_MSG_FRAME2OPENCONNECTIONS_POS (0) -#define BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK \ - (0x3 << BT_UART_MSG_FRAME2OPENCONNECTIONS_POS) -#define BT_UART_MSG_FRAME2TRAFFICLOAD_POS (2) -#define BT_UART_MSG_FRAME2TRAFFICLOAD_MSK \ - (0x3 << BT_UART_MSG_FRAME2TRAFFICLOAD_POS) -#define BT_UART_MSG_FRAME2CHLSEQN_POS (4) -#define BT_UART_MSG_FRAME2CHLSEQN_MSK \ - (0x1 << BT_UART_MSG_FRAME2CHLSEQN_POS) -#define BT_UART_MSG_FRAME2INBAND_POS (5) -#define BT_UART_MSG_FRAME2INBAND_MSK \ - (0x1 << BT_UART_MSG_FRAME2INBAND_POS) -#define BT_UART_MSG_FRAME2RESERVED_POS (6) -#define BT_UART_MSG_FRAME2RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME2RESERVED_POS) - -#define BT_UART_MSG_FRAME3SCOESCO_POS (0) -#define BT_UART_MSG_FRAME3SCOESCO_MSK \ - (0x1 << BT_UART_MSG_FRAME3SCOESCO_POS) -#define BT_UART_MSG_FRAME3SNIFF_POS (1) -#define BT_UART_MSG_FRAME3SNIFF_MSK \ - (0x1 << BT_UART_MSG_FRAME3SNIFF_POS) -#define BT_UART_MSG_FRAME3A2DP_POS (2) -#define BT_UART_MSG_FRAME3A2DP_MSK \ - (0x1 << BT_UART_MSG_FRAME3A2DP_POS) -#define BT_UART_MSG_FRAME3ACL_POS (3) -#define BT_UART_MSG_FRAME3ACL_MSK \ - (0x1 << BT_UART_MSG_FRAME3ACL_POS) -#define BT_UART_MSG_FRAME3MASTER_POS (4) -#define BT_UART_MSG_FRAME3MASTER_MSK \ - (0x1 << BT_UART_MSG_FRAME3MASTER_POS) -#define BT_UART_MSG_FRAME3OBEX_POS (5) -#define BT_UART_MSG_FRAME3OBEX_MSK \ - (0x1 << BT_UART_MSG_FRAME3OBEX_POS) -#define BT_UART_MSG_FRAME3RESERVED_POS (6) -#define BT_UART_MSG_FRAME3RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME3RESERVED_POS) - -#define BT_UART_MSG_FRAME4IDLEDURATION_POS (0) -#define BT_UART_MSG_FRAME4IDLEDURATION_MSK \ - (0x3F << BT_UART_MSG_FRAME4IDLEDURATION_POS) -#define BT_UART_MSG_FRAME4RESERVED_POS (6) -#define BT_UART_MSG_FRAME4RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME4RESERVED_POS) - -#define BT_UART_MSG_FRAME5TXACTIVITY_POS (0) -#define BT_UART_MSG_FRAME5TXACTIVITY_MSK \ - (0x3 << BT_UART_MSG_FRAME5TXACTIVITY_POS) -#define BT_UART_MSG_FRAME5RXACTIVITY_POS (2) -#define BT_UART_MSG_FRAME5RXACTIVITY_MSK \ - (0x3 << BT_UART_MSG_FRAME5RXACTIVITY_POS) -#define BT_UART_MSG_FRAME5ESCORETRANSMIT_POS (4) -#define BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK \ - (0x3 << BT_UART_MSG_FRAME5ESCORETRANSMIT_POS) -#define BT_UART_MSG_FRAME5RESERVED_POS (6) -#define BT_UART_MSG_FRAME5RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME5RESERVED_POS) - -#define BT_UART_MSG_FRAME6SNIFFINTERVAL_POS (0) -#define BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK \ - (0x1F << BT_UART_MSG_FRAME6SNIFFINTERVAL_POS) -#define BT_UART_MSG_FRAME6DISCOVERABLE_POS (5) -#define BT_UART_MSG_FRAME6DISCOVERABLE_MSK \ - (0x1 << BT_UART_MSG_FRAME6DISCOVERABLE_POS) -#define BT_UART_MSG_FRAME6RESERVED_POS (6) -#define BT_UART_MSG_FRAME6RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME6RESERVED_POS) - -#define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0) -#define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \ - (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS) -#define BT_UART_MSG_FRAME7PAGE_POS (3) -#define BT_UART_MSG_FRAME7PAGE_MSK \ - (0x1 << BT_UART_MSG_FRAME7PAGE_POS) -#define BT_UART_MSG_FRAME7INQUIRY_POS (4) -#define BT_UART_MSG_FRAME7INQUIRY_MSK \ - (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS) -#define BT_UART_MSG_FRAME7CONNECTABLE_POS (5) -#define BT_UART_MSG_FRAME7CONNECTABLE_MSK \ - (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS) -#define BT_UART_MSG_FRAME7RESERVED_POS (6) -#define BT_UART_MSG_FRAME7RESERVED_MSK \ - (0x3 << BT_UART_MSG_FRAME7RESERVED_POS) - -/* BT Session Activity 2 UART message (BT -> WiFi) */ -#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5) -#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \ - (0x1< - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - -#include -#include -#include -#include - -#include -#include - - -#include "iwl-dev.h" -#include "iwl-debug.h" -#include "iwl-io.h" -#include "iwl-agn.h" -#include "iwl-modparams.h" - -/* create and remove of files */ -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, priv, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \ - struct dentry *__tmp; \ - __tmp = debugfs_create_u32(#name, mode, \ - parent, ptr); \ - if (IS_ERR(__tmp) || !__tmp) \ - goto err; \ -} while (0) - -/* file operation */ -#define DEBUGFS_READ_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ - char __user *user_buf, \ - size_t count, loff_t *ppos); - -#define DEBUGFS_WRITE_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos); - - -#define DEBUGFS_READ_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_WRITE_FILE_OPS(name) \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - - -#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; - -static ssize_t iwl_dbgfs_sram_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - u32 val = 0; - char *buf; - ssize_t ret; - int i = 0; - bool device_format = false; - int offset = 0; - int len = 0; - int pos = 0; - int sram; - struct iwl_priv *priv = file->private_data; - const struct fw_img *img; - size_t bufsz; - - /* default is to dump the entire data segment */ - if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { - priv->dbgfs_sram_offset = 0x800000; - if (!priv->ucode_loaded) - return -EINVAL; - img = &priv->fw->img[priv->cur_ucode]; - priv->dbgfs_sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; - } - len = priv->dbgfs_sram_len; - - if (len == -4) { - device_format = true; - len = 4; - } - - bufsz = 50 + len * 4; - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", - len); - pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", - priv->dbgfs_sram_offset); - - /* adjust sram address since reads are only on even u32 boundaries */ - offset = priv->dbgfs_sram_offset & 0x3; - sram = priv->dbgfs_sram_offset & ~0x3; - - /* read the first u32 from sram */ - val = iwl_read_targ_mem(priv->trans, sram); - - for (; len; len--) { - /* put the address at the start of every line */ - if (i == 0) - pos += scnprintf(buf + pos, bufsz - pos, - "%08X: ", sram + offset); - - if (device_format) - pos += scnprintf(buf + pos, bufsz - pos, - "%02x", (val >> (8 * (3 - offset))) & 0xff); - else - pos += scnprintf(buf + pos, bufsz - pos, - "%02x ", (val >> (8 * offset)) & 0xff); - - /* if all bytes processed, read the next u32 from sram */ - if (++offset == 4) { - sram += 4; - offset = 0; - val = iwl_read_targ_mem(priv->trans, sram); - } - - /* put in extra spaces and split lines for human readability */ - if (++i == 16) { - i = 0; - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } else if (!(i & 7)) { - pos += scnprintf(buf + pos, bufsz - pos, " "); - } else if (!(i & 3)) { - pos += scnprintf(buf + pos, bufsz - pos, " "); - } - } - if (i) - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_sram_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[64]; - int buf_size; - u32 offset, len; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x,%x", &offset, &len) == 2) { - priv->dbgfs_sram_offset = offset; - priv->dbgfs_sram_len = len; - } else if (sscanf(buf, "%x", &offset) == 1) { - priv->dbgfs_sram_offset = offset; - priv->dbgfs_sram_len = -4; - } else { - priv->dbgfs_sram_offset = 0; - priv->dbgfs_sram_len = 0; - } - - return count; -} - -static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - const struct fw_img *img = &priv->fw->img[IWL_UCODE_WOWLAN]; - - if (!priv->wowlan_sram) - return -ENODATA; - - return simple_read_from_buffer(user_buf, count, ppos, - priv->wowlan_sram, - img->sec[IWL_UCODE_SECTION_DATA].len); -} -static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct iwl_station_entry *station; - struct iwl_tid_data *tid_data; - char *buf; - int i, j, pos = 0; - ssize_t ret; - /* Add 30 for initial string */ - const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations); - - buf = kmalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", - priv->num_stations); - - for (i = 0; i < IWLAGN_STATION_COUNT; i++) { - station = &priv->stations[i]; - if (!station->used) - continue; - pos += scnprintf(buf + pos, bufsz - pos, - "station %d - addr: %pM, flags: %#x\n", - i, station->sta.sta.addr, - station->sta.station_flags_msk); - pos += scnprintf(buf + pos, bufsz - pos, - "TID seqno next_rclmd " - "rate_n_flags state txq\n"); - - for (j = 0; j < IWL_MAX_TID_COUNT; j++) { - tid_data = &priv->tid_data[i][j]; - pos += scnprintf(buf + pos, bufsz - pos, - "%d: 0x%.4x 0x%.4x 0x%.8x " - "%d %.2d", - j, tid_data->seq_number, - tid_data->next_reclaimed, - tid_data->agg.rate_n_flags, - tid_data->agg.state, - tid_data->agg.txq_id); - - if (tid_data->agg.wait_for_ba) - pos += scnprintf(buf + pos, bufsz - pos, - " - waitforba"); - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_nvm_read(struct file *file, - char __user *user_buf, - size_t count, - loff_t *ppos) -{ - ssize_t ret; - struct iwl_priv *priv = file->private_data; - int pos = 0, ofs = 0, buf_size = 0; - const u8 *ptr; - char *buf; - u16 eeprom_ver; - size_t eeprom_len = priv->cfg->base_params->eeprom_size; - buf_size = 4 * eeprom_len + 256; - - if (eeprom_len % 16) - return -ENODATA; - - ptr = priv->eeprom; - if (!ptr) - return -ENOMEM; - - /* 4 characters for byte 0xYY */ - buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s, " - "version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", eeprom_ver); - for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { - pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); - hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, - buf_size - pos, 0); - pos += strlen(buf + pos); - if (buf_size - pos > 0) - buf[pos++] = '\n'; - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct ieee80211_channel *channels = NULL; - const struct ieee80211_supported_band *supp_band = NULL; - int pos = 0, i, bufsz = PAGE_SIZE; - char *buf; - ssize_t ret; - - if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); - if (supp_band) { - channels = supp_band->channels; - - pos += scnprintf(buf + pos, bufsz - pos, - "Displaying %d channels in 2.4GHz band 802.11bg):\n", - supp_band->n_channels); - - for (i = 0; i < supp_band->n_channels; i++) - pos += scnprintf(buf + pos, bufsz - pos, - "%d: %ddBm: BSS%s%s, %s.\n", - channels[i].hw_value, - channels[i].max_power, - channels[i].flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) - || (channels[i].flags & - IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i].flags & - IEEE80211_CHAN_PASSIVE_SCAN ? - "passive only" : "active/passive"); - } - supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); - if (supp_band) { - channels = supp_band->channels; - - pos += scnprintf(buf + pos, bufsz - pos, - "Displaying %d channels in 5.2GHz band (802.11a)\n", - supp_band->n_channels); - - for (i = 0; i < supp_band->n_channels; i++) - pos += scnprintf(buf + pos, bufsz - pos, - "%d: %ddBm: BSS%s%s, %s.\n", - channels[i].hw_value, - channels[i].max_power, - channels[i].flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) - || (channels[i].flags & - IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i].flags & - IEEE80211_CHAN_PASSIVE_SCAN ? - "passive only" : "active/passive"); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_status_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[512]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", - test_bit(STATUS_RF_KILL_HW, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", - test_bit(STATUS_CT_KILL, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", - test_bit(STATUS_ALIVE, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", - test_bit(STATUS_READY, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n", - test_bit(STATUS_GEO_CONFIGURED, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", - test_bit(STATUS_EXIT_PENDING, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", - test_bit(STATUS_STATISTICS, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", - test_bit(STATUS_SCANNING, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", - test_bit(STATUS_SCAN_ABORTING, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", - test_bit(STATUS_SCAN_HW, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", - test_bit(STATUS_POWER_PMI, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", - test_bit(STATUS_FW_ERROR, &priv->status)); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_rx_handlers_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = 24 * 64; /* 24 items * 64 char per item */ - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (cnt = 0; cnt < REPLY_MAX; cnt++) { - if (priv->rx_handlers_stats[cnt] > 0) - pos += scnprintf(buf + pos, bufsz - pos, - "\tRx handler[%36s]:\t\t %u\n", - iwl_dvm_get_cmd_string(cnt), - priv->rx_handlers_stats[cnt]); - } - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_rx_handlers_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - - char buf[8]; - int buf_size; - u32 reset_flag; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &reset_flag) != 1) - return -EFAULT; - if (reset_flag == 0) - memset(&priv->rx_handlers_stats[0], 0, - sizeof(priv->rx_handlers_stats)); - - return count; -} - -static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct iwl_rxon_context *ctx; - int pos = 0, i; - char buf[256 * NUM_IWL_RXON_CTX]; - const size_t bufsz = sizeof(buf); - - for_each_context(priv, ctx) { - pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n", - ctx->ctxid); - for (i = 0; i < AC_NUM; i++) { - pos += scnprintf(buf + pos, bufsz - pos, - "\tcw_min\tcw_max\taifsn\ttxop\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "AC[%d]\t%u\t%u\t%u\t%u\n", i, - ctx->qos_data.def_qos_parm.ac[i].cw_min, - ctx->qos_data.def_qos_parm.ac[i].cw_max, - ctx->qos_data.def_qos_parm.ac[i].aifsn, - ctx->qos_data.def_qos_parm.ac[i].edca_txop); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - char buf[100]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, - "Thermal Throttling Mode: %s\n", - tt->advanced_tt ? "Advance" : "Legacy"); - pos += scnprintf(buf + pos, bufsz - pos, - "Thermal Throttling State: %d\n", - tt->state); - if (tt->advanced_tt) { - restriction = tt->restriction + tt->state; - pos += scnprintf(buf + pos, bufsz - pos, - "Tx mode: %d\n", - restriction->tx_stream); - pos += scnprintf(buf + pos, bufsz - pos, - "Rx mode: %d\n", - restriction->rx_stream); - pos += scnprintf(buf + pos, bufsz - pos, - "HT mode: %d\n", - restriction->is_ht); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int ht40; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &ht40) != 1) - return -EFAULT; - if (!iwl_is_any_associated(priv)) - priv->disable_ht40 = ht40 ? true : false; - else - return -EINVAL; - - return count; -} - -static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[100]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, - "11n 40MHz Mode: %s\n", - priv->disable_ht40 ? "Disabled" : "Enabled"); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_temperature_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - - -static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int value; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%d", &value) != 1) - return -EINVAL; - - /* - * Our users expect 0 to be "CAM", but 0 isn't actually - * valid here. However, let's not confuse them and present - * IWL_POWER_INDEX_1 as "1", not "0". - */ - if (value == 0) - return -EINVAL; - else if (value > 0) - value -= 1; - - if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) - return -EINVAL; - - if (!iwl_is_ready_rf(priv)) - return -EAGAIN; - - priv->power_data.debug_sleep_level_override = value; - - mutex_lock(&priv->mutex); - iwl_power_update_mode(priv, true); - mutex_unlock(&priv->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[10]; - int pos, value; - const size_t bufsz = sizeof(buf); - - /* see the write function */ - value = priv->power_data.debug_sleep_level_override; - if (value >= 0) - value += 1; - - pos = scnprintf(buf, bufsz, "%d\n", value); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[200]; - int pos = 0, i; - const size_t bufsz = sizeof(buf); - struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; - - pos += scnprintf(buf + pos, bufsz - pos, - "flags: %#.2x\n", le16_to_cpu(cmd->flags)); - pos += scnprintf(buf + pos, bufsz - pos, - "RX/TX timeout: %d/%d usec\n", - le32_to_cpu(cmd->rx_data_timeout), - le32_to_cpu(cmd->tx_data_timeout)); - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - pos += scnprintf(buf + pos, bufsz - pos, - "sleep_interval[%d]: %d\n", i, - le32_to_cpu(cmd->sleep_interval[i])); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -DEBUGFS_READ_WRITE_FILE_OPS(sram); -DEBUGFS_READ_FILE_OPS(wowlan_sram); -DEBUGFS_READ_FILE_OPS(nvm); -DEBUGFS_READ_FILE_OPS(stations); -DEBUGFS_READ_FILE_OPS(channels); -DEBUGFS_READ_FILE_OPS(status); -DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers); -DEBUGFS_READ_FILE_OPS(qos); -DEBUGFS_READ_FILE_OPS(thermal_throttling); -DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); -DEBUGFS_READ_FILE_OPS(temperature); -DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); -DEBUGFS_READ_FILE_OPS(current_sleep_command); - -static const char *fmt_value = " %-30s %10u\n"; -static const char *fmt_hex = " %-30s 0x%02X\n"; -static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; -static const char *fmt_header = - "%-32s current cumulative delta max\n"; - -static int iwl_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) -{ - int p = 0; - u32 flag; - - lockdep_assert_held(&priv->statistics.lock); - - flag = le32_to_cpu(priv->statistics.flag); - - p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); - if (flag & UCODE_STATISTICS_CLEAR_MSK) - p += scnprintf(buf + p, bufsz - p, - "\tStatistics have been cleared\n"); - p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", - (flag & UCODE_STATISTICS_FREQUENCY_MSK) - ? "2.4 GHz" : "5.2 GHz"); - p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", - (flag & UCODE_STATISTICS_NARROW_BAND_MSK) - ? "enabled" : "disabled"); - - return p; -} - -static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char *buf; - int bufsz = sizeof(struct statistics_rx_phy) * 40 + - sizeof(struct statistics_rx_non_phy) * 40 + - sizeof(struct statistics_rx_ht_phy) * 40 + 400; - ssize_t ret; - struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; - struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; - struct statistics_rx_non_phy *general, *accum_general; - struct statistics_rx_non_phy *delta_general, *max_general; - struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* - * the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - spin_lock_bh(&priv->statistics.lock); - ofdm = &priv->statistics.rx_ofdm; - cck = &priv->statistics.rx_cck; - general = &priv->statistics.rx_non_phy; - ht = &priv->statistics.rx_ofdm_ht; - accum_ofdm = &priv->accum_stats.rx_ofdm; - accum_cck = &priv->accum_stats.rx_cck; - accum_general = &priv->accum_stats.rx_non_phy; - accum_ht = &priv->accum_stats.rx_ofdm_ht; - delta_ofdm = &priv->delta_stats.rx_ofdm; - delta_cck = &priv->delta_stats.rx_cck; - delta_general = &priv->delta_stats.rx_non_phy; - delta_ht = &priv->delta_stats.rx_ofdm_ht; - max_ofdm = &priv->max_delta_stats.rx_ofdm; - max_cck = &priv->max_delta_stats.rx_cck; - max_general = &priv->max_delta_stats.rx_non_phy; - max_ht = &priv->max_delta_stats.rx_ofdm_ht; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - OFDM:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ina_cnt:", - le32_to_cpu(ofdm->ina_cnt), - accum_ofdm->ina_cnt, - delta_ofdm->ina_cnt, max_ofdm->ina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_cnt:", - le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, - delta_ofdm->fina_cnt, max_ofdm->fina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "plcp_err:", - le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, - delta_ofdm->plcp_err, max_ofdm->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_err:", - le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, - delta_ofdm->crc32_err, max_ofdm->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "overrun_err:", - le32_to_cpu(ofdm->overrun_err), - accum_ofdm->overrun_err, delta_ofdm->overrun_err, - max_ofdm->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "early_overrun_err:", - le32_to_cpu(ofdm->early_overrun_err), - accum_ofdm->early_overrun_err, - delta_ofdm->early_overrun_err, - max_ofdm->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_good:", - le32_to_cpu(ofdm->crc32_good), - accum_ofdm->crc32_good, delta_ofdm->crc32_good, - max_ofdm->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "false_alarm_cnt:", - le32_to_cpu(ofdm->false_alarm_cnt), - accum_ofdm->false_alarm_cnt, - delta_ofdm->false_alarm_cnt, - max_ofdm->false_alarm_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_sync_err_cnt:", - le32_to_cpu(ofdm->fina_sync_err_cnt), - accum_ofdm->fina_sync_err_cnt, - delta_ofdm->fina_sync_err_cnt, - max_ofdm->fina_sync_err_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sfd_timeout:", - le32_to_cpu(ofdm->sfd_timeout), - accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, - max_ofdm->sfd_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_timeout:", - le32_to_cpu(ofdm->fina_timeout), - accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, - max_ofdm->fina_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "unresponded_rts:", - le32_to_cpu(ofdm->unresponded_rts), - accum_ofdm->unresponded_rts, - delta_ofdm->unresponded_rts, - max_ofdm->unresponded_rts); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rxe_frame_lmt_ovrun:", - le32_to_cpu(ofdm->rxe_frame_limit_overrun), - accum_ofdm->rxe_frame_limit_overrun, - delta_ofdm->rxe_frame_limit_overrun, - max_ofdm->rxe_frame_limit_overrun); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ack_cnt:", - le32_to_cpu(ofdm->sent_ack_cnt), - accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, - max_ofdm->sent_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_cts_cnt:", - le32_to_cpu(ofdm->sent_cts_cnt), - accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, - max_ofdm->sent_cts_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ba_rsp_cnt:", - le32_to_cpu(ofdm->sent_ba_rsp_cnt), - accum_ofdm->sent_ba_rsp_cnt, - delta_ofdm->sent_ba_rsp_cnt, - max_ofdm->sent_ba_rsp_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dsp_self_kill:", - le32_to_cpu(ofdm->dsp_self_kill), - accum_ofdm->dsp_self_kill, - delta_ofdm->dsp_self_kill, - max_ofdm->dsp_self_kill); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "mh_format_err:", - le32_to_cpu(ofdm->mh_format_err), - accum_ofdm->mh_format_err, - delta_ofdm->mh_format_err, - max_ofdm->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "re_acq_main_rssi_sum:", - le32_to_cpu(ofdm->re_acq_main_rssi_sum), - accum_ofdm->re_acq_main_rssi_sum, - delta_ofdm->re_acq_main_rssi_sum, - max_ofdm->re_acq_main_rssi_sum); - - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - CCK:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ina_cnt:", - le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, - delta_cck->ina_cnt, max_cck->ina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_cnt:", - le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, - delta_cck->fina_cnt, max_cck->fina_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "plcp_err:", - le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, - delta_cck->plcp_err, max_cck->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_err:", - le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, - delta_cck->crc32_err, max_cck->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "overrun_err:", - le32_to_cpu(cck->overrun_err), - accum_cck->overrun_err, delta_cck->overrun_err, - max_cck->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "early_overrun_err:", - le32_to_cpu(cck->early_overrun_err), - accum_cck->early_overrun_err, - delta_cck->early_overrun_err, - max_cck->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_good:", - le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, - delta_cck->crc32_good, max_cck->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "false_alarm_cnt:", - le32_to_cpu(cck->false_alarm_cnt), - accum_cck->false_alarm_cnt, - delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_sync_err_cnt:", - le32_to_cpu(cck->fina_sync_err_cnt), - accum_cck->fina_sync_err_cnt, - delta_cck->fina_sync_err_cnt, - max_cck->fina_sync_err_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sfd_timeout:", - le32_to_cpu(cck->sfd_timeout), - accum_cck->sfd_timeout, delta_cck->sfd_timeout, - max_cck->sfd_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "fina_timeout:", - le32_to_cpu(cck->fina_timeout), - accum_cck->fina_timeout, delta_cck->fina_timeout, - max_cck->fina_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "unresponded_rts:", - le32_to_cpu(cck->unresponded_rts), - accum_cck->unresponded_rts, delta_cck->unresponded_rts, - max_cck->unresponded_rts); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rxe_frame_lmt_ovrun:", - le32_to_cpu(cck->rxe_frame_limit_overrun), - accum_cck->rxe_frame_limit_overrun, - delta_cck->rxe_frame_limit_overrun, - max_cck->rxe_frame_limit_overrun); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ack_cnt:", - le32_to_cpu(cck->sent_ack_cnt), - accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, - max_cck->sent_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_cts_cnt:", - le32_to_cpu(cck->sent_cts_cnt), - accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, - max_cck->sent_cts_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sent_ba_rsp_cnt:", - le32_to_cpu(cck->sent_ba_rsp_cnt), - accum_cck->sent_ba_rsp_cnt, - delta_cck->sent_ba_rsp_cnt, - max_cck->sent_ba_rsp_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dsp_self_kill:", - le32_to_cpu(cck->dsp_self_kill), - accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, - max_cck->dsp_self_kill); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "mh_format_err:", - le32_to_cpu(cck->mh_format_err), - accum_cck->mh_format_err, delta_cck->mh_format_err, - max_cck->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "re_acq_main_rssi_sum:", - le32_to_cpu(cck->re_acq_main_rssi_sum), - accum_cck->re_acq_main_rssi_sum, - delta_cck->re_acq_main_rssi_sum, - max_cck->re_acq_main_rssi_sum); - - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - GENERAL:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bogus_cts:", - le32_to_cpu(general->bogus_cts), - accum_general->bogus_cts, delta_general->bogus_cts, - max_general->bogus_cts); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bogus_ack:", - le32_to_cpu(general->bogus_ack), - accum_general->bogus_ack, delta_general->bogus_ack, - max_general->bogus_ack); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "non_bssid_frames:", - le32_to_cpu(general->non_bssid_frames), - accum_general->non_bssid_frames, - delta_general->non_bssid_frames, - max_general->non_bssid_frames); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "filtered_frames:", - le32_to_cpu(general->filtered_frames), - accum_general->filtered_frames, - delta_general->filtered_frames, - max_general->filtered_frames); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "non_channel_beacons:", - le32_to_cpu(general->non_channel_beacons), - accum_general->non_channel_beacons, - delta_general->non_channel_beacons, - max_general->non_channel_beacons); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "channel_beacons:", - le32_to_cpu(general->channel_beacons), - accum_general->channel_beacons, - delta_general->channel_beacons, - max_general->channel_beacons); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "num_missed_bcon:", - le32_to_cpu(general->num_missed_bcon), - accum_general->num_missed_bcon, - delta_general->num_missed_bcon, - max_general->num_missed_bcon); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "adc_rx_saturation_time:", - le32_to_cpu(general->adc_rx_saturation_time), - accum_general->adc_rx_saturation_time, - delta_general->adc_rx_saturation_time, - max_general->adc_rx_saturation_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ina_detect_search_tm:", - le32_to_cpu(general->ina_detection_search_time), - accum_general->ina_detection_search_time, - delta_general->ina_detection_search_time, - max_general->ina_detection_search_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_silence_rssi_a:", - le32_to_cpu(general->beacon_silence_rssi_a), - accum_general->beacon_silence_rssi_a, - delta_general->beacon_silence_rssi_a, - max_general->beacon_silence_rssi_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_silence_rssi_b:", - le32_to_cpu(general->beacon_silence_rssi_b), - accum_general->beacon_silence_rssi_b, - delta_general->beacon_silence_rssi_b, - max_general->beacon_silence_rssi_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_silence_rssi_c:", - le32_to_cpu(general->beacon_silence_rssi_c), - accum_general->beacon_silence_rssi_c, - delta_general->beacon_silence_rssi_c, - max_general->beacon_silence_rssi_c); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "interference_data_flag:", - le32_to_cpu(general->interference_data_flag), - accum_general->interference_data_flag, - delta_general->interference_data_flag, - max_general->interference_data_flag); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "channel_load:", - le32_to_cpu(general->channel_load), - accum_general->channel_load, - delta_general->channel_load, - max_general->channel_load); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dsp_false_alarms:", - le32_to_cpu(general->dsp_false_alarms), - accum_general->dsp_false_alarms, - delta_general->dsp_false_alarms, - max_general->dsp_false_alarms); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_rssi_a:", - le32_to_cpu(general->beacon_rssi_a), - accum_general->beacon_rssi_a, - delta_general->beacon_rssi_a, - max_general->beacon_rssi_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_rssi_b:", - le32_to_cpu(general->beacon_rssi_b), - accum_general->beacon_rssi_b, - delta_general->beacon_rssi_b, - max_general->beacon_rssi_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_rssi_c:", - le32_to_cpu(general->beacon_rssi_c), - accum_general->beacon_rssi_c, - delta_general->beacon_rssi_c, - max_general->beacon_rssi_c); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_energy_a:", - le32_to_cpu(general->beacon_energy_a), - accum_general->beacon_energy_a, - delta_general->beacon_energy_a, - max_general->beacon_energy_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_energy_b:", - le32_to_cpu(general->beacon_energy_b), - accum_general->beacon_energy_b, - delta_general->beacon_energy_b, - max_general->beacon_energy_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "beacon_energy_c:", - le32_to_cpu(general->beacon_energy_c), - accum_general->beacon_energy_c, - delta_general->beacon_energy_c, - max_general->beacon_energy_c); - - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Rx - OFDM_HT:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "plcp_err:", - le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, - delta_ht->plcp_err, max_ht->plcp_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "overrun_err:", - le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, - delta_ht->overrun_err, max_ht->overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "early_overrun_err:", - le32_to_cpu(ht->early_overrun_err), - accum_ht->early_overrun_err, - delta_ht->early_overrun_err, - max_ht->early_overrun_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_good:", - le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, - delta_ht->crc32_good, max_ht->crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "crc32_err:", - le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, - delta_ht->crc32_err, max_ht->crc32_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "mh_format_err:", - le32_to_cpu(ht->mh_format_err), - accum_ht->mh_format_err, - delta_ht->mh_format_err, max_ht->mh_format_err); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg_crc32_good:", - le32_to_cpu(ht->agg_crc32_good), - accum_ht->agg_crc32_good, - delta_ht->agg_crc32_good, max_ht->agg_crc32_good); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg_mpdu_cnt:", - le32_to_cpu(ht->agg_mpdu_cnt), - accum_ht->agg_mpdu_cnt, - delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg_cnt:", - le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, - delta_ht->agg_cnt, max_ht->agg_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "unsupport_mcs:", - le32_to_cpu(ht->unsupport_mcs), - accum_ht->unsupport_mcs, - delta_ht->unsupport_mcs, max_ht->unsupport_mcs); - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct statistics_tx) * 48) + 250; - ssize_t ret; - struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - spin_lock_bh(&priv->statistics.lock); - - tx = &priv->statistics.tx; - accum_tx = &priv->accum_stats.tx; - delta_tx = &priv->delta_stats.tx; - max_tx = &priv->max_delta_stats.tx; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_Tx:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "preamble:", - le32_to_cpu(tx->preamble_cnt), - accum_tx->preamble_cnt, - delta_tx->preamble_cnt, max_tx->preamble_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rx_detected_cnt:", - le32_to_cpu(tx->rx_detected_cnt), - accum_tx->rx_detected_cnt, - delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bt_prio_defer_cnt:", - le32_to_cpu(tx->bt_prio_defer_cnt), - accum_tx->bt_prio_defer_cnt, - delta_tx->bt_prio_defer_cnt, - max_tx->bt_prio_defer_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "bt_prio_kill_cnt:", - le32_to_cpu(tx->bt_prio_kill_cnt), - accum_tx->bt_prio_kill_cnt, - delta_tx->bt_prio_kill_cnt, - max_tx->bt_prio_kill_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "few_bytes_cnt:", - le32_to_cpu(tx->few_bytes_cnt), - accum_tx->few_bytes_cnt, - delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "cts_timeout:", - le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, - delta_tx->cts_timeout, max_tx->cts_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ack_timeout:", - le32_to_cpu(tx->ack_timeout), - accum_tx->ack_timeout, - delta_tx->ack_timeout, max_tx->ack_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "expected_ack_cnt:", - le32_to_cpu(tx->expected_ack_cnt), - accum_tx->expected_ack_cnt, - delta_tx->expected_ack_cnt, - max_tx->expected_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "actual_ack_cnt:", - le32_to_cpu(tx->actual_ack_cnt), - accum_tx->actual_ack_cnt, - delta_tx->actual_ack_cnt, - max_tx->actual_ack_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "dump_msdu_cnt:", - le32_to_cpu(tx->dump_msdu_cnt), - accum_tx->dump_msdu_cnt, - delta_tx->dump_msdu_cnt, - max_tx->dump_msdu_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "abort_nxt_frame_mismatch:", - le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), - accum_tx->burst_abort_next_frame_mismatch_cnt, - delta_tx->burst_abort_next_frame_mismatch_cnt, - max_tx->burst_abort_next_frame_mismatch_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "abort_missing_nxt_frame:", - le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), - accum_tx->burst_abort_missing_next_frame_cnt, - delta_tx->burst_abort_missing_next_frame_cnt, - max_tx->burst_abort_missing_next_frame_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "cts_timeout_collision:", - le32_to_cpu(tx->cts_timeout_collision), - accum_tx->cts_timeout_collision, - delta_tx->cts_timeout_collision, - max_tx->cts_timeout_collision); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "ack_ba_timeout_collision:", - le32_to_cpu(tx->ack_or_ba_timeout_collision), - accum_tx->ack_or_ba_timeout_collision, - delta_tx->ack_or_ba_timeout_collision, - max_tx->ack_or_ba_timeout_collision); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg ba_timeout:", - le32_to_cpu(tx->agg.ba_timeout), - accum_tx->agg.ba_timeout, - delta_tx->agg.ba_timeout, - max_tx->agg.ba_timeout); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg ba_resched_frames:", - le32_to_cpu(tx->agg.ba_reschedule_frames), - accum_tx->agg.ba_reschedule_frames, - delta_tx->agg.ba_reschedule_frames, - max_tx->agg.ba_reschedule_frames); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_agg_frame:", - le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), - accum_tx->agg.scd_query_agg_frame_cnt, - delta_tx->agg.scd_query_agg_frame_cnt, - max_tx->agg.scd_query_agg_frame_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_no_agg:", - le32_to_cpu(tx->agg.scd_query_no_agg), - accum_tx->agg.scd_query_no_agg, - delta_tx->agg.scd_query_no_agg, - max_tx->agg.scd_query_no_agg); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_agg:", - le32_to_cpu(tx->agg.scd_query_agg), - accum_tx->agg.scd_query_agg, - delta_tx->agg.scd_query_agg, - max_tx->agg.scd_query_agg); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg scd_query_mismatch:", - le32_to_cpu(tx->agg.scd_query_mismatch), - accum_tx->agg.scd_query_mismatch, - delta_tx->agg.scd_query_mismatch, - max_tx->agg.scd_query_mismatch); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg frame_not_ready:", - le32_to_cpu(tx->agg.frame_not_ready), - accum_tx->agg.frame_not_ready, - delta_tx->agg.frame_not_ready, - max_tx->agg.frame_not_ready); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg underrun:", - le32_to_cpu(tx->agg.underrun), - accum_tx->agg.underrun, - delta_tx->agg.underrun, max_tx->agg.underrun); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg bt_prio_kill:", - le32_to_cpu(tx->agg.bt_prio_kill), - accum_tx->agg.bt_prio_kill, - delta_tx->agg.bt_prio_kill, - max_tx->agg.bt_prio_kill); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "agg rx_ba_rsp_cnt:", - le32_to_cpu(tx->agg.rx_ba_rsp_cnt), - accum_tx->agg.rx_ba_rsp_cnt, - delta_tx->agg.rx_ba_rsp_cnt, - max_tx->agg.rx_ba_rsp_cnt); - - if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { - pos += scnprintf(buf + pos, bufsz - pos, - "tx power: (1/2 dB step)\n"); - if ((priv->hw_params.valid_tx_ant & ANT_A) && - tx->tx_power.ant_a) - pos += scnprintf(buf + pos, bufsz - pos, - fmt_hex, "antenna A:", - tx->tx_power.ant_a); - if ((priv->hw_params.valid_tx_ant & ANT_B) && - tx->tx_power.ant_b) - pos += scnprintf(buf + pos, bufsz - pos, - fmt_hex, "antenna B:", - tx->tx_power.ant_b); - if ((priv->hw_params.valid_tx_ant & ANT_C) && - tx->tx_power.ant_c) - pos += scnprintf(buf + pos, bufsz - pos, - fmt_hex, "antenna C:", - tx->tx_power.ant_c); - } - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char *buf; - int bufsz = sizeof(struct statistics_general) * 10 + 300; - ssize_t ret; - struct statistics_general_common *general, *accum_general; - struct statistics_general_common *delta_general, *max_general; - struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; - struct statistics_div *div, *accum_div, *delta_div, *max_div; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - - spin_lock_bh(&priv->statistics.lock); - - general = &priv->statistics.common; - dbg = &priv->statistics.common.dbg; - div = &priv->statistics.common.div; - accum_general = &priv->accum_stats.common; - accum_dbg = &priv->accum_stats.common.dbg; - accum_div = &priv->accum_stats.common.div; - delta_general = &priv->delta_stats.common; - max_general = &priv->max_delta_stats.common; - delta_dbg = &priv->delta_stats.common.dbg; - max_dbg = &priv->max_delta_stats.common.dbg; - delta_div = &priv->delta_stats.common.div; - max_div = &priv->max_delta_stats.common.div; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_header, "Statistics_General:"); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_value, "temperature:", - le32_to_cpu(general->temperature)); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_value, "temperature_m:", - le32_to_cpu(general->temperature_m)); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_value, "ttl_timestamp:", - le32_to_cpu(general->ttl_timestamp)); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "burst_check:", - le32_to_cpu(dbg->burst_check), - accum_dbg->burst_check, - delta_dbg->burst_check, max_dbg->burst_check); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "burst_count:", - le32_to_cpu(dbg->burst_count), - accum_dbg->burst_count, - delta_dbg->burst_count, max_dbg->burst_count); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "wait_for_silence_timeout_count:", - le32_to_cpu(dbg->wait_for_silence_timeout_cnt), - accum_dbg->wait_for_silence_timeout_cnt, - delta_dbg->wait_for_silence_timeout_cnt, - max_dbg->wait_for_silence_timeout_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "sleep_time:", - le32_to_cpu(general->sleep_time), - accum_general->sleep_time, - delta_general->sleep_time, max_general->sleep_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "slots_out:", - le32_to_cpu(general->slots_out), - accum_general->slots_out, - delta_general->slots_out, max_general->slots_out); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "slots_idle:", - le32_to_cpu(general->slots_idle), - accum_general->slots_idle, - delta_general->slots_idle, max_general->slots_idle); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "tx_on_a:", - le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, - delta_div->tx_on_a, max_div->tx_on_a); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "tx_on_b:", - le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, - delta_div->tx_on_b, max_div->tx_on_b); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "exec_time:", - le32_to_cpu(div->exec_time), accum_div->exec_time, - delta_div->exec_time, max_div->exec_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "probe_time:", - le32_to_cpu(div->probe_time), accum_div->probe_time, - delta_div->probe_time, max_div->probe_time); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "rx_enable_counter:", - le32_to_cpu(general->rx_enable_counter), - accum_general->rx_enable_counter, - delta_general->rx_enable_counter, - max_general->rx_enable_counter); - pos += scnprintf(buf + pos, bufsz - pos, - fmt_table, "num_of_sos_states:", - le32_to_cpu(general->num_of_sos_states), - accum_general->num_of_sos_states, - delta_general->num_of_sos_states, - max_general->num_of_sos_states); - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct statistics_bt_activity) * 24) + 200; - ssize_t ret; - struct statistics_bt_activity *bt, *accum_bt; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - if (!priv->bt_enable_flag) - return -EINVAL; - - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); - mutex_unlock(&priv->mutex); - - if (ret) - return -EAGAIN; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* - * the statistic information display here is based on - * the last statistics notification from uCode - * might not reflect the current uCode activity - */ - - spin_lock_bh(&priv->statistics.lock); - - bt = &priv->statistics.bt_activity; - accum_bt = &priv->accum_stats.bt_activity; - - pos += iwl_statistics_flag(priv, buf, bufsz); - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_BT:\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\t\t\tcurrent\t\t\taccumulative\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_tx_req_cnt), - accum_bt->hi_priority_tx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_tx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_tx_denied_cnt), - accum_bt->hi_priority_tx_denied_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_tx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_tx_req_cnt), - accum_bt->lo_priority_tx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_tx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_tx_denied_cnt), - accum_bt->lo_priority_tx_denied_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_rx_req_cnt), - accum_bt->hi_priority_rx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "hi_priority_rx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->hi_priority_rx_denied_cnt), - accum_bt->hi_priority_rx_denied_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_rx_req_cnt:\t\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_rx_req_cnt), - accum_bt->lo_priority_rx_req_cnt); - pos += scnprintf(buf + pos, bufsz - pos, - "lo_priority_rx_denied_cnt:\t%u\t\t\t%u\n", - le32_to_cpu(bt->lo_priority_rx_denied_cnt), - accum_bt->lo_priority_rx_denied_cnt); - - pos += scnprintf(buf + pos, bufsz - pos, - "(rx)num_bt_kills:\t\t%u\t\t\t%u\n", - le32_to_cpu(priv->statistics.num_bt_kills), - priv->statistics.accum_num_bt_kills); - - spin_unlock_bh(&priv->statistics.lock); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_reply_tx_error_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - int pos = 0; - char *buf; - int bufsz = (sizeof(struct reply_tx_error_statistics) * 24) + - (sizeof(struct reply_agg_tx_error_statistics) * 24) + 200; - ssize_t ret; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "Statistics_TX_Error:\n"); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_DELAY), - priv->reply_tx_stats.pp_delay); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_FEW_BYTES), - priv->reply_tx_stats.pp_few_bytes); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_BT_PRIO), - priv->reply_tx_stats.pp_bt_prio); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_QUIET_PERIOD), - priv->reply_tx_stats.pp_quiet_period); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_POSTPONE_CALC_TTAK), - priv->reply_tx_stats.pp_calc_ttak); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_tx_fail_reason( - TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY), - priv->reply_tx_stats.int_crossed_retry); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_SHORT_LIMIT), - priv->reply_tx_stats.short_limit); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_LONG_LIMIT), - priv->reply_tx_stats.long_limit); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_UNDERRUN), - priv->reply_tx_stats.fifo_underrun); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_DRAIN_FLOW), - priv->reply_tx_stats.drain_flow); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_RFKILL_FLUSH), - priv->reply_tx_stats.rfkill_flush); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_LIFE_EXPIRE), - priv->reply_tx_stats.life_expire); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_DEST_PS), - priv->reply_tx_stats.dest_ps); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_HOST_ABORTED), - priv->reply_tx_stats.host_abort); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_BT_RETRY), - priv->reply_tx_stats.pp_delay); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_STA_INVALID), - priv->reply_tx_stats.sta_invalid); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_FRAG_DROPPED), - priv->reply_tx_stats.frag_drop); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_TID_DISABLE), - priv->reply_tx_stats.tid_disable); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_FIFO_FLUSHED), - priv->reply_tx_stats.fifo_flush); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_tx_fail_reason( - TX_STATUS_FAIL_INSUFFICIENT_CF_POLL), - priv->reply_tx_stats.insuff_cf_poll); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_tx_fail_reason(TX_STATUS_FAIL_PASSIVE_NO_RX), - priv->reply_tx_stats.fail_hw_drop); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_tx_fail_reason( - TX_STATUS_FAIL_NO_BEACON_ON_RADAR), - priv->reply_tx_stats.sta_color_mismatch); - pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", - priv->reply_tx_stats.unknown); - - pos += scnprintf(buf + pos, bufsz - pos, - "\nStatistics_Agg_TX_Error:\n"); - - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_UNDERRUN_MSK), - priv->reply_agg_tx_stats.underrun); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_BT_PRIO_MSK), - priv->reply_agg_tx_stats.bt_prio); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_FEW_BYTES_MSK), - priv->reply_agg_tx_stats.few_bytes); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_ABORT_MSK), - priv->reply_agg_tx_stats.abort); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_LAST_SENT_TTL_MSK), - priv->reply_agg_tx_stats.last_sent_ttl); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK), - priv->reply_agg_tx_stats.last_sent_try); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_LAST_SENT_BT_KILL_MSK), - priv->reply_agg_tx_stats.last_sent_bt_kill); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_SCD_QUERY_MSK), - priv->reply_agg_tx_stats.scd_query); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t%u\n", - iwl_get_agg_tx_fail_reason( - AGG_TX_STATE_TEST_BAD_CRC32_MSK), - priv->reply_agg_tx_stats.bad_crc32); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_RESPONSE_MSK), - priv->reply_agg_tx_stats.response); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DUMP_TX_MSK), - priv->reply_agg_tx_stats.dump_tx); - pos += scnprintf(buf + pos, bufsz - pos, "%s:\t\t\t%u\n", - iwl_get_agg_tx_fail_reason(AGG_TX_STATE_DELAY_TX_MSK), - priv->reply_agg_tx_stats.delay_tx); - pos += scnprintf(buf + pos, bufsz - pos, "UNKNOWN:\t\t\t%u\n", - priv->reply_agg_tx_stats.unknown); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; - ssize_t ret; - struct iwl_sensitivity_data *data; - - data = &priv->sensitivity_data; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", - data->auto_corr_ofdm); - pos += scnprintf(buf + pos, bufsz - pos, - "auto_corr_ofdm_mrc:\t\t %u\n", - data->auto_corr_ofdm_mrc); - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", - data->auto_corr_ofdm_x1); - pos += scnprintf(buf + pos, bufsz - pos, - "auto_corr_ofdm_mrc_x1:\t\t %u\n", - data->auto_corr_ofdm_mrc_x1); - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", - data->auto_corr_cck); - pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", - data->auto_corr_cck_mrc); - pos += scnprintf(buf + pos, bufsz - pos, - "last_bad_plcp_cnt_ofdm:\t\t %u\n", - data->last_bad_plcp_cnt_ofdm); - pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", - data->last_fa_cnt_ofdm); - pos += scnprintf(buf + pos, bufsz - pos, - "last_bad_plcp_cnt_cck:\t\t %u\n", - data->last_bad_plcp_cnt_cck); - pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", - data->last_fa_cnt_cck); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", - data->nrg_curr_state); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", - data->nrg_prev_state); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); - for (cnt = 0; cnt < 10; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->nrg_value[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); - for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->nrg_silence_rssi[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", - data->nrg_silence_ref); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", - data->nrg_energy_idx); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", - data->nrg_silence_idx); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", - data->nrg_th_cck); - pos += scnprintf(buf + pos, bufsz - pos, - "nrg_auto_corr_silence_diff:\t %u\n", - data->nrg_auto_corr_silence_diff); - pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", - data->num_in_cck_no_fa); - pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", - data->nrg_th_ofdm); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - - -static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - int cnt = 0; - char *buf; - int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; - ssize_t ret; - struct iwl_chain_noise_data *data; - - data = &priv->chain_noise_data; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", - data->active_chains); - pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", - data->chain_noise_a); - pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", - data->chain_noise_b); - pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", - data->chain_noise_c); - pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", - data->chain_signal_a); - pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", - data->chain_signal_b); - pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", - data->chain_signal_c); - pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", - data->beacon_count); - - pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); - for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->disconn_array[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); - for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { - pos += scnprintf(buf + pos, bufsz - pos, " %u", - data->delta_gain_code[cnt]); - } - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", - data->radio_write); - pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", - data->state); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[60]; - int pos = 0; - const size_t bufsz = sizeof(buf); - u32 pwrsave_status; - - pwrsave_status = iwl_read32(priv->trans, CSR_GP_CNTRL) & - CSR_GP_REG_POWER_SAVE_STATUS_MSK; - - pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", - (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : - (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : - (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : - "error"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int clear; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &clear) != 1) - return -EFAULT; - - /* make request to uCode to retrieve statistics information */ - mutex_lock(&priv->mutex); - iwl_send_statistics_request(priv, CMD_SYNC, true); - mutex_unlock(&priv->mutex); - - return count; -} - -static ssize_t iwl_dbgfs_ucode_tracing_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[128]; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", - priv->event_log.ucode_trace ? "On" : "Off"); - pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", - priv->event_log.non_wraps_count); - pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", - priv->event_log.wraps_once_count); - pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", - priv->event_log.wraps_more_count); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int trace; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &trace) != 1) - return -EFAULT; - - if (trace) { - priv->event_log.ucode_trace = true; - if (iwl_is_alive(priv)) { - /* start collecting data now */ - mod_timer(&priv->ucode_trace, jiffies); - } - } else { - priv->event_log.ucode_trace = false; - del_timer_sync(&priv->ucode_trace); - } - - return count; -} - -static ssize_t iwl_dbgfs_rxon_flags_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int len = 0; - char buf[20]; - - len = sprintf(buf, "0x%04X\n", - le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags)); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t iwl_dbgfs_rxon_filter_flags_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int len = 0; - char buf[20]; - - len = sprintf(buf, "0x%04X\n", - le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags)); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t iwl_dbgfs_missed_beacon_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[12]; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "%d\n", - priv->missed_beacon_threshold); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_missed_beacon_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int missed; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &missed) != 1) - return -EINVAL; - - if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || - missed > IWL_MISSED_BEACON_THRESHOLD_MAX) - priv->missed_beacon_threshold = - IWL_MISSED_BEACON_THRESHOLD_DEF; - else - priv->missed_beacon_threshold = missed; - - return count; -} - -static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[12]; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "%u\n", - priv->plcp_delta_threshold); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int plcp; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &plcp) != 1) - return -EINVAL; - if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || - (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) - priv->plcp_delta_threshold = - IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; - else - priv->plcp_delta_threshold = plcp; - return count; -} - -static ssize_t iwl_dbgfs_rf_reset_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - int pos = 0; - char buf[300]; - const size_t bufsz = sizeof(buf); - struct iwl_rf_reset *rf_reset = &priv->rf_reset; - - pos += scnprintf(buf + pos, bufsz - pos, - "RF reset statistics\n"); - pos += scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request: %d\n", - rf_reset->reset_request_count); - pos += scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request success: %d\n", - rf_reset->reset_success_count); - pos += scnprintf(buf + pos, bufsz - pos, - "\tnumber of reset request reject: %d\n", - rf_reset->reset_reject_count); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_rf_reset_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - int ret; - - ret = iwl_force_rf_reset(priv, true); - return ret ? ret : count; -} - -static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int flush; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &flush) != 1) - return -EINVAL; - - if (iwl_is_rfkill(priv)) - return -EFAULT; - - iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); - - return count; -} - -static ssize_t iwl_dbgfs_bt_traffic_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - int pos = 0; - char buf[200]; - const size_t bufsz = sizeof(buf); - - if (!priv->bt_enable_flag) { - pos += scnprintf(buf + pos, bufsz - pos, "BT coex disabled\n"); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); - } - pos += scnprintf(buf + pos, bufsz - pos, "BT enable flag: 0x%x\n", - priv->bt_enable_flag); - pos += scnprintf(buf + pos, bufsz - pos, "BT in %s mode\n", - priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - pos += scnprintf(buf + pos, bufsz - pos, "BT status: %s, " - "last traffic notif: %d\n", - priv->bt_status ? "On" : "Off", priv->last_bt_traffic_load); - pos += scnprintf(buf + pos, bufsz - pos, "ch_announcement: %d, " - "kill_ack_mask: %x, kill_cts_mask: %x\n", - priv->bt_ch_announce, priv->kill_ack_mask, - priv->kill_cts_mask); - - pos += scnprintf(buf + pos, bufsz - pos, "bluetooth traffic load: "); - switch (priv->bt_traffic_load) { - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - pos += scnprintf(buf + pos, bufsz - pos, "Continuous\n"); - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - pos += scnprintf(buf + pos, bufsz - pos, "High\n"); - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - pos += scnprintf(buf + pos, bufsz - pos, "Low\n"); - break; - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - default: - pos += scnprintf(buf + pos, bufsz - pos, "None\n"); - break; - } - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - - int pos = 0; - char buf[40]; - const size_t bufsz = sizeof(buf); - - if (priv->cfg->ht_params) - pos += scnprintf(buf + pos, bufsz - pos, - "use %s for aggregation\n", - (priv->hw_params.use_rts_for_aggregation) ? - "rts/cts" : "cts-to-self"); - else - pos += scnprintf(buf + pos, bufsz - pos, "N/A"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - int rts; - - if (!priv->cfg->ht_params) - return -EINVAL; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &rts) != 1) - return -EINVAL; - if (rts) - priv->hw_params.use_rts_for_aggregation = true; - else - priv->hw_params.use_rts_for_aggregation = false; - return count; -} - -static int iwl_cmd_echo_test(struct iwl_priv *priv) -{ - int ret; - struct iwl_host_cmd cmd = { - .id = REPLY_ECHO, - .len = { 0 }, - .flags = CMD_SYNC, - }; - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) - IWL_ERR(priv, "echo testing fail: 0X%x\n", ret); - else - IWL_DEBUG_INFO(priv, "echo testing pass\n"); - return ret; -} - -static ssize_t iwl_dbgfs_echo_test_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - iwl_cmd_echo_test(priv); - return count; -} - -static ssize_t iwl_dbgfs_log_event_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char *buf; - int pos = 0; - ssize_t ret = -ENOMEM; - - ret = pos = iwl_dump_nic_event_log(priv, true, &buf, true); - if (buf) { - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - } - return ret; -} - -static ssize_t iwl_dbgfs_log_event_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - u32 event_log_flag; - char buf[8]; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d", &event_log_flag) != 1) - return -EFAULT; - if (event_log_flag == 1) - iwl_dump_nic_event_log(priv, true, NULL, false); - - return count; -} - -static ssize_t iwl_dbgfs_calib_disabled_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[120]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, - "Sensitivity calibrations %s\n", - (priv->calib_disabled & - IWL_SENSITIVITY_CALIB_DISABLED) ? - "DISABLED" : "ENABLED"); - pos += scnprintf(buf + pos, bufsz - pos, - "Chain noise calibrations %s\n", - (priv->calib_disabled & - IWL_CHAIN_NOISE_CALIB_DISABLED) ? - "DISABLED" : "ENABLED"); - pos += scnprintf(buf + pos, bufsz - pos, - "Tx power calibrations %s\n", - (priv->calib_disabled & - IWL_TX_POWER_CALIB_DISABLED) ? - "DISABLED" : "ENABLED"); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_calib_disabled_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_priv *priv = file->private_data; - char buf[8]; - u32 calib_disabled; - int buf_size; - - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &calib_disabled) != 1) - return -EFAULT; - - priv->calib_disabled = calib_disabled; - - return count; -} - -DEBUGFS_READ_FILE_OPS(ucode_rx_stats); -DEBUGFS_READ_FILE_OPS(ucode_tx_stats); -DEBUGFS_READ_FILE_OPS(ucode_general_stats); -DEBUGFS_READ_FILE_OPS(sensitivity); -DEBUGFS_READ_FILE_OPS(chain_noise); -DEBUGFS_READ_FILE_OPS(power_save_status); -DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); -DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); -DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); -DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); -DEBUGFS_READ_WRITE_FILE_OPS(rf_reset); -DEBUGFS_READ_FILE_OPS(rxon_flags); -DEBUGFS_READ_FILE_OPS(rxon_filter_flags); -DEBUGFS_WRITE_FILE_OPS(txfifo_flush); -DEBUGFS_READ_FILE_OPS(ucode_bt_stats); -DEBUGFS_READ_FILE_OPS(bt_traffic); -DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); -DEBUGFS_READ_FILE_OPS(reply_tx_error); -DEBUGFS_WRITE_FILE_OPS(echo_test); -DEBUGFS_READ_WRITE_FILE_OPS(log_event); -DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled); - -/* - * Create the debugfs files and directories - * - */ -int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) -{ - struct dentry *phyd = priv->hw->wiphy->debugfsdir; - struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; - - dir_drv = debugfs_create_dir(name, phyd); - if (!dir_drv) - return -ENOMEM; - - priv->debugfs_dir = dir_drv; - - dir_data = debugfs_create_dir("data", dir_drv); - if (!dir_data) - goto err; - dir_rf = debugfs_create_dir("rf", dir_drv); - if (!dir_rf) - goto err; - dir_debug = debugfs_create_dir("debug", dir_drv); - if (!dir_debug) - goto err; - - DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(rx_handlers, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(sleep_level_override, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); - DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR); - - DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(rf_reset, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(ucode_bt_stats, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(reply_tx_error, dir_debug, S_IRUSR); - DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(echo_test, dir_debug, S_IWUSR); - DEBUGFS_ADD_FILE(log_event, dir_debug, S_IWUSR | S_IRUSR); - - if (iwl_advanced_bt_coexist(priv)) - DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR); - - /* Calibrations disabled/enabled status*/ - DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR); - - if (iwl_trans_dbgfs_register(priv->trans, dir_debug)) - goto err; - return 0; - -err: - IWL_ERR(priv, "Can't create the debugfs directory\n"); - iwl_dbgfs_unregister(priv); - return -ENOMEM; -} - -/** - * Remove the debugfs files and directories - * - */ -void iwl_dbgfs_unregister(struct iwl_priv *priv) -{ - if (!priv->debugfs_dir) - return; - - debugfs_remove_recursive(priv->debugfs_dir); - priv->debugfs_dir = NULL; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h deleted file mode 100644 index a4936a134162..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ /dev/null @@ -1,1072 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -/* - * Please use this file (iwl-dev.h) for driver implementation definitions. - * Please use iwl-commands.h for uCode API definitions. - */ - -#ifndef __iwl_dev_h__ -#define __iwl_dev_h__ - -#include -#include -#include -#include -#include -#include - -#include "iwl-fw.h" -#include "iwl-eeprom.h" -#include "iwl-csr.h" -#include "iwl-debug.h" -#include "iwl-agn-hw.h" -#include "iwl-led.h" -#include "iwl-power.h" -#include "iwl-agn-rs.h" -#include "iwl-agn-tt.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-notif-wait.h" - -/* CT-KILL constants */ -#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ -#define CT_KILL_THRESHOLD 114 /* in Celsius */ -#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ - -/* Default noise level to report when noise measurement is not available. - * This may be because we're: - * 1) Not associated no beacon statistics being sent to driver) - * 2) Scanning (noise measurement does not apply to associated channel) - * Use default noise value of -127 ... this is below the range of measurable - * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user. - * Also, -127 works better than 0 when averaging frames with/without - * noise info (e.g. averaging might be done in app); measured dBm values are - * always negative ... using a negative value as the default keeps all - * averages within an s8's (used in some apps) range of negative values. */ -#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127) - -/* - * RTS threshold here is total size [2347] minus 4 FCS bytes - * Per spec: - * a value of 0 means RTS on all data/management packets - * a value > max MSDU size means no RTS - * else RTS for data/management frames where MPDU is larger - * than RTS value. - */ -#define DEFAULT_RTS_THRESHOLD 2347U -#define MIN_RTS_THRESHOLD 0U -#define MAX_RTS_THRESHOLD 2347U -#define MAX_MSDU_SIZE 2304U -#define MAX_MPDU_SIZE 2346U -#define DEFAULT_BEACON_INTERVAL 200U -#define DEFAULT_SHORT_RETRY_LIMIT 7U -#define DEFAULT_LONG_RETRY_LIMIT 4U - -#define IWL_NUM_SCAN_RATES (2) - -/* - * One for each channel, holds all channel setup data - * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant - * with one another! - */ -struct iwl_channel_info { - struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */ - struct iwl_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for - * HT40 channel */ - - u8 channel; /* channel number */ - u8 flags; /* flags copied from EEPROM */ - s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ - s8 min_power; /* always 0 */ - s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ - - u8 group_index; /* 0-4, maps channel to group1/2/3/4/5 */ - u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */ - enum ieee80211_band band; - - /* HT40 channel info */ - s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - u8 ht40_flags; /* flags copied from EEPROM */ - u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ -}; - -/* - * Minimum number of queues. MAX_NUM is defined in hw specific files. - * Set the minimum to accommodate - * - 4 standard TX queues - * - the command queue - * - 4 PAN TX queues - * - the PAN multicast queue, and - * - the AUX (TX during scan dwell) queue. - */ -#define IWL_MIN_NUM_QUEUES 11 - -/* - * Command queue depends on iPAN support. - */ -#define IWL_DEFAULT_CMD_QUEUE_NUM 4 -#define IWL_IPAN_CMD_QUEUE_NUM 9 - -#define IEEE80211_DATA_LEN 2304 -#define IEEE80211_4ADDR_LEN 30 -#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) -#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) - -#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 -#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 -#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 - -#define IWL_SUPPORTED_RATES_IE_LEN 8 - -#define IWL_INVALID_RATE 0xFF -#define IWL_INVALID_VALUE -1 - -union iwl_ht_rate_supp { - u16 rates; - struct { - u8 siso_rate; - u8 mimo_rate; - }; -}; - -#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) -#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) -#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) -#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) -#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K -#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K -#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K - -/* - * Maximal MPDU density for TX aggregation - * 4 - 2us density - * 5 - 4us density - * 6 - 8us density - * 7 - 16us density - */ -#define CFG_HT_MPDU_DENSITY_2USEC (0x4) -#define CFG_HT_MPDU_DENSITY_4USEC (0x5) -#define CFG_HT_MPDU_DENSITY_8USEC (0x6) -#define CFG_HT_MPDU_DENSITY_16USEC (0x7) -#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC -#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC -#define CFG_HT_MPDU_DENSITY_MIN (0x1) - -struct iwl_ht_config { - bool single_chain_sufficient; - enum ieee80211_smps_mode smps; /* current smps mode */ -}; - -/* QoS structures */ -struct iwl_qos_info { - int qos_active; - struct iwl_qosparam_cmd def_qos_parm; -}; - -/** - * enum iwl_agg_state - * - * The state machine of the BA agreement establishment / tear down. - * These states relate to a specific RA / TID. - * - * @IWL_AGG_OFF: aggregation is not used - * @IWL_AGG_STARTING: aggregation are starting (between start and oper) - * @IWL_AGG_ON: aggregation session is up - * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - */ -enum iwl_agg_state { - IWL_AGG_OFF = 0, - IWL_AGG_STARTING, - IWL_AGG_ON, - IWL_EMPTYING_HW_QUEUE_ADDBA, - IWL_EMPTYING_HW_QUEUE_DELBA, -}; - -/** - * struct iwl_ht_agg - aggregation state machine - - * This structs holds the states for the BA agreement establishment and tear - * down. It also holds the state during the BA session itself. This struct is - * duplicated for each RA / TID. - - * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the - * Tx response (REPLY_TX), and the block ack notification - * (REPLY_COMPRESSED_BA). - * @state: state of the BA agreement establishment / tear down. - * @txq_id: Tx queue used by the BA session - * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or - * the first packet to be sent in legacy HW queue in Tx AGG stop flow. - * Basically when next_reclaimed reaches ssn, we can tell mac80211 that - * we are ready to finish the Tx AGG stop / start flow. - * @wait_for_ba: Expect block-ack before next Tx reply - */ -struct iwl_ht_agg { - u32 rate_n_flags; - enum iwl_agg_state state; - u16 txq_id; - u16 ssn; - bool wait_for_ba; -}; - -/** - * struct iwl_tid_data - one for each RA / TID - - * This structs holds the states for each RA / TID. - - * @seq_number: the next WiFi sequence number to use - * @next_reclaimed: the WiFi sequence number of the next packet to be acked. - * This is basically (last acked packet++). - * @agg: aggregation state machine - */ -struct iwl_tid_data { - u16 seq_number; - u16 next_reclaimed; - struct iwl_ht_agg agg; -}; - -/* - * Structure should be accessed with sta_lock held. When station addition - * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only - * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock - * held. - */ -struct iwl_station_entry { - struct iwl_addsta_cmd sta; - u8 used, ctxid; - struct iwl_link_quality_cmd *lq; -}; - -/* - * iwl_station_priv: Driver's private station information - * - * When mac80211 creates a station it reserves some space (hw->sta_data_size) - * in the structure for use by driver. This structure is places in that - * space. - */ -struct iwl_station_priv { - struct iwl_rxon_context *ctx; - struct iwl_lq_sta lq_sta; - atomic_t pending_frames; - bool client; - bool asleep; - u8 max_agg_bufsize; - u8 sta_id; -}; - -/** - * struct iwl_vif_priv - driver's private per-interface information - * - * When mac80211 allocates a virtual interface, it can allocate - * space for us to put data into. - */ -struct iwl_vif_priv { - struct iwl_rxon_context *ctx; - u8 ibss_bssid_sta_id; -}; - -struct iwl_sensitivity_ranges { - u16 min_nrg_cck; - - u16 nrg_th_cck; - u16 nrg_th_ofdm; - - u16 auto_corr_min_ofdm; - u16 auto_corr_min_ofdm_mrc; - u16 auto_corr_min_ofdm_x1; - u16 auto_corr_min_ofdm_mrc_x1; - - u16 auto_corr_max_ofdm; - u16 auto_corr_max_ofdm_mrc; - u16 auto_corr_max_ofdm_x1; - u16 auto_corr_max_ofdm_mrc_x1; - - u16 auto_corr_max_cck; - u16 auto_corr_max_cck_mrc; - u16 auto_corr_min_cck; - u16 auto_corr_min_cck_mrc; - - u16 barker_corr_th_min; - u16 barker_corr_th_min_mrc; - u16 nrg_th_cca; -}; - - -#define KELVIN_TO_CELSIUS(x) ((x)-273) -#define CELSIUS_TO_KELVIN(x) ((x)+273) - - -/****************************************************************************** - * - * Functions implemented in core module which are forward declared here - * for use by iwl-[4-5].c - * - * NOTE: The implementation of these functions are not hardware specific - * which is why they are in the core module files. - * - * Naming convention -- - * iwl_ <-- Is part of iwlwifi - * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) - * - ****************************************************************************/ -extern void iwl_update_chain_flags(struct iwl_priv *priv); -extern const u8 iwl_bcast_addr[ETH_ALEN]; - -#define IWL_OPERATION_MODE_AUTO 0 -#define IWL_OPERATION_MODE_HT_ONLY 1 -#define IWL_OPERATION_MODE_MIXED 2 -#define IWL_OPERATION_MODE_20MHZ 3 - -#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 - -/* Sensitivity and chain noise calibration */ -#define INITIALIZATION_VALUE 0xFFFF -#define IWL_CAL_NUM_BEACONS 16 -#define MAXIMUM_ALLOWED_PATHLOSS 15 - -#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 - -#define MAX_FA_OFDM 50 -#define MIN_FA_OFDM 5 -#define MAX_FA_CCK 50 -#define MIN_FA_CCK 5 - -#define AUTO_CORR_STEP_OFDM 1 - -#define AUTO_CORR_STEP_CCK 3 -#define AUTO_CORR_MAX_TH_CCK 160 - -#define NRG_DIFF 2 -#define NRG_STEP_CCK 2 -#define NRG_MARGIN 8 -#define MAX_NUMBER_CCK_NO_FA 100 - -#define AUTO_CORR_CCK_MIN_VAL_DEF (125) - -#define CHAIN_A 0 -#define CHAIN_B 1 -#define CHAIN_C 2 -#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 -#define ALL_BAND_FILTER 0xFF00 -#define IN_BAND_FILTER 0xFF -#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF - -#define NRG_NUM_PREV_STAT_L 20 -#define NUM_RX_CHAINS 3 - -enum iwlagn_false_alarm_state { - IWL_FA_TOO_MANY = 0, - IWL_FA_TOO_FEW = 1, - IWL_FA_GOOD_RANGE = 2, -}; - -enum iwlagn_chain_noise_state { - IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ - IWL_CHAIN_NOISE_ACCUMULATE, - IWL_CHAIN_NOISE_CALIBRATED, - IWL_CHAIN_NOISE_DONE, -}; - -/* Sensitivity calib data */ -struct iwl_sensitivity_data { - u32 auto_corr_ofdm; - u32 auto_corr_ofdm_mrc; - u32 auto_corr_ofdm_x1; - u32 auto_corr_ofdm_mrc_x1; - u32 auto_corr_cck; - u32 auto_corr_cck_mrc; - - u32 last_bad_plcp_cnt_ofdm; - u32 last_fa_cnt_ofdm; - u32 last_bad_plcp_cnt_cck; - u32 last_fa_cnt_cck; - - u32 nrg_curr_state; - u32 nrg_prev_state; - u32 nrg_value[10]; - u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; - u32 nrg_silence_ref; - u32 nrg_energy_idx; - u32 nrg_silence_idx; - u32 nrg_th_cck; - s32 nrg_auto_corr_silence_diff; - u32 num_in_cck_no_fa; - u32 nrg_th_ofdm; - - u16 barker_corr_th_min; - u16 barker_corr_th_min_mrc; - u16 nrg_th_cca; -}; - -/* Chain noise (differential Rx gain) calib data */ -struct iwl_chain_noise_data { - u32 active_chains; - u32 chain_noise_a; - u32 chain_noise_b; - u32 chain_noise_c; - u32 chain_signal_a; - u32 chain_signal_b; - u32 chain_signal_c; - u16 beacon_count; - u8 disconn_array[NUM_RX_CHAINS]; - u8 delta_gain_code[NUM_RX_CHAINS]; - u8 radio_write; - u8 state; -}; - -enum { - MEASUREMENT_READY = (1 << 0), - MEASUREMENT_ACTIVE = (1 << 1), -}; - -enum iwl_nvm_type { - NVM_DEVICE_TYPE_EEPROM = 0, - NVM_DEVICE_TYPE_OTP, -}; - -/* - * Two types of OTP memory access modes - * IWL_OTP_ACCESS_ABSOLUTE - absolute address mode, - * based on physical memory addressing - * IWL_OTP_ACCESS_RELATIVE - relative address mode, - * based on logical memory addressing - */ -enum iwl_access_mode { - IWL_OTP_ACCESS_ABSOLUTE, - IWL_OTP_ACCESS_RELATIVE, -}; - -/* reply_tx_statistics (for _agn devices) */ -struct reply_tx_error_statistics { - u32 pp_delay; - u32 pp_few_bytes; - u32 pp_bt_prio; - u32 pp_quiet_period; - u32 pp_calc_ttak; - u32 int_crossed_retry; - u32 short_limit; - u32 long_limit; - u32 fifo_underrun; - u32 drain_flow; - u32 rfkill_flush; - u32 life_expire; - u32 dest_ps; - u32 host_abort; - u32 bt_retry; - u32 sta_invalid; - u32 frag_drop; - u32 tid_disable; - u32 fifo_flush; - u32 insuff_cf_poll; - u32 fail_hw_drop; - u32 sta_color_mismatch; - u32 unknown; -}; - -/* reply_agg_tx_statistics (for _agn devices) */ -struct reply_agg_tx_error_statistics { - u32 underrun; - u32 bt_prio; - u32 few_bytes; - u32 abort; - u32 last_sent_ttl; - u32 last_sent_try; - u32 last_sent_bt_kill; - u32 scd_query; - u32 bad_crc32; - u32 response; - u32 dump_tx; - u32 delay_tx; - u32 unknown; -}; - -/* - * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds - * to perform continuous uCode event logging operation if enabled - */ -#define UCODE_TRACE_PERIOD (10) - -/* - * iwl_event_log: current uCode event log position - * - * @ucode_trace: enable/disable ucode continuous trace timer - * @num_wraps: how many times the event buffer wraps - * @next_entry: the entry just before the next one that uCode would fill - * @non_wraps_count: counter for no wrap detected when dump ucode events - * @wraps_once_count: counter for wrap once detected when dump ucode events - * @wraps_more_count: counter for wrap more than once detected - * when dump ucode events - */ -struct iwl_event_log { - bool ucode_trace; - u32 num_wraps; - u32 next_entry; - int non_wraps_count; - int wraps_once_count; - int wraps_more_count; -}; - -#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) - -/* BT Antenna Coupling Threshold (dB) */ -#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) - -/* Firmware reload counter and Timestamp */ -#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */ -#define IWL_MAX_CONTINUE_RELOAD_CNT 4 - - -struct iwl_rf_reset { - int reset_request_count; - int reset_success_count; - int reset_reject_count; - unsigned long last_reset_jiffies; -}; - -enum iwl_rxon_context_id { - IWL_RXON_CTX_BSS, - IWL_RXON_CTX_PAN, - - NUM_IWL_RXON_CTX -}; - -/* extend beacon time format bit shifting */ -/* - * for _agn devices - * bits 31:22 - extended - * bits 21:0 - interval - */ -#define IWLAGN_EXT_BEACON_TIME_POS 22 - -struct iwl_rxon_context { - struct ieee80211_vif *vif; - - u8 mcast_queue; - u8 ac_to_queue[IEEE80211_NUM_ACS]; - u8 ac_to_fifo[IEEE80211_NUM_ACS]; - - /* - * We could use the vif to indicate active, but we - * also need it to be active during disabling when - * we already removed the vif for type setting. - */ - bool always_active, is_active; - - bool ht_need_multiple_chains; - - enum iwl_rxon_context_id ctxid; - - u32 interface_modes, exclusive_interface_modes; - u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype; - - /* - * We declare this const so it can only be - * changed via explicit cast within the - * routines that actually update the physical - * hardware. - */ - const struct iwl_rxon_cmd active; - struct iwl_rxon_cmd staging; - - struct iwl_rxon_time_cmd timing; - - struct iwl_qos_info qos_data; - - u8 bcast_sta_id, ap_sta_id; - - u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd; - u8 qos_cmd; - u8 wep_key_cmd; - - struct iwl_wep_key wep_keys[WEP_KEYS_MAX]; - u8 key_mapping_keys; - - __le32 station_flags; - - int beacon_int; - - struct { - bool non_gf_sta_present; - u8 protection; - bool enabled, is_40mhz; - u8 extension_chan_offset; - } ht; -}; - -enum iwl_scan_type { - IWL_SCAN_NORMAL, - IWL_SCAN_RADIO_RESET, - IWL_SCAN_ROC, -}; - -/** - * struct iwl_hw_params - * - * Holds the module parameters - * - * @tx_chains_num: Number of TX chains - * @rx_chains_num: Number of RX chains - * @valid_tx_ant: usable antennas for TX - * @valid_rx_ant: usable antennas for RX - * @ht40_channel: is 40MHz width possible: BIT(IEEE80211_BAND_XXX) - * @sku: sku read from EEPROM - * @ct_kill_threshold: temperature threshold - in hw dependent unit - * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit - * relevant for 1000, 6000 and up - * @struct iwl_sensitivity_ranges: range of sensitivity values - * @use_rts_for_aggregation: use rts/cts protection for HT traffic - */ -struct iwl_hw_params { - u8 tx_chains_num; - u8 rx_chains_num; - u8 valid_tx_ant; - u8 valid_rx_ant; - u8 ht40_channel; - bool use_rts_for_aggregation; - u16 sku; - u32 ct_kill_threshold; - u32 ct_kill_exit_threshold; - - const struct iwl_sensitivity_ranges *sens; -}; - -struct iwl_lib_ops { - /* set hw dependent parameters */ - void (*set_hw_params)(struct iwl_priv *priv); - int (*set_channel_switch)(struct iwl_priv *priv, - struct ieee80211_channel_switch *ch_switch); - /* device specific configuration */ - void (*nic_config)(struct iwl_priv *priv); - - /* eeprom operations (as defined in iwl-eeprom.h) */ - struct iwl_eeprom_ops eeprom_ops; - - /* temperature */ - void (*temperature)(struct iwl_priv *priv); -}; - -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE -struct iwl_testmode_trace { - u32 buff_size; - u32 total_size; - u32 num_chunks; - u8 *cpu_addr; - u8 *trace_addr; - dma_addr_t dma_addr; - bool trace_enabled; -}; -struct iwl_testmode_mem { - u32 buff_size; - u32 num_chunks; - u8 *buff_addr; - bool read_in_progress; -}; -#endif - -struct iwl_wipan_noa_data { - struct rcu_head rcu_head; - u32 length; - u8 data[]; -}; - -/* Calibration disabling bit mask */ -enum { - IWL_CALIB_ENABLE_ALL = 0, - - IWL_SENSITIVITY_CALIB_DISABLED = BIT(0), - IWL_CHAIN_NOISE_CALIB_DISABLED = BIT(1), - IWL_TX_POWER_CALIB_DISABLED = BIT(2), - - IWL_CALIB_DISABLE_ALL = 0xFFFFFFFF, -}; - -#define IWL_OP_MODE_GET_DVM(_iwl_op_mode) \ - ((struct iwl_priv *) ((_iwl_op_mode)->op_mode_specific)) - -#define IWL_MAC80211_GET_DVM(_hw) \ - ((struct iwl_priv *) ((struct iwl_op_mode *) \ - (_hw)->priv)->op_mode_specific) - -struct iwl_priv { - - struct iwl_trans *trans; - struct device *dev; /* for debug prints only */ - const struct iwl_cfg *cfg; - const struct iwl_fw *fw; - const struct iwl_lib_ops *lib; - unsigned long status; - - spinlock_t sta_lock; - struct mutex mutex; - - unsigned long transport_queue_stop; - bool passive_no_rx; -#define IWL_INVALID_MAC80211_QUEUE 0xff - u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; - atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; - - unsigned long agg_q_alloc[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; - - /* ieee device used by generic ieee processing code */ - struct ieee80211_hw *hw; - struct ieee80211_channel *ieee_channels; - struct ieee80211_rate *ieee_rates; - - struct list_head calib_results; - - struct workqueue_struct *workqueue; - - struct iwl_hw_params hw_params; - - enum ieee80211_band band; - u8 valid_contexts; - - void (*pre_rx_handler)(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb); - int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd); - - struct iwl_notif_wait_data notif_wait; - - struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; - - /* spectrum measurement report caching */ - struct iwl_spectrum_notification measure_report; - u8 measurement_status; - -#define IWL_OWNERSHIP_DRIVER 0 -#define IWL_OWNERSHIP_TM 1 - u8 ucode_owner; - - /* ucode beacon time */ - u32 ucode_beacon_time; - int missed_beacon_threshold; - - /* track IBSS manager (last beacon) status */ - u32 ibss_manager; - - /* jiffies when last recovery from statistics was performed */ - unsigned long rx_statistics_jiffies; - - /*counters */ - u32 rx_handlers_stats[REPLY_MAX]; - - /* rf reset */ - struct iwl_rf_reset rf_reset; - - /* firmware reload counter and timestamp */ - unsigned long reload_jiffies; - int reload_count; - bool ucode_loaded; - bool init_ucode_run; /* Don't run init uCode again */ - - /* we allocate array of iwl_channel_info for NIC's valid channels. - * Access via channel # using indirect index array */ - struct iwl_channel_info *channel_info; /* channel info array */ - u8 channel_count; /* # of channels */ - - u8 plcp_delta_threshold; - - /* thermal calibration */ - s32 temperature; /* Celsius */ - s32 last_temperature; - - struct iwl_wipan_noa_data __rcu *noa_data; - - /* Scan related variables */ - unsigned long scan_start; - unsigned long scan_start_tsf; - void *scan_cmd; - enum ieee80211_band scan_band; - struct cfg80211_scan_request *scan_request; - struct ieee80211_vif *scan_vif; - enum iwl_scan_type scan_type; - u8 scan_tx_ant[IEEE80211_NUM_BANDS]; - u8 mgmt_tx_ant; - - /* max number of station keys */ - u8 sta_key_max_num; - - bool new_scan_threshold_behaviour; - - bool wowlan; - - /* EEPROM MAC addresses */ - struct mac_address addresses[2]; - - struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; - - __le16 switch_channel; - - u8 start_calib; - struct iwl_sensitivity_data sensitivity_data; - struct iwl_chain_noise_data chain_noise_data; - __le16 sensitivity_tbl[HD_TABLE_SIZE]; - __le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES]; - - struct iwl_ht_config current_ht_config; - - /* Rate scaling data */ - u8 retry_rate; - - int activity_timer_active; - - struct iwl_power_mgr power_data; - struct iwl_tt_mgmt thermal_throttle; - - /* station table variables */ - int num_stations; - struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; - unsigned long ucode_key_table; - struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; - atomic_t num_aux_in_flight; - - u8 mac80211_registered; - - /* Indication if ieee80211_ops->open has been called */ - u8 is_open; - - enum nl80211_iftype iw_mode; - - /* Last Rx'd beacon timestamp */ - u64 timestamp; - - struct { - __le32 flag; - struct statistics_general_common common; - struct statistics_rx_non_phy rx_non_phy; - struct statistics_rx_phy rx_ofdm; - struct statistics_rx_ht_phy rx_ofdm_ht; - struct statistics_rx_phy rx_cck; - struct statistics_tx tx; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct statistics_bt_activity bt_activity; - __le32 num_bt_kills, accum_num_bt_kills; -#endif - spinlock_t lock; - } statistics; -#ifdef CONFIG_IWLWIFI_DEBUGFS - struct { - struct statistics_general_common common; - struct statistics_rx_non_phy rx_non_phy; - struct statistics_rx_phy rx_ofdm; - struct statistics_rx_ht_phy rx_ofdm_ht; - struct statistics_rx_phy rx_cck; - struct statistics_tx tx; - struct statistics_bt_activity bt_activity; - } accum_stats, delta_stats, max_delta_stats; -#endif - - /* - * reporting the number of tids has AGG on. 0 means - * no AGGREGATION - */ - u8 agg_tids_count; - - struct iwl_rx_phy_res last_phy_res; - bool last_phy_res_valid; - - /* - * chain noise reset and gain commands are the - * two extra calibration commands follows the standard - * phy calibration commands - */ - u8 phy_calib_chain_noise_reset_cmd; - u8 phy_calib_chain_noise_gain_cmd; - - /* counts reply_tx error */ - struct reply_tx_error_statistics reply_tx_stats; - struct reply_agg_tx_error_statistics reply_agg_tx_stats; - - /* remain-on-channel offload support */ - struct ieee80211_channel *hw_roc_channel; - struct delayed_work hw_roc_disable_work; - enum nl80211_channel_type hw_roc_chantype; - int hw_roc_duration; - bool hw_roc_setup, hw_roc_start_notified; - - /* bt coex */ - u8 bt_enable_flag; - u8 bt_status; - u8 bt_traffic_load, last_bt_traffic_load; - bool bt_ch_announce; - bool bt_full_concurrent; - bool bt_ant_couple_ok; - __le32 kill_ack_mask; - __le32 kill_cts_mask; - __le16 bt_valid; - bool reduced_txpower; - u16 bt_on_thresh; - u16 bt_duration; - u16 dynamic_frag_thresh; - u8 bt_ci_compliance; - struct work_struct bt_traffic_change_work; - bool bt_enable_pspoll; - struct iwl_rxon_context *cur_rssi_ctx; - bool bt_is_sco; - - struct work_struct restart; - struct work_struct scan_completed; - struct work_struct abort_scan; - - struct work_struct beacon_update; - struct iwl_rxon_context *beacon_ctx; - struct sk_buff *beacon_skb; - void *beacon_cmd; - - struct work_struct tt_work; - struct work_struct ct_enter; - struct work_struct ct_exit; - struct work_struct start_internal_scan; - struct work_struct tx_flush; - struct work_struct bt_full_concurrency; - struct work_struct bt_runtime_config; - - struct delayed_work scan_check; - - /* TX Power */ - s8 tx_power_user_lmt; - s8 tx_power_device_lmt; - s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */ - s8 tx_power_next; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* debugfs */ - struct dentry *debugfs_dir; - u32 dbgfs_sram_offset, dbgfs_sram_len; - bool disable_ht40; - void *wowlan_sram; -#endif /* CONFIG_IWLWIFI_DEBUGFS */ - - /* eeprom -- this is in the card's little endian byte order */ - u8 *eeprom; - enum iwl_nvm_type nvm_device_type; - - struct work_struct txpower_work; - u32 calib_disabled; - struct work_struct run_time_calib_work; - struct timer_list statistics_periodic; - struct timer_list ucode_trace; - - struct iwl_event_log event_log; - - struct led_classdev led; - unsigned long blink_on, blink_off; - bool led_registered; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - struct iwl_testmode_trace testmode_trace; - struct iwl_testmode_mem testmode_mem; - u32 tm_fixed_rate; -#endif - - /* WoWLAN GTK rekey data */ - u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; - __le64 replay_ctr; - __le16 last_seq_ctl; - bool have_rekey_data; - - /* device_pointers: pointers to ucode event tables */ - struct { - u32 error_event_table; - u32 log_event_table; - } device_pointers; - - /* indicator of loaded ucode image */ - enum iwl_ucode_type cur_ucode; -}; /*iwl_priv */ - -extern struct kmem_cache *iwl_tx_cmd_pool; - -static inline struct iwl_rxon_context * -iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) -{ - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - - return vif_priv->ctx; -} - -#define for_each_context(priv, ctx) \ - for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \ - ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ - if (priv->valid_contexts & BIT(ctx->ctxid)) - -static inline int iwl_is_associated_ctx(struct iwl_rxon_context *ctx) -{ - return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; -} - -static inline int iwl_is_associated(struct iwl_priv *priv, - enum iwl_rxon_context_id ctxid) -{ - return iwl_is_associated_ctx(&priv->contexts[ctxid]); -} - -static inline int iwl_is_any_associated(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - for_each_context(priv, ctx) - if (iwl_is_associated_ctx(ctx)) - return true; - return false; -} - -static inline int is_channel_valid(const struct iwl_channel_info *ch_info) -{ - if (ch_info == NULL) - return 0; - return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; -} - -static inline int is_channel_radar(const struct iwl_channel_info *ch_info) -{ - return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; -} - -static inline u8 is_channel_a_band(const struct iwl_channel_info *ch_info) -{ - return ch_info->band == IEEE80211_BAND_5GHZ; -} - -static inline u8 is_channel_bg_band(const struct iwl_channel_info *ch_info) -{ - return ch_info->band == IEEE80211_BAND_2GHZ; -} - -static inline int is_channel_passive(const struct iwl_channel_info *ch) -{ - return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; -} - -static inline int is_channel_ibss(const struct iwl_channel_info *ch) -{ - return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0; -} - -#endif /* __iwl_dev_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 06203d6a1d86..65364793021f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -28,6 +28,7 @@ #define __IWLWIFI_DEVICE_TRACE #include +#include #if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c deleted file mode 100644 index 50c58911e718..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ /dev/null @@ -1,1148 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - - -#include -#include -#include -#include - -#include - -#include "iwl-dev.h" -#include "iwl-debug.h" -#include "iwl-agn.h" -#include "iwl-eeprom.h" -#include "iwl-io.h" -#include "iwl-prph.h" - -/************************** EEPROM BANDS **************************** - * - * The iwl_eeprom_band definitions below provide the mapping from the - * EEPROM contents to the specific channel number supported for each - * band. - * - * For example, iwl_priv->eeprom.band_3_channels[4] from the band_3 - * definition below maps to physical channel 42 in the 5.2GHz spectrum. - * The specific geography and calibration information for that channel - * is contained in the eeprom map itself. - * - * During init, we copy the eeprom information and channel map - * information into priv->channel_info_24/52 and priv->channel_map_24/52 - * - * channel_map_24/52 provides the index in the channel_info array for a - * given channel. We have to have two separate maps as there is channel - * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and - * band_2 - * - * A value of 0xff stored in the channel_map indicates that the channel - * is not supported by the hardware at all. - * - * A value of 0xfe in the channel_map indicates that the channel is not - * valid for Tx with the current hardware. This means that - * while the system can tune and receive on a given channel, it may not - * be able to associate or transmit any frames on that - * channel. There is no corresponding channel information for that - * entry. - * - *********************************************************************/ - -/* 2.4 GHz */ -const u8 iwl_eeprom_band_1[14] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 -}; - -/* 5.2 GHz bands */ -static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ - 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 -}; - -static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ - 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 -}; - -static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ - 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 -}; - -static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ - 145, 149, 153, 157, 161, 165 -}; - -static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ - 1, 2, 3, 4, 5, 6, 7 -}; - -static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ - 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 -}; - -/****************************************************************************** - * - * generic NVM functions - * -******************************************************************************/ - -/* - * The device's EEPROM semaphore prevents conflicts between driver and uCode - * when accessing the EEPROM; each access is a series of pulses to/from the - * EEPROM chip, not a single event, so even reads could conflict if they - * weren't arbitrated by the semaphore. - */ - -#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ -#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - -static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) -{ - u16 count; - int ret; - - for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { - /* Request semaphore */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - - /* See if we got it */ - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - EEPROM_SEM_TIMEOUT); - if (ret >= 0) { - IWL_DEBUG_EEPROM(trans, - "Acquired semaphore after %d tries.\n", - count+1); - return ret; - } - } - - return ret; -} - -static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) -{ - iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - -} - -static int iwl_eeprom_verify_signature(struct iwl_priv *priv) -{ - u32 gp = iwl_read32(priv->trans, CSR_EEPROM_GP) & - CSR_EEPROM_GP_VALID_MSK; - int ret = 0; - - IWL_DEBUG_EEPROM(priv, "EEPROM signature=0x%08x\n", gp); - switch (gp) { - case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: - if (priv->nvm_device_type != NVM_DEVICE_TYPE_OTP) { - IWL_ERR(priv, "EEPROM with bad signature: 0x%08x\n", - gp); - ret = -ENOENT; - } - break; - case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: - case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: - if (priv->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) { - IWL_ERR(priv, "OTP with bad signature: 0x%08x\n", gp); - ret = -ENOENT; - } - break; - case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: - default: - IWL_ERR(priv, "bad EEPROM/OTP signature, type=%s, " - "EEPROM_GP=0x%08x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", gp); - ret = -ENOENT; - break; - } - return ret; -} - -u16 iwl_eeprom_query16(struct iwl_priv *priv, size_t offset) -{ - if (!priv->eeprom) - return 0; - return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); -} - -int iwl_eeprom_check_version(struct iwl_priv *priv) -{ - u16 eeprom_ver; - u16 calib_ver; - - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - calib_ver = iwl_eeprom_calib_version(priv); - - if (eeprom_ver < priv->cfg->eeprom_ver || - calib_ver < priv->cfg->eeprom_calib_ver) - goto err; - - IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", - eeprom_ver, calib_ver); - - return 0; -err: - IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " - "CALIB=0x%x < 0x%x\n", - eeprom_ver, priv->cfg->eeprom_ver, - calib_ver, priv->cfg->eeprom_calib_ver); - return -EINVAL; - -} - -int iwl_eeprom_init_hw_params(struct iwl_priv *priv) -{ - u16 radio_cfg; - - priv->hw_params.sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP); - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE && - !priv->cfg->ht_params) { - IWL_ERR(priv, "Invalid 11n configuration\n"); - return -EINVAL; - } - - if (!priv->hw_params.sku) { - IWL_ERR(priv, "Invalid device sku\n"); - return -EINVAL; - } - - IWL_INFO(priv, "Device SKU: 0x%X\n", priv->hw_params.sku); - - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); - - priv->hw_params.valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); - priv->hw_params.valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); - - /* check overrides (some devices have wrong EEPROM) */ - if (priv->cfg->valid_tx_ant) - priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; - if (priv->cfg->valid_rx_ant) - priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; - - if (!priv->hw_params.valid_tx_ant || !priv->hw_params.valid_rx_ant) { - IWL_ERR(priv, "Invalid chain (0x%X, 0x%X)\n", - priv->hw_params.valid_tx_ant, - priv->hw_params.valid_rx_ant); - return -EINVAL; - } - - IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", - priv->hw_params.valid_tx_ant, priv->hw_params.valid_rx_ant); - - return 0; -} - -u16 iwl_eeprom_calib_version(struct iwl_priv *priv) -{ - struct iwl_eeprom_calib_hdr *hdr; - - hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, - EEPROM_CALIB_ALL); - return hdr->version; -} - -static u32 eeprom_indirect_address(struct iwl_priv *priv, u32 address) -{ - u16 offset = 0; - - if ((address & INDIRECT_ADDRESS) == 0) - return address; - - switch (address & INDIRECT_TYPE_MSK) { - case INDIRECT_HOST: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_HOST); - break; - case INDIRECT_GENERAL: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_GENERAL); - break; - case INDIRECT_REGULATORY: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_REGULATORY); - break; - case INDIRECT_TXP_LIMIT: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT); - break; - case INDIRECT_TXP_LIMIT_SIZE: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT_SIZE); - break; - case INDIRECT_CALIBRATION: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_CALIBRATION); - break; - case INDIRECT_PROCESS_ADJST: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_PROCESS_ADJST); - break; - case INDIRECT_OTHERS: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_OTHERS); - break; - default: - IWL_ERR(priv, "illegal indirect type: 0x%X\n", - address & INDIRECT_TYPE_MSK); - break; - } - - /* translate the offset from words to byte */ - return (address & ADDRESS_MSK) + (offset << 1); -} - -const u8 *iwl_eeprom_query_addr(struct iwl_priv *priv, size_t offset) -{ - u32 address = eeprom_indirect_address(priv, offset); - BUG_ON(address >= priv->cfg->base_params->eeprom_size); - return &priv->eeprom[address]; -} - -void iwl_eeprom_get_mac(struct iwl_priv *priv, u8 *mac) -{ - const u8 *addr = iwl_eeprom_query_addr(priv, - EEPROM_MAC_ADDRESS); - memcpy(mac, addr, ETH_ALEN); -} - -/****************************************************************************** - * - * OTP related functions - * -******************************************************************************/ - -static void iwl_set_otp_access(struct iwl_trans *trans, - enum iwl_access_mode mode) -{ - iwl_read32(trans, CSR_OTP_GP_REG); - - if (mode == IWL_OTP_ACCESS_ABSOLUTE) - iwl_clear_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_OTP_ACCESS_MODE); - else - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_OTP_ACCESS_MODE); -} - -static int iwl_get_nvm_type(struct iwl_trans *trans, u32 hw_rev) -{ - u32 otpgp; - int nvm_type; - - /* OTP only valid for CP/PP and after */ - switch (hw_rev & CSR_HW_REV_TYPE_MSK) { - case CSR_HW_REV_TYPE_NONE: - IWL_ERR(trans, "Unknown hardware type\n"); - return -ENOENT; - case CSR_HW_REV_TYPE_5300: - case CSR_HW_REV_TYPE_5350: - case CSR_HW_REV_TYPE_5100: - case CSR_HW_REV_TYPE_5150: - nvm_type = NVM_DEVICE_TYPE_EEPROM; - break; - default: - otpgp = iwl_read32(trans, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) - nvm_type = NVM_DEVICE_TYPE_OTP; - else - nvm_type = NVM_DEVICE_TYPE_EEPROM; - break; - } - return nvm_type; -} - -static int iwl_init_otp_access(struct iwl_trans *trans) -{ - int ret; - - /* Enable 40MHz radio clock */ - iwl_write32(trans, CSR_GP_CNTRL, - iwl_read32(trans, CSR_GP_CNTRL) | - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* wait for clock to be ready */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - 25000); - if (ret < 0) - IWL_ERR(trans, "Time out access OTP\n"); - else { - iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - udelay(5); - iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - - /* - * CSR auto clock gate disable bit - - * this is only applicable for HW with OTP shadow RAM - */ - if (trans->cfg->base_params->shadow_ram_support) - iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - } - return ret; -} - -static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, - __le16 *eeprom_data) -{ - int ret = 0; - u32 r; - u32 otpgp; - - iwl_write32(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - ret = iwl_poll_bit(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); - return ret; - } - r = iwl_read32(trans, CSR_EEPROM_REG); - /* check for ECC errors: */ - otpgp = iwl_read32(trans, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { - /* stop in this case */ - /* set the uncorrectable OTP ECC bit for acknowledgement */ - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); - return -EINVAL; - } - if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { - /* continue in this case */ - /* set the correctable OTP ECC bit for acknowledgement */ - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); - IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); - } - *eeprom_data = cpu_to_le16(r >> 16); - return 0; -} - -/* - * iwl_is_otp_empty: check for empty OTP - */ -static bool iwl_is_otp_empty(struct iwl_trans *trans) -{ - u16 next_link_addr = 0; - __le16 link_value; - bool is_empty = false; - - /* locate the beginning of OTP link list */ - if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { - if (!link_value) { - IWL_ERR(trans, "OTP is empty\n"); - is_empty = true; - } - } else { - IWL_ERR(trans, "Unable to read first block of OTP list.\n"); - is_empty = true; - } - - return is_empty; -} - - -/* - * iwl_find_otp_image: find EEPROM image in OTP - * finding the OTP block that contains the EEPROM image. - * the last valid block on the link list (the block _before_ the last block) - * is the block we should read and used to configure the device. - * If all the available OTP blocks are full, the last block will be the block - * we should read and used to configure the device. - * only perform this operation if shadow RAM is disabled - */ -static int iwl_find_otp_image(struct iwl_trans *trans, - u16 *validblockaddr) -{ - u16 next_link_addr = 0, valid_addr; - __le16 link_value = 0; - int usedblocks = 0; - - /* set addressing mode to absolute to traverse the link list */ - iwl_set_otp_access(trans, IWL_OTP_ACCESS_ABSOLUTE); - - /* checking for empty OTP or error */ - if (iwl_is_otp_empty(trans)) - return -EINVAL; - - /* - * start traverse link list - * until reach the max number of OTP blocks - * different devices have different number of OTP blocks - */ - do { - /* save current valid block address - * check for more block on the link list - */ - valid_addr = next_link_addr; - next_link_addr = le16_to_cpu(link_value) * sizeof(u16); - IWL_DEBUG_EEPROM(trans, "OTP blocks %d addr 0x%x\n", - usedblocks, next_link_addr); - if (iwl_read_otp_word(trans, next_link_addr, &link_value)) - return -EINVAL; - if (!link_value) { - /* - * reach the end of link list, return success and - * set address point to the starting address - * of the image - */ - *validblockaddr = valid_addr; - /* skip first 2 bytes (link list pointer) */ - *validblockaddr += 2; - return 0; - } - /* more in the link list, continue */ - usedblocks++; - } while (usedblocks <= trans->cfg->base_params->max_ll_items); - - /* OTP has no valid blocks */ - IWL_DEBUG_EEPROM(trans, "OTP has no valid blocks\n"); - return -EINVAL; -} - -/****************************************************************************** - * - * Tx Power related functions - * -******************************************************************************/ -/** - * iwl_get_max_txpower_avg - get the highest tx power from all chains. - * find the highest tx power from all chains for the channel - */ -static s8 iwl_get_max_txpower_avg(const struct iwl_cfg *cfg, - struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, - int element, s8 *max_txpower_in_half_dbm) -{ - s8 max_txpower_avg = 0; /* (dBm) */ - - /* Take the highest tx power from any valid chains */ - if ((cfg->valid_tx_ant & ANT_A) && - (enhanced_txpower[element].chain_a_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_a_max; - if ((cfg->valid_tx_ant & ANT_B) && - (enhanced_txpower[element].chain_b_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_b_max; - if ((cfg->valid_tx_ant & ANT_C) && - (enhanced_txpower[element].chain_c_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_c_max; - if (((cfg->valid_tx_ant == ANT_AB) | - (cfg->valid_tx_ant == ANT_BC) | - (cfg->valid_tx_ant == ANT_AC)) && - (enhanced_txpower[element].mimo2_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].mimo2_max; - if ((cfg->valid_tx_ant == ANT_ABC) && - (enhanced_txpower[element].mimo3_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].mimo3_max; - - /* - * max. tx power in EEPROM is in 1/2 dBm format - * convert from 1/2 dBm to dBm (round-up convert) - * but we also do not want to loss 1/2 dBm resolution which - * will impact performance - */ - *max_txpower_in_half_dbm = max_txpower_avg; - return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); -} - -static void -iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv, - struct iwl_eeprom_enhanced_txpwr *txp, - s8 max_txpower_avg) -{ - int ch_idx; - bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ; - enum ieee80211_band band; - - band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - - for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) { - struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx]; - - /* update matching channel or from common data only */ - if (txp->channel != 0 && ch_info->channel != txp->channel) - continue; - - /* update matching band only */ - if (band != ch_info->band) - continue; - - if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) { - ch_info->max_power_avg = max_txpower_avg; - ch_info->curr_txpow = max_txpower_avg; - ch_info->scan_power = max_txpower_avg; - } - - if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg) - ch_info->ht40_max_power_avg = max_txpower_avg; - } -} - -#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) -#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) -#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) - -#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \ - ? # x " " : "") - -static void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) -{ - struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; - int idx, entries; - __le16 *txp_len; - s8 max_txp_avg, max_txp_avg_halfdbm; - - BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); - - /* the length is in 16-bit words, but we want entries */ - txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS); - entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; - - txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS); - - for (idx = 0; idx < entries; idx++) { - txp = &txp_array[idx]; - /* skip invalid entries */ - if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) - continue; - - IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", - (txp->channel && (txp->flags & - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? - "Common " : (txp->channel) ? - "Channel" : "Common", - (txp->channel), - TXP_CHECK_AND_PRINT(VALID), - TXP_CHECK_AND_PRINT(BAND_52G), - TXP_CHECK_AND_PRINT(OFDM), - TXP_CHECK_AND_PRINT(40MHZ), - TXP_CHECK_AND_PRINT(HT_AP), - TXP_CHECK_AND_PRINT(RES1), - TXP_CHECK_AND_PRINT(RES2), - TXP_CHECK_AND_PRINT(COMMON_TYPE), - txp->flags); - IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x " - "chain_B: 0X%02x chain_C: 0X%02x\n", - txp->chain_a_max, txp->chain_b_max, - txp->chain_c_max); - IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x " - "MIMO3: 0x%02x High 20_on_40: 0x%02x " - "Low 20_on_40: 0x%02x\n", - txp->mimo2_max, txp->mimo3_max, - ((txp->delta_20_in_40 & 0xf0) >> 4), - (txp->delta_20_in_40 & 0x0f)); - - max_txp_avg = iwl_get_max_txpower_avg(priv->cfg, txp_array, idx, - &max_txp_avg_halfdbm); - - /* - * Update the user limit values values to the highest - * power supported by any channel - */ - if (max_txp_avg > priv->tx_power_user_lmt) - priv->tx_power_user_lmt = max_txp_avg; - if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm) - priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm; - - iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg); - } -} - -/** - * iwl_eeprom_init - read EEPROM contents - * - * Load the EEPROM contents from adapter into priv->eeprom - * - * NOTE: This routine uses the non-debug IO access functions. - */ -int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) -{ - __le16 *e; - u32 gp = iwl_read32(priv->trans, CSR_EEPROM_GP); - int sz; - int ret; - u16 addr; - u16 validblockaddr = 0; - u16 cache_addr = 0; - - priv->nvm_device_type = iwl_get_nvm_type(priv->trans, hw_rev); - if (priv->nvm_device_type == -ENOENT) - return -ENOENT; - /* allocate eeprom */ - sz = priv->cfg->base_params->eeprom_size; - IWL_DEBUG_EEPROM(priv, "NVM size = %d\n", sz); - priv->eeprom = kzalloc(sz, GFP_KERNEL); - if (!priv->eeprom) { - ret = -ENOMEM; - goto alloc_err; - } - e = (__le16 *)priv->eeprom; - - ret = iwl_eeprom_verify_signature(priv); - if (ret < 0) { - IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); - ret = -ENOENT; - goto err; - } - - /* Make sure driver (instead of uCode) is allowed to read EEPROM */ - ret = iwl_eeprom_acquire_semaphore(priv->trans); - if (ret < 0) { - IWL_ERR(priv, "Failed to acquire EEPROM semaphore.\n"); - ret = -ENOENT; - goto err; - } - - if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { - - ret = iwl_init_otp_access(priv->trans); - if (ret) { - IWL_ERR(priv, "Failed to initialize OTP access.\n"); - ret = -ENOENT; - goto done; - } - iwl_write32(priv->trans, CSR_EEPROM_GP, - iwl_read32(priv->trans, CSR_EEPROM_GP) & - ~CSR_EEPROM_GP_IF_OWNER_MSK); - - iwl_set_bit(priv->trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | - CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - /* traversing the linked list if no shadow ram supported */ - if (!priv->cfg->base_params->shadow_ram_support) { - if (iwl_find_otp_image(priv->trans, &validblockaddr)) { - ret = -ENOENT; - goto done; - } - } - for (addr = validblockaddr; addr < validblockaddr + sz; - addr += sizeof(u16)) { - __le16 eeprom_data; - - ret = iwl_read_otp_word(priv->trans, addr, - &eeprom_data); - if (ret) - goto done; - e[cache_addr / 2] = eeprom_data; - cache_addr += sizeof(u16); - } - } else { - /* eeprom is an array of 16bit values */ - for (addr = 0; addr < sz; addr += sizeof(u16)) { - u32 r; - - iwl_write32(priv->trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - - ret = iwl_poll_bit(priv->trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(priv, - "Time out reading EEPROM[%d]\n", addr); - goto done; - } - r = iwl_read32(priv->trans, CSR_EEPROM_REG); - e[addr / 2] = cpu_to_le16(r >> 16); - } - } - - IWL_DEBUG_EEPROM(priv, "NVM Type: %s, version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", - iwl_eeprom_query16(priv, EEPROM_VERSION)); - - ret = 0; -done: - iwl_eeprom_release_semaphore(priv->trans); - -err: - if (ret) - iwl_eeprom_free(priv); -alloc_err: - return ret; -} - -void iwl_eeprom_free(struct iwl_priv *priv) -{ - kfree(priv->eeprom); - priv->eeprom = NULL; -} - -static void iwl_init_band_reference(struct iwl_priv *priv, - int eep_band, int *eeprom_ch_count, - const struct iwl_eeprom_channel **eeprom_ch_info, - const u8 **eeprom_ch_index) -{ - u32 offset = priv->lib-> - eeprom_ops.regulatory_bands[eep_band - 1]; - switch (eep_band) { - case 1: /* 2.4GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_1; - break; - case 2: /* 4.9GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_2; - break; - case 3: /* 5.2GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_3; - break; - case 4: /* 5.5GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_4; - break; - case 5: /* 5.7GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_5; - break; - case 6: /* 2.4GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_6; - break; - case 7: /* 5 GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_7; - break; - default: - BUG(); - return; - } -} - -#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ - ? # x " " : "") -/** - * iwl_mod_ht40_chan_info - Copy ht40 channel info into driver's priv. - * - * Does not set up a command, or touch hardware. - */ -static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, - enum ieee80211_band band, u16 channel, - const struct iwl_eeprom_channel *eeprom_ch, - u8 clear_ht40_extension_channel) -{ - struct iwl_channel_info *ch_info; - - ch_info = (struct iwl_channel_info *) - iwl_get_channel_info(priv, band, channel); - - if (!is_channel_valid(ch_info)) - return -1; - - IWL_DEBUG_EEPROM(priv, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" - " Ad-Hoc %ssupported\n", - ch_info->channel, - is_channel_a_band(ch_info) ? - "5.2" : "2.4", - CHECK_AND_PRINT(IBSS), - CHECK_AND_PRINT(ACTIVE), - CHECK_AND_PRINT(RADAR), - CHECK_AND_PRINT(WIDE), - CHECK_AND_PRINT(DFS), - eeprom_ch->flags, - eeprom_ch->max_power_avg, - ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) - && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? - "" : "not "); - - ch_info->ht40_eeprom = *eeprom_ch; - ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; - ch_info->ht40_flags = eeprom_ch->flags; - if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) - ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; - - return 0; -} - -#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ - ? # x " " : "") - -/** - * iwl_init_channel_map - Set up driver's info for all possible channels - */ -int iwl_init_channel_map(struct iwl_priv *priv) -{ - int eeprom_ch_count = 0; - const u8 *eeprom_ch_index = NULL; - const struct iwl_eeprom_channel *eeprom_ch_info = NULL; - int band, ch; - struct iwl_channel_info *ch_info; - - if (priv->channel_count) { - IWL_DEBUG_EEPROM(priv, "Channel map already initialized.\n"); - return 0; - } - - IWL_DEBUG_EEPROM(priv, "Initializing regulatory info from EEPROM\n"); - - priv->channel_count = - ARRAY_SIZE(iwl_eeprom_band_1) + - ARRAY_SIZE(iwl_eeprom_band_2) + - ARRAY_SIZE(iwl_eeprom_band_3) + - ARRAY_SIZE(iwl_eeprom_band_4) + - ARRAY_SIZE(iwl_eeprom_band_5); - - IWL_DEBUG_EEPROM(priv, "Parsing data for %d channels.\n", - priv->channel_count); - - priv->channel_info = kcalloc(priv->channel_count, - sizeof(struct iwl_channel_info), - GFP_KERNEL); - if (!priv->channel_info) { - IWL_ERR(priv, "Could not allocate channel_info\n"); - priv->channel_count = 0; - return -ENOMEM; - } - - ch_info = priv->channel_info; - - /* Loop through the 5 EEPROM bands adding them in order to the - * channel map we maintain (that contains additional information than - * what just in the EEPROM) */ - for (band = 1; band <= 5; band++) { - - iwl_init_band_reference(priv, band, &eeprom_ch_count, - &eeprom_ch_info, &eeprom_ch_index); - - /* Loop through each band adding each of the channels */ - for (ch = 0; ch < eeprom_ch_count; ch++) { - ch_info->channel = eeprom_ch_index[ch]; - ch_info->band = (band == 1) ? IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ; - - /* permanently store EEPROM's channel regulatory flags - * and max power in channel info database. */ - ch_info->eeprom = eeprom_ch_info[ch]; - - /* Copy the run-time flags so they are there even on - * invalid channels */ - ch_info->flags = eeprom_ch_info[ch].flags; - /* First write that ht40 is not enabled, and then enable - * one by one */ - ch_info->ht40_extension_channel = - IEEE80211_CHAN_NO_HT40; - - if (!(is_channel_valid(ch_info))) { - IWL_DEBUG_EEPROM(priv, - "Ch. %d Flags %x [%sGHz] - " - "No traffic\n", - ch_info->channel, - ch_info->flags, - is_channel_a_band(ch_info) ? - "5.2" : "2.4"); - ch_info++; - continue; - } - - /* Initialize regulatory-based run-time data */ - ch_info->max_power_avg = ch_info->curr_txpow = - eeprom_ch_info[ch].max_power_avg; - ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; - ch_info->min_power = 0; - - IWL_DEBUG_EEPROM(priv, "Ch. %d [%sGHz] " - "%s%s%s%s%s%s(0x%02x %ddBm):" - " Ad-Hoc %ssupported\n", - ch_info->channel, - is_channel_a_band(ch_info) ? - "5.2" : "2.4", - CHECK_AND_PRINT_I(VALID), - CHECK_AND_PRINT_I(IBSS), - CHECK_AND_PRINT_I(ACTIVE), - CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(DFS), - eeprom_ch_info[ch].flags, - eeprom_ch_info[ch].max_power_avg, - ((eeprom_ch_info[ch]. - flags & EEPROM_CHANNEL_IBSS) - && !(eeprom_ch_info[ch]. - flags & EEPROM_CHANNEL_RADAR)) - ? "" : "not "); - - ch_info++; - } - } - - /* Check if we do have HT40 channels */ - if (priv->lib->eeprom_ops.regulatory_bands[5] == - EEPROM_REGULATORY_BAND_NO_HT40 && - priv->lib->eeprom_ops.regulatory_bands[6] == - EEPROM_REGULATORY_BAND_NO_HT40) - return 0; - - /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ - for (band = 6; band <= 7; band++) { - enum ieee80211_band ieeeband; - - iwl_init_band_reference(priv, band, &eeprom_ch_count, - &eeprom_ch_info, &eeprom_ch_index); - - /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ - ieeeband = - (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - - /* Loop through each band adding each of the channels */ - for (ch = 0; ch < eeprom_ch_count; ch++) { - /* Set up driver's info for lower half */ - iwl_mod_ht40_chan_info(priv, ieeeband, - eeprom_ch_index[ch], - &eeprom_ch_info[ch], - IEEE80211_CHAN_NO_HT40PLUS); - - /* Set up driver's info for upper half */ - iwl_mod_ht40_chan_info(priv, ieeeband, - eeprom_ch_index[ch] + 4, - &eeprom_ch_info[ch], - IEEE80211_CHAN_NO_HT40MINUS); - } - } - - /* for newer device (6000 series and up) - * EEPROM contain enhanced tx power information - * driver need to process addition information - * to determine the max channel tx power limits - */ - if (priv->lib->eeprom_ops.enhanced_txpower) - iwl_eeprom_enhanced_txpower(priv); - - return 0; -} - -/* - * iwl_free_channel_map - undo allocations in iwl_init_channel_map - */ -void iwl_free_channel_map(struct iwl_priv *priv) -{ - kfree(priv->channel_info); - priv->channel_count = 0; -} - -/** - * iwl_get_channel_info - Find driver's private channel info - * - * Based on band and channel number. - */ -const struct iwl_channel_info *iwl_get_channel_info(const struct iwl_priv *priv, - enum ieee80211_band band, u16 channel) -{ - int i; - - switch (band) { - case IEEE80211_BAND_5GHZ: - for (i = 14; i < priv->channel_count; i++) { - if (priv->channel_info[i].channel == channel) - return &priv->channel_info[i]; - } - break; - case IEEE80211_BAND_2GHZ: - if (channel >= 1 && channel <= 14) - return &priv->channel_info[channel - 1]; - break; - default: - BUG(); - } - - return NULL; -} - -void iwl_rf_config(struct iwl_priv *priv) -{ - u16 radio_cfg; - - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); - - /* write radio config values to register */ - if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | - EEPROM_RF_CFG_STEP_MSK(radio_cfg) | - EEPROM_RF_CFG_DASH_MSK(radio_cfg)); - IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", - EEPROM_RF_CFG_TYPE_MSK(radio_cfg), - EEPROM_RF_CFG_STEP_MSK(radio_cfg), - EEPROM_RF_CFG_DASH_MSK(radio_cfg)); - } else - WARN_ON(1); - - /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h deleted file mode 100644 index 64bfd947caeb..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ /dev/null @@ -1,269 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_eeprom_h__ -#define __iwl_eeprom_h__ - -#include - -struct iwl_priv; - -/* - * EEPROM access time values: - * - * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. - * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). - * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. - * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. - */ -#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ - -#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ -#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - - -/* - * Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags. - * - * IBSS and/or AP operation is allowed *only* on those channels with - * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because - * RADAR detection is not supported by the 4965 driver, but is a - * requirement for establishing a new network for legal operation on channels - * requiring RADAR detection or restricting ACTIVE scanning. - * - * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. - * It only indicates that 20 MHz channel use is supported; HT40 channel - * usage is indicated by a separate set of regulatory flags for each - * HT40 channel pair. - * - * NOTE: Using a channel inappropriately will result in a uCode error! - */ -#define IWL_NUM_TX_CALIB_GROUPS 5 -enum { - EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ - EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ - /* Bit 2 Reserved */ - EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ - EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ - EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ - /* Bit 6 Reserved (was Narrow Channel) */ - EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ -}; - -/* SKU Capabilities */ -#define EEPROM_SKU_CAP_BAND_24GHZ (1 << 4) -#define EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) -#define EEPROM_SKU_CAP_11N_ENABLE (1 << 6) -#define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7) -#define EEPROM_SKU_CAP_IPAN_ENABLE (1 << 8) - -/* *regulatory* channel data format in eeprom, one for each channel. - * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ -struct iwl_eeprom_channel { - u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ - s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ -} __packed; - -enum iwl_eeprom_enhanced_txpwr_flags { - IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), - IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), - IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), - IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), - IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), - IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), - IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), -}; - -/** - * iwl_eeprom_enhanced_txpwr structure - * This structure presents the enhanced regulatory tx power limit layout - * in eeprom image - * Enhanced regulatory tx power portion of eeprom image can be broken down - * into individual structures; each one is 8 bytes in size and contain the - * following information - * @flags: entry flags - * @channel: channel number - * @chain_a_max_pwr: chain a max power in 1/2 dBm - * @chain_b_max_pwr: chain b max power in 1/2 dBm - * @chain_c_max_pwr: chain c max power in 1/2 dBm - * @delta_20_in_40: 20-in-40 deltas (hi/lo) - * @mimo2_max_pwr: mimo2 max power in 1/2 dBm - * @mimo3_max_pwr: mimo3 max power in 1/2 dBm - * - */ -struct iwl_eeprom_enhanced_txpwr { - u8 flags; - u8 channel; - s8 chain_a_max; - s8 chain_b_max; - s8 chain_c_max; - u8 delta_20_in_40; - s8 mimo2_max; - s8 mimo3_max; -} __packed; - -/* calibration */ -struct iwl_eeprom_calib_hdr { - u8 version; - u8 pa_type; - __le16 voltage; -} __packed; - -#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) -#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) - -/* temperature */ -#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) -#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) - - -/* agn links */ -#define EEPROM_LINK_HOST (2*0x64) -#define EEPROM_LINK_GENERAL (2*0x65) -#define EEPROM_LINK_REGULATORY (2*0x66) -#define EEPROM_LINK_CALIBRATION (2*0x67) -#define EEPROM_LINK_PROCESS_ADJST (2*0x68) -#define EEPROM_LINK_OTHERS (2*0x69) -#define EEPROM_LINK_TXP_LIMIT (2*0x6a) -#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) - -/* agn regulatory - indirect access */ -#define EEPROM_REG_BAND_1_CHANNELS ((0x08)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 28 bytes */ -#define EEPROM_REG_BAND_2_CHANNELS ((0x26)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 26 bytes */ -#define EEPROM_REG_BAND_3_CHANNELS ((0x42)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ -#define EEPROM_REG_BAND_4_CHANNELS ((0x5C)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ -#define EEPROM_REG_BAND_5_CHANNELS ((0x74)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 12 bytes */ -#define EEPROM_REG_BAND_24_HT40_CHANNELS ((0x82)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ -#define EEPROM_REG_BAND_52_HT40_CHANNELS ((0x92)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ - -/* 6000 regulatory - indirect access */ -#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS ((0x80)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ -/* 2.4 GHz */ -extern const u8 iwl_eeprom_band_1[14]; - -#define ADDRESS_MSK 0x0000FFFF -#define INDIRECT_TYPE_MSK 0x000F0000 -#define INDIRECT_HOST 0x00010000 -#define INDIRECT_GENERAL 0x00020000 -#define INDIRECT_REGULATORY 0x00030000 -#define INDIRECT_CALIBRATION 0x00040000 -#define INDIRECT_PROCESS_ADJST 0x00050000 -#define INDIRECT_OTHERS 0x00060000 -#define INDIRECT_TXP_LIMIT 0x00070000 -#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 -#define INDIRECT_ADDRESS 0x00100000 - -/* General */ -#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ -#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ -#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ -#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ -#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ -#define EEPROM_VERSION (2*0x44) /* 2 bytes */ -#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ -#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ -#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ -#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ - -/* The following masks are to be applied on EEPROM_RADIO_CONFIG */ -#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ -#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ -#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ -#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ -#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ -#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ - -#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 - -#define EEPROM_REGULATORY_BAND_NO_HT40 (0) - -struct iwl_eeprom_ops { - const u32 regulatory_bands[7]; - bool enhanced_txpower; -}; - - -int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev); -void iwl_eeprom_free(struct iwl_priv *priv); -int iwl_eeprom_check_version(struct iwl_priv *priv); -int iwl_eeprom_init_hw_params(struct iwl_priv *priv); -u16 iwl_eeprom_calib_version(struct iwl_priv *priv); -const u8 *iwl_eeprom_query_addr(struct iwl_priv *priv, size_t offset); -u16 iwl_eeprom_query16(struct iwl_priv *priv, size_t offset); -void iwl_eeprom_get_mac(struct iwl_priv *priv, u8 *mac); -int iwl_init_channel_map(struct iwl_priv *priv); -void iwl_free_channel_map(struct iwl_priv *priv); -const struct iwl_channel_info *iwl_get_channel_info( - const struct iwl_priv *priv, - enum ieee80211_band band, u16 channel); -void iwl_rf_config(struct iwl_priv *priv); - -#endif /* __iwl_eeprom_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c deleted file mode 100644 index 47000419f916..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ /dev/null @@ -1,225 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iwl-dev.h" -#include "iwl-agn.h" -#include "iwl-io.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" - -/* Throughput OFF time(ms) ON time (ms) - * >300 25 25 - * >200 to 300 40 40 - * >100 to 200 55 55 - * >70 to 100 65 65 - * >50 to 70 75 75 - * >20 to 50 85 85 - * >10 to 20 95 95 - * >5 to 10 110 110 - * >1 to 5 130 130 - * >0 to 1 167 167 - * <=0 SOLID ON - */ -static const struct ieee80211_tpt_blink iwl_blink[] = { - { .throughput = 0, .blink_time = 334 }, - { .throughput = 1 * 1024 - 1, .blink_time = 260 }, - { .throughput = 5 * 1024 - 1, .blink_time = 220 }, - { .throughput = 10 * 1024 - 1, .blink_time = 190 }, - { .throughput = 20 * 1024 - 1, .blink_time = 170 }, - { .throughput = 50 * 1024 - 1, .blink_time = 150 }, - { .throughput = 70 * 1024 - 1, .blink_time = 130 }, - { .throughput = 100 * 1024 - 1, .blink_time = 110 }, - { .throughput = 200 * 1024 - 1, .blink_time = 80 }, - { .throughput = 300 * 1024 - 1, .blink_time = 50 }, -}; - -/* Set led register off */ -void iwlagn_led_enable(struct iwl_priv *priv) -{ - iwl_write32(priv->trans, CSR_LED_REG, CSR_LED_REG_TRUN_ON); -} - -/* - * Adjust led blink rate to compensate on a MAC Clock difference on every HW - * Led blink rate analysis showed an average deviation of 20% on 5000 series - * and up. - * Need to compensate on the led on/off time per HW according to the deviation - * to achieve the desired led frequency - * The calculation is: (100-averageDeviation)/100 * blinkTime - * For code efficiency the calculation will be: - * compensation = (100 - averageDeviation) * 64 / 100 - * NewBlinkTime = (compensation * BlinkTime) / 64 - */ -static inline u8 iwl_blink_compensation(struct iwl_priv *priv, - u8 time, u16 compensation) -{ - if (!compensation) { - IWL_ERR(priv, "undefined blink compensation: " - "use pre-defined blinking time\n"); - return time; - } - - return (u8)((time * compensation) >> 6); -} - -static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_LEDS_CMD, - .len = { sizeof(struct iwl_led_cmd), }, - .data = { led_cmd, }, - .flags = CMD_ASYNC, - }; - u32 reg; - - reg = iwl_read32(priv->trans, CSR_LED_REG); - if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) - iwl_write32(priv->trans, CSR_LED_REG, - reg & CSR_LED_BSM_CTRL_MSK); - - return iwl_dvm_send_cmd(priv, &cmd); -} - -/* Set led pattern command */ -static int iwl_led_cmd(struct iwl_priv *priv, - unsigned long on, - unsigned long off) -{ - struct iwl_led_cmd led_cmd = { - .id = IWL_LED_LINK, - .interval = IWL_DEF_LED_INTRVL - }; - int ret; - - if (!test_bit(STATUS_READY, &priv->status)) - return -EBUSY; - - if (priv->blink_on == on && priv->blink_off == off) - return 0; - - if (off == 0) { - /* led is SOLID_ON */ - on = IWL_LED_SOLID; - } - - IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", - priv->cfg->base_params->led_compensation); - led_cmd.on = iwl_blink_compensation(priv, on, - priv->cfg->base_params->led_compensation); - led_cmd.off = iwl_blink_compensation(priv, off, - priv->cfg->base_params->led_compensation); - - ret = iwl_send_led_cmd(priv, &led_cmd); - if (!ret) { - priv->blink_on = on; - priv->blink_off = off; - } - return ret; -} - -static void iwl_led_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); - unsigned long on = 0; - - if (brightness > 0) - on = IWL_LED_SOLID; - - iwl_led_cmd(priv, on, 0); -} - -static int iwl_led_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); - - return iwl_led_cmd(priv, *delay_on, *delay_off); -} - -void iwl_leds_init(struct iwl_priv *priv) -{ - int mode = iwlwifi_mod_params.led_mode; - int ret; - - if (mode == IWL_LED_DISABLE) { - IWL_INFO(priv, "Led disabled\n"); - return; - } - if (mode == IWL_LED_DEFAULT) - mode = priv->cfg->led_mode; - - priv->led.name = kasprintf(GFP_KERNEL, "%s-led", - wiphy_name(priv->hw->wiphy)); - priv->led.brightness_set = iwl_led_brightness_set; - priv->led.blink_set = iwl_led_blink_set; - priv->led.max_brightness = 1; - - switch (mode) { - case IWL_LED_DEFAULT: - WARN_ON(1); - break; - case IWL_LED_BLINK: - priv->led.default_trigger = - ieee80211_create_tpt_led_trigger(priv->hw, - IEEE80211_TPT_LEDTRIG_FL_CONNECTED, - iwl_blink, ARRAY_SIZE(iwl_blink)); - break; - case IWL_LED_RF_STATE: - priv->led.default_trigger = - ieee80211_get_radio_led_name(priv->hw); - break; - } - - ret = led_classdev_register(priv->trans->dev, &priv->led); - if (ret) { - kfree(priv->led.name); - return; - } - - priv->led_registered = true; -} - -void iwl_leds_exit(struct iwl_priv *priv) -{ - if (!priv->led_registered) - return; - - led_classdev_unregister(&priv->led); - kfree(priv->led.name); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h deleted file mode 100644 index b02a853103d3..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-led.h +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_leds_h__ -#define __iwl_leds_h__ - - -struct iwl_priv; - -#define IWL_LED_SOLID 11 -#define IWL_DEF_LED_INTRVL cpu_to_le32(1000) - -#define IWL_LED_ACTIVITY (0<<1) -#define IWL_LED_LINK (1<<1) - -void iwlagn_led_enable(struct iwl_priv *priv); -void iwl_leds_init(struct iwl_priv *priv); -void iwl_leds_exit(struct iwl_priv *priv); - -#endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c deleted file mode 100644 index 26a87c2b1653..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ /dev/null @@ -1,1630 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn-calib.h" -#include "iwl-agn.h" -#include "iwl-trans.h" -#include "iwl-op-mode.h" -#include "iwl-modparams.h" - -/***************************************************************************** - * - * mac80211 entry point functions - * - *****************************************************************************/ - -static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT), - }, -}; - -static const struct ieee80211_iface_combination -iwlagn_iface_combinations_dualmode[] = { - { .num_different_channels = 1, - .max_interfaces = 2, - .beacon_int_infra_match = true, - .limits = iwlagn_sta_ap_limits, - .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), - }, - { .num_different_channels = 1, - .max_interfaces = 2, - .limits = iwlagn_2sta_limits, - .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), - }, -}; - -static const struct ieee80211_iface_combination -iwlagn_iface_combinations_p2p[] = { - { .num_different_channels = 1, - .max_interfaces = 2, - .beacon_int_infra_match = true, - .limits = iwlagn_p2p_sta_go_limits, - .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits), - }, - { .num_different_channels = 1, - .max_interfaces = 2, - .limits = iwlagn_p2p_2sta_limits, - .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits), - }, -}; - -/* - * Not a mac80211 entry point function, but it fits in with all the - * other mac80211 functions grouped here. - */ -int iwlagn_mac_setup_register(struct iwl_priv *priv, - const struct iwl_ucode_capabilities *capa) -{ - int ret; - struct ieee80211_hw *hw = priv->hw; - struct iwl_rxon_context *ctx; - - hw->rate_control_algorithm = "iwl-agn-rs"; - - /* Tell mac80211 our characteristics */ - hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_NEED_DTIM_PERIOD | - IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_QUEUE_CONTROL | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_SCAN_WHILE_IDLE; - - hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; - hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FMT; - - /* - * Including the following line will crash some AP's. This - * workaround removes the stimulus which causes the crash until - * the AP software can be fixed. - hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - */ - - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) - hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_SUPPORTS_STATIC_SMPS; - -#ifndef CONFIG_IWLWIFI_EXPERIMENTAL_MFP - /* enable 11w if the uCode advertise */ - if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) -#endif /* !CONFIG_IWLWIFI_EXPERIMENTAL_MFP */ - hw->flags |= IEEE80211_HW_MFP_CAPABLE; - - hw->sta_data_size = sizeof(struct iwl_station_priv); - hw->vif_data_size = sizeof(struct iwl_vif_priv); - - for_each_context(priv, ctx) { - hw->wiphy->interface_modes |= ctx->interface_modes; - hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; - } - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) { - hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwlagn_iface_combinations_p2p); - } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { - hw->wiphy->iface_combinations = - iwlagn_iface_combinations_dualmode; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwlagn_iface_combinations_dualmode); - } - - hw->wiphy->max_remain_on_channel_duration = 1000; - - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; - - if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && - priv->trans->ops->wowlan_suspend && - device_can_wakeup(priv->trans->dev)) { - hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE; - if (!iwlwifi_mod_params.sw_crypto) - hw->wiphy->wowlan.flags |= - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_GTK_REKEY_FAILURE; - - hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; - hw->wiphy->wowlan.pattern_min_len = - IWLAGN_WOWLAN_MIN_PATTERN_LEN; - hw->wiphy->wowlan.pattern_max_len = - IWLAGN_WOWLAN_MAX_PATTERN_LEN; - } - - if (iwlwifi_mod_params.power_save) - hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; - else - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - /* we create the 802.11 header and a max-length SSID element */ - hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 34; - - /* - * We don't use all queues: 4 and 9 are unused and any - * aggregation queue gets mapped down to the AC queue. - */ - hw->queues = IWLAGN_FIRST_AMPDU_QUEUE; - - hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - - if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &priv->bands[IEEE80211_BAND_2GHZ]; - if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &priv->bands[IEEE80211_BAND_5GHZ]; - - hw->wiphy->hw_version = priv->trans->hw_id; - - iwl_leds_init(priv); - - ret = ieee80211_register_hw(priv->hw); - if (ret) { - IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); - return ret; - } - priv->mac80211_registered = 1; - - return 0; -} - -void iwlagn_mac_unregister(struct iwl_priv *priv) -{ - if (!priv->mac80211_registered) - return; - iwl_leds_exit(priv); - ieee80211_unregister_hw(priv->hw); - priv->mac80211_registered = 0; -} - -static int __iwl_up(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - int ret; - - lockdep_assert_held(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { - IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - for_each_context(priv, ctx) { - ret = iwlagn_alloc_bcast_station(priv, ctx); - if (ret) { - iwl_dealloc_bcast_stations(priv); - return ret; - } - } - - ret = iwl_run_init_ucode(priv); - if (ret) { - IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); - goto error; - } - - ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); - if (ret) { - IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); - goto error; - } - - ret = iwl_alive_start(priv); - if (ret) - goto error; - return 0; - - error: - set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_down(priv); - clear_bit(STATUS_EXIT_PENDING, &priv->status); - - IWL_ERR(priv, "Unable to initialize device.\n"); - return ret; -} - -static int iwlagn_mac_start(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int ret; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - /* we should be verifying the device is ready to be opened */ - mutex_lock(&priv->mutex); - ret = __iwl_up(priv); - mutex_unlock(&priv->mutex); - if (ret) - return ret; - - IWL_DEBUG_INFO(priv, "Start UP work done.\n"); - - /* Now we should be done, and the READY bit should be set. */ - if (WARN_ON(!test_bit(STATUS_READY, &priv->status))) - ret = -EIO; - - iwlagn_led_enable(priv); - - priv->is_open = 1; - IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; -} - -static void iwlagn_mac_stop(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!priv->is_open) - return; - - priv->is_open = 0; - - mutex_lock(&priv->mutex); - iwl_down(priv); - mutex_unlock(&priv->mutex); - - iwl_cancel_deferred_work(priv); - - flush_workqueue(priv->workqueue); - - /* User space software may expect getting rfkill changes - * even if interface is down, trans->down will leave the RF - * kill interrupt enabled - */ - iwl_trans_stop_hw(priv->trans, false); - - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - if (iwlwifi_mod_params.sw_crypto) - return; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) - goto out; - - memcpy(priv->kek, data->kek, NL80211_KEK_LEN); - memcpy(priv->kck, data->kck, NL80211_KCK_LEN); - priv->replay_ctr = - cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); - priv->have_rekey_data = true; - - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -#ifdef CONFIG_PM_SLEEP - -static int iwlagn_mac_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - int ret; - - if (WARN_ON(!wowlan)) - return -EINVAL; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - /* Don't attempt WoWLAN when not associated, tear down instead. */ - if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || - !iwl_is_associated_ctx(ctx)) { - ret = 1; - goto out; - } - - ret = iwlagn_suspend(priv, wowlan); - if (ret) - goto error; - - iwl_trans_wowlan_suspend(priv->trans); - - goto out; - - error: - priv->wowlan = false; - iwlagn_prepare_restart(priv); - ieee80211_restart_hw(priv->hw); - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static int iwlagn_mac_resume(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct ieee80211_vif *vif; - unsigned long flags; - u32 base, status = 0xffffffff; - int ret = -EIO; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - - base = priv->device_pointers.error_event_table; - if (iwlagn_hw_valid_rtc_data_addr(base)) { - spin_lock_irqsave(&priv->trans->reg_lock, flags); - ret = iwl_grab_nic_access_silent(priv->trans); - if (likely(ret == 0)) { - iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base); - status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - iwl_release_nic_access(priv->trans); - } - spin_unlock_irqrestore(&priv->trans->reg_lock, flags); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (ret == 0) { - const struct fw_img *img; - - img = &(priv->fw->img[IWL_UCODE_WOWLAN]); - if (!priv->wowlan_sram) { - priv->wowlan_sram = - kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, - GFP_KERNEL); - } - - if (priv->wowlan_sram) - _iwl_read_targ_mem_words( - priv->trans, 0x800000, - priv->wowlan_sram, - img->sec[IWL_UCODE_SECTION_DATA].len / 4); - } -#endif - } - - /* we'll clear ctx->vif during iwlagn_prepare_restart() */ - vif = ctx->vif; - - priv->wowlan = false; - - iwlagn_prepare_restart(priv); - - memset((void *)&ctx->active, 0, sizeof(ctx->active)); - iwl_connection_init_rx_config(priv, ctx); - iwlagn_set_rxon_chain(priv, ctx); - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - ieee80211_resume_disconnect(vif); - - return 1; -} - -static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - device_set_wakeup_enable(priv->trans->dev, enabled); -} -#endif - -static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, - ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - - if (iwlagn_tx_skb(priv, skb)) - dev_kfree_skb_any(skb); -} - -static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); -} - -static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - int ret; - bool is_default_wep_key = false; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (iwlwifi_mod_params.sw_crypto) { - IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - /* fall through */ - case WLAN_CIPHER_SUITE_CCMP: - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - break; - default: - break; - } - - /* - * We could program these keys into the hardware as well, but we - * don't expect much multicast traffic in IBSS and having keys - * for more stations is probably more useful. - * - * Mark key TX-only and return 0. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - key->hw_key_idx = WEP_INVALID_OFFSET; - return 0; - } - - /* If they key was TX-only, accept deletion */ - if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) - return 0; - - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 100); - - BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); - - /* - * If we are getting WEP group key and we didn't receive any key mapping - * so far, we are in legacy wep mode (group key only), otherwise we are - * in 1X mode. - * In legacy wep mode, we use another host command to the uCode. - */ - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { - if (cmd == SET_KEY) - is_default_wep_key = !ctx->key_mapping_keys; - else - is_default_wep_key = - key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; - } - - - switch (cmd) { - case SET_KEY: - if (is_default_wep_key) { - ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); - break; - } - ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); - if (ret) { - /* - * can't add key for RX, but we don't need it - * in the device for TX so still return 0 - */ - ret = 0; - key->hw_key_idx = WEP_INVALID_OFFSET; - } - - IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); - break; - case DISABLE_KEY: - if (is_default_wep_key) - ret = iwl_remove_default_wep_key(priv, ctx, key); - else - ret = iwl_remove_dynamic_key(priv, ctx, key, sta); - - IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int ret = -EINVAL; - struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; - - IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", - sta->addr, tid); - - if (!(priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE)) - return -EACCES; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) - break; - IWL_DEBUG_HT(priv, "start Rx\n"); - ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); - break; - case IEEE80211_AMPDU_RX_STOP: - IWL_DEBUG_HT(priv, "stop Rx\n"); - ret = iwl_sta_rx_agg_stop(priv, sta, tid); - break; - case IEEE80211_AMPDU_TX_START: - if (!priv->trans->ops->tx_agg_setup) - break; - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) - break; - IWL_DEBUG_HT(priv, "start Tx\n"); - ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); - break; - case IEEE80211_AMPDU_TX_STOP: - IWL_DEBUG_HT(priv, "stop Tx\n"); - ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); - if ((ret == 0) && (priv->agg_tids_count > 0)) { - priv->agg_tids_count--; - IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", - priv->agg_tids_count); - } - if (!priv->agg_tids_count && - priv->hw_params.use_rts_for_aggregation) { - /* - * switch off RTS/CTS if it was previously enabled - */ - sta_priv->lq_sta.lq.general_params.flags &= - ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; - iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), - &sta_priv->lq_sta.lq, CMD_ASYNC, false); - } - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); - break; - } - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - return ret; -} - -static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - bool is_ap = vif->type == NL80211_IFTYPE_STATION; - int ret; - u8 sta_id; - - IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", - sta->addr); - sta_priv->sta_id = IWL_INVALID_STATION; - - atomic_set(&sta_priv->pending_frames, 0); - if (vif->type == NL80211_IFTYPE_AP) - sta_priv->client = true; - - ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, - is_ap, sta, &sta_id); - if (ret) { - IWL_ERR(priv, "Unable to add station %pM (%d)\n", - sta->addr, ret); - /* Should we return success if return code is EEXIST ? */ - return ret; - } - - sta_priv->sta_id = sta_id; - - return 0; -} - -static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - int ret; - - IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); - - if (vif->type == NL80211_IFTYPE_STATION) { - /* - * Station will be removed from device when the RXON - * is set to unassociated -- just deactivate it here - * to avoid re-programming it. - */ - ret = 0; - iwl_deactivate_station(priv, sta_priv->sta_id, sta->addr); - } else { - ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); - if (ret) - IWL_DEBUG_QUIET_RFKILL(priv, - "Error removing station %pM\n", sta->addr); - } - return ret; -} - -static int iwlagn_mac_sta_state(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - enum ieee80211_sta_state old_state, - enum ieee80211_sta_state new_state) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - enum { - NONE, ADD, REMOVE, HT_RATE_INIT, ADD_RATE_INIT, - } op = NONE; - int ret; - - IWL_DEBUG_MAC80211(priv, "station %pM state change %d->%d\n", - sta->addr, old_state, new_state); - - mutex_lock(&priv->mutex); - if (vif->type == NL80211_IFTYPE_STATION) { - if (old_state == IEEE80211_STA_NOTEXIST && - new_state == IEEE80211_STA_NONE) - op = ADD; - else if (old_state == IEEE80211_STA_NONE && - new_state == IEEE80211_STA_NOTEXIST) - op = REMOVE; - else if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) - op = HT_RATE_INIT; - } else { - if (old_state == IEEE80211_STA_AUTH && - new_state == IEEE80211_STA_ASSOC) - op = ADD_RATE_INIT; - else if (old_state == IEEE80211_STA_ASSOC && - new_state == IEEE80211_STA_AUTH) - op = REMOVE; - } - - switch (op) { - case ADD: - ret = iwlagn_mac_sta_add(hw, vif, sta); - break; - case REMOVE: - ret = iwlagn_mac_sta_remove(hw, vif, sta); - break; - case ADD_RATE_INIT: - ret = iwlagn_mac_sta_add(hw, vif, sta); - if (ret) - break; - /* Initialize rate scaling */ - IWL_DEBUG_INFO(priv, - "Initializing rate scaling for station %pM\n", - sta->addr); - iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); - ret = 0; - break; - case HT_RATE_INIT: - /* Initialize rate scaling */ - ret = iwl_sta_update_ht(priv, vif_priv->ctx, sta); - if (ret) - break; - IWL_DEBUG_INFO(priv, - "Initializing rate scaling for station %pM\n", - sta->addr); - iwl_rs_rate_init(priv, sta, iwl_sta_id(sta)); - ret = 0; - break; - default: - ret = 0; - break; - } - - /* - * mac80211 might WARN if we fail, but due the way we - * (badly) handle hard rfkill, we might fail here - */ - if (iwl_is_rfkill(priv)) - ret = 0; - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_channel_switch *ch_switch) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = ch_switch->channel; - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - /* - * MULTI-FIXME - * When we add support for multiple interfaces, we need to - * revisit this. The channel switch command in the device - * only affects the BSS context, but what does that really - * mean? And what if we get a CSA on the second interface? - * This needs a lot of work. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - u16 ch; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - mutex_lock(&priv->mutex); - - if (iwl_is_rfkill(priv)) - goto out; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - test_bit(STATUS_SCANNING, &priv->status) || - test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - goto out; - - if (!iwl_is_associated_ctx(ctx)) - goto out; - - if (!priv->lib->set_channel_switch) - goto out; - - ch = channel->hw_value; - if (le16_to_cpu(ctx->active.channel) == ch) - goto out; - - priv->current_ht_config.smps = conf->smps_mode; - - /* Configure HT40 channels */ - ctx->ht.enabled = conf_is_ht(conf); - if (ctx->ht.enabled) - iwlagn_config_ht40(conf, ctx); - else - ctx->ht.is_40mhz = false; - - if ((le16_to_cpu(ctx->staging.channel) != ch)) - ctx->staging.flags = 0; - - iwl_set_rxon_channel(priv, channel, ctx); - iwl_set_rxon_ht(priv, ht_conf); - iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); - - /* - * at this point, staging_rxon has the - * configuration for channel switch - */ - set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); - priv->switch_channel = cpu_to_le16(ch); - if (priv->lib->set_channel_switch(priv, ch_switch)) { - clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status); - priv->switch_channel = 0; - ieee80211_chswitch_done(ctx->vif, false); - } - -out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) -{ - /* - * MULTI-FIXME - * See iwlagn_mac_channel_switch. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) - ieee80211_chswitch_done(ctx->vif, is_success); -} - -static void iwlagn_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - __le32 filter_or = 0, filter_nand = 0; - struct iwl_rxon_context *ctx; - -#define CHK(test, flag) do { \ - if (*total_flags & (test)) \ - filter_or |= (flag); \ - else \ - filter_nand |= (flag); \ - } while (0) - - IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", - changed_flags, *total_flags); - - CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); - /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ - CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); - CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); - -#undef CHK - - mutex_lock(&priv->mutex); - - for_each_context(priv, ctx) { - ctx->staging.filter_flags &= ~filter_nand; - ctx->staging.filter_flags |= filter_or; - - /* - * Not committing directly because hardware can perform a scan, - * but we'll eventually commit the filter flags change anyway. - */ - } - - mutex_unlock(&priv->mutex); - - /* - * Receiving all multicast frames is always enabled by the - * default flags setup in iwl_connection_init_rx_config() - * since we currently do not support programming multicast - * filters into the device. - */ - *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; -} - -static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { - IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); - goto done; - } - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); - goto done; - } - - /* - * mac80211 will not push any more frames for transmit - * until the flush is completed - */ - if (drop) { - IWL_DEBUG_MAC80211(priv, "send flush command\n"); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; - } - } - IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans); -done: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, - int duration) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; - int err = 0; - - if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) - return -EOPNOTSUPP; - - if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT))) - return -EOPNOTSUPP; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - err = -EBUSY; - goto out; - } - - priv->hw_roc_channel = channel; - priv->hw_roc_chantype = channel_type; - /* convert from ms to TU */ - priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); - priv->hw_roc_start_notified = false; - cancel_delayed_work(&priv->hw_roc_disable_work); - - if (!ctx->is_active) { - static const struct iwl_qos_info default_qos_data = { - .def_qos_parm = { - .ac[0] = { - .cw_min = cpu_to_le16(3), - .cw_max = cpu_to_le16(7), - .aifsn = 2, - .edca_txop = cpu_to_le16(1504), - }, - .ac[1] = { - .cw_min = cpu_to_le16(7), - .cw_max = cpu_to_le16(15), - .aifsn = 2, - .edca_txop = cpu_to_le16(3008), - }, - .ac[2] = { - .cw_min = cpu_to_le16(15), - .cw_max = cpu_to_le16(1023), - .aifsn = 3, - }, - .ac[3] = { - .cw_min = cpu_to_le16(15), - .cw_max = cpu_to_le16(1023), - .aifsn = 7, - }, - }, - }; - - ctx->is_active = true; - ctx->qos_data = default_qos_data; - ctx->staging.dev_type = RXON_DEV_TYPE_P2P; - memcpy(ctx->staging.node_addr, - priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, - ETH_ALEN); - memcpy(ctx->staging.bssid_addr, - priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, - ETH_ALEN); - err = iwlagn_commit_rxon(priv, ctx); - if (err) - goto out; - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK | - RXON_FILTER_PROMISC_MSK | - RXON_FILTER_CTL2HOST_MSK; - - err = iwlagn_commit_rxon(priv, ctx); - if (err) { - iwlagn_disable_roc(priv); - goto out; - } - priv->hw_roc_setup = true; - } - - err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band); - if (err) - iwlagn_disable_roc(priv); - - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return err; -} - -static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN))) - return -EOPNOTSUPP; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, priv->hw_roc_duration); - iwlagn_disable_roc(priv); - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return 0; -} - -static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, - enum ieee80211_rssi_event rssi_event) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->mutex); - - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - if (rssi_event == RSSI_EVENT_LOW) - priv->bt_enable_pspoll = true; - else if (rssi_event == RSSI_EVENT_HIGH) - priv->bt_enable_pspoll = false; - - iwlagn_send_advance_bt_config(priv); - } else { - IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," - "ignoring RSSI callback\n"); - } - - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - queue_work(priv->workqueue, &priv->beacon_update); - - return 0; -} - -static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - int q; - - if (WARN_ON(!ctx)) - return -EINVAL; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); - return -EIO; - } - - if (queue >= AC_NUM) { - IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); - return 0; - } - - q = AC_NUM - 1 - queue; - - mutex_lock(&priv->mutex); - - ctx->qos_data.def_qos_parm.ac[q].cw_min = - cpu_to_le16(params->cw_min); - ctx->qos_data.def_qos_parm.ac[q].cw_max = - cpu_to_le16(params->cw_max); - ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; - ctx->qos_data.def_qos_parm.ac[q].edca_txop = - cpu_to_le16((params->txop * 32)); - - ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; -} - -static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - - return priv->ibss_manager == IWL_IBSS_MANAGER; -} - -static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - iwl_connection_init_rx_config(priv, ctx); - - iwlagn_set_rxon_chain(priv, ctx); - - return iwlagn_commit_rxon(priv, ctx); -} - -static int iwl_setup_interface(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct ieee80211_vif *vif = ctx->vif; - int err, ac; - - lockdep_assert_held(&priv->mutex); - - /* - * This variable will be correct only when there's just - * a single context, but all code using it is for hardware - * that supports only one context. - */ - priv->iw_mode = vif->type; - - ctx->is_active = true; - - err = iwl_set_mode(priv, ctx); - if (err) { - if (!ctx->always_active) - ctx->is_active = false; - return err; - } - - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && - vif->type == NL80211_IFTYPE_ADHOC) { - /* - * pretend to have high BT traffic as long as we - * are operating in IBSS mode, as this will cause - * the rate scaling etc. to behave as intended. - */ - priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - } - - /* set up queue mappings */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - vif->hw_queue[ac] = ctx->ac_to_queue[ac]; - - if (vif->type == NL80211_IFTYPE_AP) - vif->cab_queue = ctx->mcast_queue; - else - vif->cab_queue = IEEE80211_INVAL_HW_QUEUE; - - return 0; -} - -static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *tmp, *ctx = NULL; - int err; - enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); - bool reset = false; - - IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", - viftype, vif->addr); - - cancel_delayed_work_sync(&priv->hw_roc_disable_work); - - mutex_lock(&priv->mutex); - - iwlagn_disable_roc(priv); - - if (!iwl_is_ready_rf(priv)) { - IWL_WARN(priv, "Try to add interface when device not ready\n"); - err = -EINVAL; - goto out; - } - - for_each_context(priv, tmp) { - u32 possible_modes = - tmp->interface_modes | tmp->exclusive_interface_modes; - - if (tmp->vif) { - /* On reset we need to add the same interface again */ - if (tmp->vif == vif) { - reset = true; - ctx = tmp; - break; - } - - /* check if this busy context is exclusive */ - if (tmp->exclusive_interface_modes & - BIT(tmp->vif->type)) { - err = -EINVAL; - goto out; - } - continue; - } - - if (!(possible_modes & BIT(viftype))) - continue; - - /* have maybe usable context w/o interface */ - ctx = tmp; - break; - } - - if (!ctx) { - err = -EOPNOTSUPP; - goto out; - } - - vif_priv->ctx = ctx; - ctx->vif = vif; - - err = iwl_setup_interface(priv, ctx); - if (!err || reset) - goto out; - - ctx->vif = NULL; - priv->iw_mode = NL80211_IFTYPE_STATION; - out: - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - return err; -} - -static void iwl_teardown_interface(struct iwl_priv *priv, - struct ieee80211_vif *vif, - bool mode_change) -{ - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - lockdep_assert_held(&priv->mutex); - - if (priv->scan_vif == vif) { - iwl_scan_cancel_timeout(priv, 200); - iwl_force_scan_end(priv); - } - - if (!mode_change) { - iwl_set_mode(priv, ctx); - if (!ctx->always_active) - ctx->is_active = false; - } - - /* - * When removing the IBSS interface, overwrite the - * BT traffic load with the stored one from the last - * notification, if any. If this is a device that - * doesn't implement this, this has no effect since - * both values are the same and zero. - */ - if (vif->type == NL80211_IFTYPE_ADHOC) - priv->bt_traffic_load = priv->last_bt_traffic_load; -} - -static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - mutex_lock(&priv->mutex); - - if (WARN_ON(ctx->vif != vif)) { - struct iwl_rxon_context *tmp; - IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); - for_each_context(priv, tmp) - IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", - tmp->ctxid, tmp, tmp->vif); - } - ctx->vif = NULL; - - iwl_teardown_interface(priv, vif, false); - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - -} - -static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - struct iwl_rxon_context *bss_ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_rxon_context *tmp; - enum nl80211_iftype newviftype = newtype; - u32 interface_modes; - int err; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - newtype = ieee80211_iftype_p2p(newtype, newp2p); - - mutex_lock(&priv->mutex); - - if (!ctx->vif || !iwl_is_ready_rf(priv)) { - /* - * Huh? But wait ... this can maybe happen when - * we're in the middle of a firmware restart! - */ - err = -EBUSY; - goto out; - } - - interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; - - if (!(interface_modes & BIT(newtype))) { - err = -EBUSY; - goto out; - } - - /* - * Refuse a change that should be done by moving from the PAN - * context to the BSS context instead, if the BSS context is - * available and can support the new interface type. - */ - if (ctx->ctxid == IWL_RXON_CTX_PAN && !bss_ctx->vif && - (bss_ctx->interface_modes & BIT(newtype) || - bss_ctx->exclusive_interface_modes & BIT(newtype))) { - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - err = -EBUSY; - goto out; - } - - if (ctx->exclusive_interface_modes & BIT(newtype)) { - for_each_context(priv, tmp) { - if (ctx == tmp) - continue; - - if (!tmp->vif) - continue; - - /* - * The current mode switch would be exclusive, but - * another context is active ... refuse the switch. - */ - err = -EBUSY; - goto out; - } - } - - /* success */ - iwl_teardown_interface(priv, vif, true); - vif->type = newviftype; - vif->p2p = newp2p; - err = iwl_setup_interface(priv, ctx); - WARN_ON(err); - /* - * We've switched internally, but submitting to the - * device may have failed for some reason. Mask this - * error, because otherwise mac80211 will not switch - * (and set the interface type back) and we'll be - * out of sync with it. - */ - err = 0; - - out: - mutex_unlock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return err; -} - -static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int ret; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (req->n_channels == 0) - return -EINVAL; - - mutex_lock(&priv->mutex); - - /* - * If an internal scan is in progress, just set - * up the scan_request as per above. - */ - if (priv->scan_type != IWL_SCAN_NORMAL) { - IWL_DEBUG_SCAN(priv, - "SCAN request during internal scan - defer\n"); - priv->scan_request = req; - priv->scan_vif = vif; - ret = 0; - } else { - priv->scan_request = req; - priv->scan_vif = vif; - /* - * mac80211 will only ask for one band at a time - * so using channels[0] here is ok - */ - ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, - req->channels[0]->band); - if (ret) { - priv->scan_request = NULL; - priv->scan_vif = NULL; - } - } - - IWL_DEBUG_MAC80211(priv, "leave\n"); - - mutex_unlock(&priv->mutex); - - return ret; -} - -static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) -{ - struct iwl_addsta_cmd cmd = { - .mode = STA_CONTROL_MODIFY_MSK, - .station_flags_msk = STA_FLG_PWR_SAVE_MSK, - .sta.sta_id = sta_id, - }; - - iwl_send_add_sta(priv, &cmd, CMD_ASYNC); -} - -static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - int sta_id; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - switch (cmd) { - case STA_NOTIFY_SLEEP: - WARN_ON(!sta_priv->client); - sta_priv->asleep = true; - if (atomic_read(&sta_priv->pending_frames) > 0) - ieee80211_sta_block_awake(hw, sta, true); - break; - case STA_NOTIFY_AWAKE: - WARN_ON(!sta_priv->client); - if (!sta_priv->asleep) - break; - sta_priv->asleep = false; - sta_id = iwl_sta_id(sta); - if (sta_id != IWL_INVALID_STATION) - iwl_sta_modify_ps_wake(priv, sta_id); - break; - default: - break; - } - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -struct ieee80211_ops iwlagn_hw_ops = { - .tx = iwlagn_mac_tx, - .start = iwlagn_mac_start, - .stop = iwlagn_mac_stop, -#ifdef CONFIG_PM_SLEEP - .suspend = iwlagn_mac_suspend, - .resume = iwlagn_mac_resume, - .set_wakeup = iwlagn_mac_set_wakeup, -#endif - .add_interface = iwlagn_mac_add_interface, - .remove_interface = iwlagn_mac_remove_interface, - .change_interface = iwlagn_mac_change_interface, - .config = iwlagn_mac_config, - .configure_filter = iwlagn_configure_filter, - .set_key = iwlagn_mac_set_key, - .update_tkip_key = iwlagn_mac_update_tkip_key, - .set_rekey_data = iwlagn_mac_set_rekey_data, - .conf_tx = iwlagn_mac_conf_tx, - .bss_info_changed = iwlagn_bss_info_changed, - .ampdu_action = iwlagn_mac_ampdu_action, - .hw_scan = iwlagn_mac_hw_scan, - .sta_notify = iwlagn_mac_sta_notify, - .sta_state = iwlagn_mac_sta_state, - .channel_switch = iwlagn_mac_channel_switch, - .flush = iwlagn_mac_flush, - .tx_last_beacon = iwlagn_mac_tx_last_beacon, - .remain_on_channel = iwlagn_mac_remain_on_channel, - .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, - .rssi_callback = iwlagn_mac_rssi_callback, - CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd) - CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump) - .set_tim = iwlagn_mac_set_tim, -}; - -/* This function both allocates and initializes hw and priv. */ -struct ieee80211_hw *iwl_alloc_all(void) -{ - struct iwl_priv *priv; - struct iwl_op_mode *op_mode; - /* mac80211 allocates memory for this device instance, including - * space for this driver's private structure */ - struct ieee80211_hw *hw; - - hw = ieee80211_alloc_hw(sizeof(struct iwl_priv) + - sizeof(struct iwl_op_mode), &iwlagn_hw_ops); - if (!hw) - goto out; - - op_mode = hw->priv; - priv = IWL_OP_MODE_GET_DVM(op_mode); - priv->hw = hw; - -out: - return hw; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c deleted file mode 100644 index 544ddf17f5bd..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ /dev/null @@ -1,390 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - - -#include -#include -#include -#include - -#include - -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-agn.h" -#include "iwl-io.h" -#include "iwl-commands.h" -#include "iwl-debug.h" -#include "iwl-power.h" -#include "iwl-trans.h" -#include "iwl-modparams.h" - -/* - * Setting power level allows the card to go to sleep when not busy. - * - * We calculate a sleep command based on the required latency, which - * we get from mac80211. In order to handle thermal throttling, we can - * also use pre-defined power levels. - */ - -/* - * This defines the old power levels. They are still used by default - * (level 1) and for thermal throttle (levels 3 through 5) - */ - -struct iwl_power_vec_entry { - struct iwl_powertable_cmd cmd; - u8 no_dtim; /* number of skip dtim */ -}; - -#define IWL_DTIM_RANGE_0_MAX 2 -#define IWL_DTIM_RANGE_1_MAX 10 - -#define NOSLP cpu_to_le16(0), 0, 0 -#define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 -#define ASLP (IWL_POWER_POWER_SAVE_ENA_MSK | \ - IWL_POWER_POWER_MANAGEMENT_ENA_MSK | \ - IWL_POWER_ADVANCE_PM_ENA_MSK) -#define ASLP_TOUT(T) cpu_to_le32(T) -#define TU_TO_USEC 1024 -#define SLP_TOUT(T) cpu_to_le32((T) * TU_TO_USEC) -#define SLP_VEC(X0, X1, X2, X3, X4) {cpu_to_le32(X0), \ - cpu_to_le32(X1), \ - cpu_to_le32(X2), \ - cpu_to_le32(X3), \ - cpu_to_le32(X4)} -/* default power management (not Tx power) table values */ -/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ -/* DTIM 0 - 2 */ -static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} -}; - - -/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ -/* DTIM 3 - 10 */ -static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} -}; - -/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ -/* DTIM 11 - */ -static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} -}; - -/* advance power management */ -/* DTIM 0 - 2 */ -static const struct iwl_power_vec_entry apm_range_0[IWL_POWER_NUM] = { - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} -}; - - -/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ -/* DTIM 3 - 10 */ -static const struct iwl_power_vec_entry apm_range_1[IWL_POWER_NUM] = { - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 6, 8, 0xFF), 0}, 2} -}; - -/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ -/* DTIM 11 - */ -static const struct iwl_power_vec_entry apm_range_2[IWL_POWER_NUM] = { - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 4, 6, 0xFF), 0}, 0}, - {{ASLP, 0, 0, ASLP_TOUT(50), ASLP_TOUT(50), - SLP_VEC(1, 2, 6, 8, 0xFF), ASLP_TOUT(2)}, 2} -}; - -static void iwl_static_sleep_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd, - enum iwl_power_level lvl, int period) -{ - const struct iwl_power_vec_entry *table; - int max_sleep[IWL_POWER_VEC_SIZE] = { 0 }; - int i; - u8 skip; - u32 slp_itrvl; - - if (priv->cfg->adv_pm) { - table = apm_range_2; - if (period <= IWL_DTIM_RANGE_1_MAX) - table = apm_range_1; - if (period <= IWL_DTIM_RANGE_0_MAX) - table = apm_range_0; - } else { - table = range_2; - if (period <= IWL_DTIM_RANGE_1_MAX) - table = range_1; - if (period <= IWL_DTIM_RANGE_0_MAX) - table = range_0; - } - - if (WARN_ON(lvl < 0 || lvl >= IWL_POWER_NUM)) - memset(cmd, 0, sizeof(*cmd)); - else - *cmd = table[lvl].cmd; - - if (period == 0) { - skip = 0; - period = 1; - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - max_sleep[i] = 1; - - } else { - skip = table[lvl].no_dtim; - for (i = 0; i < IWL_POWER_VEC_SIZE; i++) - max_sleep[i] = le32_to_cpu(cmd->sleep_interval[i]); - max_sleep[IWL_POWER_VEC_SIZE - 1] = skip + 1; - } - - slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); - /* figure out the listen interval based on dtim period and skip */ - if (slp_itrvl == 0xFF) - cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = - cpu_to_le32(period * (skip + 1)); - - slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); - if (slp_itrvl > period) - cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = - cpu_to_le32((slp_itrvl / period) * period); - - if (skip) - cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; - else - cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; - - if (priv->cfg->base_params->shadow_reg_enable) - cmd->flags |= IWL_POWER_SHADOW_REG_ENA; - else - cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; - - if (iwl_advanced_bt_coexist(priv)) { - if (!priv->cfg->bt_params->bt_sco_disable) - cmd->flags |= IWL_POWER_BT_SCO_ENA; - else - cmd->flags &= ~IWL_POWER_BT_SCO_ENA; - } - - - slp_itrvl = le32_to_cpu(cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1]); - if (slp_itrvl > IWL_CONN_MAX_LISTEN_INTERVAL) - cmd->sleep_interval[IWL_POWER_VEC_SIZE - 1] = - cpu_to_le32(IWL_CONN_MAX_LISTEN_INTERVAL); - - /* enforce max sleep interval */ - for (i = IWL_POWER_VEC_SIZE - 1; i >= 0 ; i--) { - if (le32_to_cpu(cmd->sleep_interval[i]) > - (max_sleep[i] * period)) - cmd->sleep_interval[i] = - cpu_to_le32(max_sleep[i] * period); - if (i != (IWL_POWER_VEC_SIZE - 1)) { - if (le32_to_cpu(cmd->sleep_interval[i]) > - le32_to_cpu(cmd->sleep_interval[i+1])) - cmd->sleep_interval[i] = - cmd->sleep_interval[i+1]; - } - } - - if (priv->power_data.bus_pm) - cmd->flags |= IWL_POWER_PCI_PM_MSK; - else - cmd->flags &= ~IWL_POWER_PCI_PM_MSK; - - IWL_DEBUG_POWER(priv, "numSkipDtim = %u, dtimPeriod = %d\n", - skip, period); - /* The power level here is 0-4 (used as array index), but user expects - to see 1-5 (according to spec). */ - IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); -} - -static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd) -{ - memset(cmd, 0, sizeof(*cmd)); - - if (priv->power_data.bus_pm) - cmd->flags |= IWL_POWER_PCI_PM_MSK; - - IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); -} - -static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) -{ - IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); - IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); - IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); - IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); - IWL_DEBUG_POWER(priv, "Sleep interval vector = { %d , %d , %d , %d , %d }\n", - le32_to_cpu(cmd->sleep_interval[0]), - le32_to_cpu(cmd->sleep_interval[1]), - le32_to_cpu(cmd->sleep_interval[2]), - le32_to_cpu(cmd->sleep_interval[3]), - le32_to_cpu(cmd->sleep_interval[4])); - - return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC, - sizeof(struct iwl_powertable_cmd), cmd); -} - -static void iwl_power_build_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd) -{ - bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS; - int dtimper; - - dtimper = priv->hw->conf.ps_dtim_period ?: 1; - - if (priv->wowlan) - iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); - else if (!priv->cfg->base_params->no_idle_support && - priv->hw->conf.flags & IEEE80211_CONF_IDLE) - iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); - else if (iwl_tt_is_low_power_state(priv)) { - /* in thermal throttling low power state */ - iwl_static_sleep_cmd(priv, cmd, - iwl_tt_current_power_mode(priv), dtimper); - } else if (!enabled) - iwl_power_sleep_cam_cmd(priv, cmd); - else if (priv->power_data.debug_sleep_level_override >= 0) - iwl_static_sleep_cmd(priv, cmd, - priv->power_data.debug_sleep_level_override, - dtimper); - else { - /* Note that the user parameter is 1-5 (according to spec), - but we pass 0-4 because it acts as an array index. */ - if (iwlwifi_mod_params.power_level > IWL_POWER_INDEX_1 && - iwlwifi_mod_params.power_level <= IWL_POWER_NUM) - iwl_static_sleep_cmd(priv, cmd, - iwlwifi_mod_params.power_level - 1, dtimper); - else - iwl_static_sleep_cmd(priv, cmd, - IWL_POWER_INDEX_1, dtimper); - } -} - -int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, - bool force) -{ - int ret; - bool update_chains; - - lockdep_assert_held(&priv->mutex); - - /* Don't update the RX chain when chain noise calibration is running */ - update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || - priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; - - if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) - return 0; - - if (!iwl_is_ready_rf(priv)) - return -EIO; - - /* scan complete use sleep_power_next, need to be updated */ - memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); - if (test_bit(STATUS_SCANNING, &priv->status) && !force) { - IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); - return 0; - } - - if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) - iwl_dvm_set_pmi(priv, true); - - ret = iwl_set_power(priv, cmd); - if (!ret) { - if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) - iwl_dvm_set_pmi(priv, false); - - if (update_chains) - iwl_update_chain_flags(priv); - else - IWL_DEBUG_POWER(priv, - "Cannot update the power, chain noise " - "calibration running: %d\n", - priv->chain_noise_data.state); - - memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); - } else - IWL_ERR(priv, "set power fail, ret = %d", ret); - - return ret; -} - -int iwl_power_update_mode(struct iwl_priv *priv, bool force) -{ - struct iwl_powertable_cmd cmd; - - iwl_power_build_cmd(priv, &cmd); - return iwl_power_set_mode(priv, &cmd, force); -} - -/* initialize to default */ -void iwl_power_initialize(struct iwl_priv *priv) -{ - priv->power_data.bus_pm = priv->trans->pm_support; - - priv->power_data.debug_sleep_level_override = -1; - - memset(&priv->power_data.sleep_cmd, 0, - sizeof(priv->power_data.sleep_cmd)); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h deleted file mode 100644 index 21afc92efacb..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2007 - 2012 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#ifndef __iwl_power_setting_h__ -#define __iwl_power_setting_h__ - -#include "iwl-commands.h" - -struct iwl_power_mgr { - struct iwl_powertable_cmd sleep_cmd; - struct iwl_powertable_cmd sleep_cmd_next; - int debug_sleep_level_override; - bool bus_pm; -}; - -int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, - bool force); -int iwl_power_update_mode(struct iwl_priv *priv, bool force); -void iwl_power_initialize(struct iwl_priv *priv); - -extern bool no_sleep_autoadjust; - -#endif /* __iwl_power_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c deleted file mode 100644 index a32b35ea9e60..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ /dev/null @@ -1,1171 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ -#include -#include -#include -#include - -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn.h" -#include "iwl-trans.h" - -/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after - * sending probe req. This should be set long enough to hear probe responses - * from more than one AP. */ -#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ -#define IWL_ACTIVE_DWELL_TIME_52 (20) - -#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) -#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) - -/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. - * Must be set longer than active dwell time. - * For the most reliable scan, set > AP beacon interval (typically 100msec). */ -#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ -#define IWL_PASSIVE_DWELL_TIME_52 (10) -#define IWL_PASSIVE_DWELL_BASE (100) -#define IWL_CHANNEL_TUNE_TIME 5 -#define MAX_SCAN_CHANNEL 50 - -static int iwl_send_scan_abort(struct iwl_priv *priv) -{ - int ret; - struct iwl_host_cmd cmd = { - .id = REPLY_SCAN_ABORT_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, - }; - __le32 *status; - - /* Exit instantly with error when device is not ready - * to receive scan abort command or it does not perform - * hardware scan currently */ - if (!test_bit(STATUS_READY, &priv->status) || - !test_bit(STATUS_GEO_CONFIGURED, &priv->status) || - !test_bit(STATUS_SCAN_HW, &priv->status) || - test_bit(STATUS_FW_ERROR, &priv->status)) - return -EIO; - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) - return ret; - - status = (void *)cmd.resp_pkt->data; - if (*status != CAN_ABORT_STATUS) { - /* The scan abort will return 1 for success or - * 2 for "failure". A failure condition can be - * due to simply not being in an active scan which - * can occur if we send the scan abort before we - * the microcode has notified us that a scan is - * completed. */ - IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", - le32_to_cpu(*status)); - ret = -EIO; - } - - iwl_free_resp(&cmd); - return ret; -} - -static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) -{ - /* check if scan was requested from mac80211 */ - if (priv->scan_request) { - IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); - ieee80211_scan_completed(priv->hw, aborted); - } - - if (priv->scan_type == IWL_SCAN_ROC) - iwl_scan_roc_expired(priv); - - priv->scan_type = IWL_SCAN_NORMAL; - priv->scan_vif = NULL; - priv->scan_request = NULL; -} - -static void iwl_process_scan_complete(struct iwl_priv *priv) -{ - bool aborted; - - lockdep_assert_held(&priv->mutex); - - if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->status)) - return; - - IWL_DEBUG_SCAN(priv, "Completed scan.\n"); - - cancel_delayed_work(&priv->scan_check); - - aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); - if (aborted) - IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); - - if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); - goto out_settings; - } - - if (priv->scan_type == IWL_SCAN_ROC) - iwl_scan_roc_expired(priv); - - if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { - int err; - - /* Check if mac80211 requested scan during our internal scan */ - if (priv->scan_request == NULL) - goto out_complete; - - /* If so request a new scan */ - err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, - priv->scan_request->channels[0]->band); - if (err) { - IWL_DEBUG_SCAN(priv, - "failed to initiate pending scan: %d\n", err); - aborted = true; - goto out_complete; - } - - return; - } - -out_complete: - iwl_complete_scan(priv, aborted); - -out_settings: - /* Can we still talk to firmware ? */ - if (!iwl_is_ready_rf(priv)) - return; - - iwlagn_post_scan(priv); -} - -void iwl_force_scan_end(struct iwl_priv *priv) -{ - lockdep_assert_held(&priv->mutex); - - if (!test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); - return; - } - - IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); - clear_bit(STATUS_SCANNING, &priv->status); - clear_bit(STATUS_SCAN_HW, &priv->status); - clear_bit(STATUS_SCAN_ABORTING, &priv->status); - clear_bit(STATUS_SCAN_COMPLETE, &priv->status); - iwl_complete_scan(priv, true); -} - -static void iwl_do_scan_abort(struct iwl_priv *priv) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - if (!test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); - return; - } - - if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); - return; - } - - ret = iwl_send_scan_abort(priv); - if (ret) { - IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); - iwl_force_scan_end(priv); - } else - IWL_DEBUG_SCAN(priv, "Successfully send scan abort\n"); -} - -/** - * iwl_scan_cancel - Cancel any currently executing HW scan - */ -int iwl_scan_cancel(struct iwl_priv *priv) -{ - IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); - queue_work(priv->workqueue, &priv->abort_scan); - return 0; -} - -/** - * iwl_scan_cancel_timeout - Cancel any currently executing HW scan - * @ms: amount of time to wait (in milliseconds) for scan to abort - * - */ -void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) -{ - unsigned long timeout = jiffies + msecs_to_jiffies(ms); - - lockdep_assert_held(&priv->mutex); - - IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); - - iwl_do_scan_abort(priv); - - while (time_before_eq(jiffies, timeout)) { - if (!test_bit(STATUS_SCAN_HW, &priv->status)) - goto finished; - msleep(20); - } - - return; - - finished: - /* - * Now STATUS_SCAN_HW is clear. This means that the - * device finished, but the background work is going - * to execute at best as soon as we release the mutex. - * Since we need to be able to issue a new scan right - * after this function returns, run the complete here. - * The STATUS_SCAN_COMPLETE bit will then be cleared - * and prevent the background work from "completing" - * a possible new scan. - */ - iwl_process_scan_complete(priv); -} - -/* Service response to REPLY_SCAN_CMD (0x80) */ -static int iwl_rx_reply_scan(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scanreq_notification *notif = (void *)pkt->data; - - IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); -#endif - return 0; -} - -/* Service SCAN_START_NOTIFICATION (0x82) */ -static int iwl_rx_scan_start_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scanstart_notification *notif = (void *)pkt->data; - - priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); - IWL_DEBUG_SCAN(priv, "Scan start: " - "%d [802.11%s] " - "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", - notif->channel, - notif->band ? "bg" : "a", - le32_to_cpu(notif->tsf_high), - le32_to_cpu(notif->tsf_low), - notif->status, notif->beacon_timer); - - if (priv->scan_type == IWL_SCAN_ROC && - !priv->hw_roc_start_notified) { - ieee80211_ready_on_channel(priv->hw); - priv->hw_roc_start_notified = true; - } - - return 0; -} - -/* Service SCAN_RESULTS_NOTIFICATION (0x83) */ -static int iwl_rx_scan_results_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scanresults_notification *notif = (void *)pkt->data; - - IWL_DEBUG_SCAN(priv, "Scan ch.res: " - "%d [802.11%s] " - "probe status: %u:%u " - "(TSF: 0x%08X:%08X) - %d " - "elapsed=%lu usec\n", - notif->channel, - notif->band ? "bg" : "a", - notif->probe_status, notif->num_probe_not_sent, - le32_to_cpu(notif->tsf_high), - le32_to_cpu(notif->tsf_low), - le32_to_cpu(notif->statistics[0]), - le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); -#endif - return 0; -} - -/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ -static int iwl_rx_scan_complete_notif(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb, - struct iwl_device_cmd *cmd) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_scancomplete_notification *scan_notif = (void *)pkt->data; - - IWL_DEBUG_SCAN(priv, "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", - scan_notif->scanned_channels, - scan_notif->tsf_low, - scan_notif->tsf_high, scan_notif->status); - - IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", - (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", - jiffies_to_msecs(jiffies - priv->scan_start)); - - /* - * When aborting, we run the scan completed background work inline - * and the background work must then do nothing. The SCAN_COMPLETE - * bit helps implement that logic and thus needs to be set before - * queueing the work. Also, since the scan abort waits for SCAN_HW - * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW - * to avoid a race there. - */ - set_bit(STATUS_SCAN_COMPLETE, &priv->status); - clear_bit(STATUS_SCAN_HW, &priv->status); - queue_work(priv->workqueue, &priv->scan_completed); - - if (priv->iw_mode != NL80211_IFTYPE_ADHOC && - iwl_advanced_bt_coexist(priv) && - priv->bt_status != scan_notif->bt_status) { - if (scan_notif->bt_status) { - /* BT on */ - if (!priv->bt_ch_announce) - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - /* - * otherwise, no traffic load information provided - * no changes made - */ - } else { - /* BT off */ - priv->bt_traffic_load = - IWL_BT_COEX_TRAFFIC_LOAD_NONE; - } - priv->bt_status = scan_notif->bt_status; - queue_work(priv->workqueue, - &priv->bt_traffic_change_work); - } - return 0; -} - -void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) -{ - /* scan handlers */ - priv->rx_handlers[REPLY_SCAN_CMD] = iwl_rx_reply_scan; - priv->rx_handlers[SCAN_START_NOTIFICATION] = iwl_rx_scan_start_notif; - priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = - iwl_rx_scan_results_notif; - priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = - iwl_rx_scan_complete_notif; -} - -static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band, u8 n_probes) -{ - if (band == IEEE80211_BAND_5GHZ) - return IWL_ACTIVE_DWELL_TIME_52 + - IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); - else - return IWL_ACTIVE_DWELL_TIME_24 + - IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); -} - -static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) -{ - struct iwl_rxon_context *ctx; - - /* - * If we're associated, we clamp the dwell time 98% - * of the smallest beacon interval (minus 2 * channel - * tune time) - */ - for_each_context(priv, ctx) { - u16 value; - - switch (ctx->staging.dev_type) { - case RXON_DEV_TYPE_P2P: - /* no timing constraints */ - continue; - case RXON_DEV_TYPE_ESS: - default: - /* timing constraints if associated */ - if (!iwl_is_associated_ctx(ctx)) - continue; - break; - case RXON_DEV_TYPE_CP: - case RXON_DEV_TYPE_2STA: - /* - * These seem to always have timers for TBTT - * active in uCode even when not associated yet. - */ - break; - } - - value = ctx->beacon_int; - if (!value) - value = IWL_PASSIVE_DWELL_BASE; - value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; - dwell_time = min(value, dwell_time); - } - - return dwell_time; -} - -static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band) -{ - u16 passive = (band == IEEE80211_BAND_2GHZ) ? - IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : - IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; - - return iwl_limit_dwell(priv, passive); -} - -/* Return valid, unused, channel for a passive scan to reset the RF */ -static u8 iwl_get_single_channel_number(struct iwl_priv *priv, - enum ieee80211_band band) -{ - struct ieee80211_supported_band *sband = priv->hw->wiphy->bands[band]; - struct iwl_rxon_context *ctx; - int i; - - for (i = 0; i < sband->n_channels; i++) { - bool busy = false; - - for_each_context(priv, ctx) { - busy = sband->channels[i].hw_value == - le16_to_cpu(ctx->staging.channel); - if (busy) - break; - } - - if (busy) - continue; - - if (!(sband->channels[i].flags & IEEE80211_CHAN_DISABLED)) - return sband->channels[i].hw_value; - } - - return 0; -} - -static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - struct iwl_scan_channel *scan_ch) -{ - const struct ieee80211_supported_band *sband; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added = 0; - u16 channel = 0; - - sband = iwl_get_hw_mode(priv, band); - if (!sband) { - IWL_ERR(priv, "invalid band\n"); - return added; - } - - active_dwell = iwl_get_active_dwell_time(priv, band, 0); - passive_dwell = iwl_get_passive_dwell_time(priv, band); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - channel = iwl_get_single_channel_number(priv, band); - if (channel) { - scan_ch->channel = cpu_to_le16(channel); - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - added++; - } else - IWL_ERR(priv, "no valid channel found\n"); - return added; -} - -static int iwl_get_channels_for_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - u8 is_active, u8 n_probes, - struct iwl_scan_channel *scan_ch) -{ - struct ieee80211_channel *chan; - const struct ieee80211_supported_band *sband; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added, i; - u16 channel; - - sband = iwl_get_hw_mode(priv, band); - if (!sband) - return 0; - - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); - passive_dwell = iwl_get_passive_dwell_time(priv, band); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { - chan = priv->scan_request->channels[i]; - - if (chan->band != band) - continue; - - channel = chan->hw_value; - scan_ch->channel = cpu_to_le16(channel); - - if (!is_active || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - else - scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; - - if (n_probes) - scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); - - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - - IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", - channel, le32_to_cpu(scan_ch->type), - (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? - "ACTIVE" : "PASSIVE", - (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? - active_dwell : passive_dwell); - - scan_ch++; - added++; - } - - IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); - return added; -} - -/** - * iwl_fill_probe_req - fill in all required fields and IE for probe request - */ - -static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, - const u8 *ies, int ie_len, const u8 *ssid, - u8 ssid_len, int left) -{ - int len = 0; - u8 *pos = NULL; - - /* Make sure there is enough space for the probe request, - * two mandatory IEs and the data */ - left -= 24; - if (left < 0) - return 0; - - frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - memcpy(frame->da, iwl_bcast_addr, ETH_ALEN); - memcpy(frame->sa, ta, ETH_ALEN); - memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN); - frame->seq_ctrl = 0; - - len += 24; - - /* ...next IE... */ - pos = &frame->u.probe_req.variable[0]; - - /* fill in our SSID IE */ - left -= ssid_len + 2; - if (left < 0) - return 0; - *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - if (ssid && ssid_len) { - memcpy(pos, ssid, ssid_len); - pos += ssid_len; - } - - len += ssid_len + 2; - - if (WARN_ON(left < ie_len)) - return len; - - if (ies && ie_len) { - memcpy(pos, ies, ie_len); - len += ie_len; - } - - return (u16)len; -} - -static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_SCAN_CMD, - .len = { sizeof(struct iwl_scan_cmd), }, - .flags = CMD_SYNC, - }; - struct iwl_scan_cmd *scan; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - u32 rate_flags = 0; - u16 cmd_len = 0; - u16 rx_chain = 0; - enum ieee80211_band band; - u8 n_probes = 0; - u8 rx_ant = priv->hw_params.valid_rx_ant; - u8 rate; - bool is_active = false; - int chan_mod; - u8 active_chains; - u8 scan_tx_antennas = priv->hw_params.valid_tx_ant; - int ret; - int scan_cmd_size = sizeof(struct iwl_scan_cmd) + - MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + - priv->fw->ucode_capa.max_probe_length; - const u8 *ssid = NULL; - u8 ssid_len = 0; - - if (WARN_ON_ONCE(priv->scan_request && - priv->scan_request->n_channels > MAX_SCAN_CHANNEL)) - return -EINVAL; - - lockdep_assert_held(&priv->mutex); - - if (vif) - ctx = iwl_rxon_ctx_from_vif(vif); - - if (!priv->scan_cmd) { - priv->scan_cmd = kmalloc(scan_cmd_size, GFP_KERNEL); - if (!priv->scan_cmd) { - IWL_DEBUG_SCAN(priv, - "fail to allocate memory for scan\n"); - return -ENOMEM; - } - } - scan = priv->scan_cmd; - memset(scan, 0, scan_cmd_size); - - scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; - scan->quiet_time = IWL_ACTIVE_QUIET_TIME; - - if (priv->scan_type != IWL_SCAN_ROC && - iwl_is_any_associated(priv)) { - u16 interval = 0; - u32 extra; - u32 suspend_time = 100; - u32 scan_suspend_time = 100; - - IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); - switch (priv->scan_type) { - case IWL_SCAN_ROC: - WARN_ON(1); - break; - case IWL_SCAN_RADIO_RESET: - interval = 0; - break; - case IWL_SCAN_NORMAL: - interval = vif->bss_conf.beacon_int; - break; - } - - scan->suspend_time = 0; - scan->max_out_time = cpu_to_le32(200 * 1024); - if (!interval) - interval = suspend_time; - - extra = (suspend_time / interval) << 22; - scan_suspend_time = (extra | - ((suspend_time % interval) * 1024)); - scan->suspend_time = cpu_to_le32(scan_suspend_time); - IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", - scan_suspend_time, interval); - } else if (priv->scan_type == IWL_SCAN_ROC) { - scan->suspend_time = 0; - scan->max_out_time = 0; - scan->quiet_time = 0; - scan->quiet_plcp_th = 0; - } - - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); - break; - case IWL_SCAN_NORMAL: - if (priv->scan_request->n_ssids) { - int i, p = 0; - IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); - /* - * The highest priority SSID is inserted to the - * probe request template. - */ - ssid_len = priv->scan_request->ssids[0].ssid_len; - ssid = priv->scan_request->ssids[0].ssid; - - /* - * Invert the order of ssids, the firmware will invert - * it back. - */ - for (i = priv->scan_request->n_ssids - 1; i >= 1; i--) { - scan->direct_scan[p].id = WLAN_EID_SSID; - scan->direct_scan[p].len = - priv->scan_request->ssids[i].ssid_len; - memcpy(scan->direct_scan[p].ssid, - priv->scan_request->ssids[i].ssid, - priv->scan_request->ssids[i].ssid_len); - n_probes++; - p++; - } - is_active = true; - } else - IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); - break; - case IWL_SCAN_ROC: - IWL_DEBUG_SCAN(priv, "Start ROC scan.\n"); - break; - } - - scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; - scan->tx_cmd.sta_id = ctx->bcast_sta_id; - scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - switch (priv->scan_band) { - case IEEE80211_BAND_2GHZ: - scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; - chan_mod = le32_to_cpu( - priv->contexts[IWL_RXON_CTX_BSS].active.flags & - RXON_FLG_CHANNEL_MODE_MSK) - >> RXON_FLG_CHANNEL_MODE_POS; - if ((priv->scan_request && priv->scan_request->no_cck) || - chan_mod == CHANNEL_MODE_PURE_40) { - rate = IWL_RATE_6M_PLCP; - } else { - rate = IWL_RATE_1M_PLCP; - rate_flags = RATE_MCS_CCK_MSK; - } - /* - * Internal scans are passive, so we can indiscriminately set - * the BT ignore flag on 2.4 GHz since it applies to TX only. - */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) - scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; - break; - case IEEE80211_BAND_5GHZ: - rate = IWL_RATE_6M_PLCP; - break; - default: - IWL_WARN(priv, "Invalid scan band\n"); - return -EIO; - } - - /* - * If active scanning is requested but a certain channel is - * marked passive, we can do active scanning if we detect - * transmissions. - * - * There is an issue with some firmware versions that triggers - * a sysassert on a "good CRC threshold" of zero (== disabled), - * on a radar channel even though this means that we should NOT - * send probes. - * - * The "good CRC threshold" is the number of frames that we - * need to receive during our dwell time on a channel before - * sending out probes -- setting this to a huge value will - * mean we never reach it, but at the same time work around - * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER - * here instead of IWL_GOOD_CRC_TH_DISABLED. - * - * This was fixed in later versions along with some other - * scan changes, and the threshold behaves as a flag in those - * versions. - */ - if (priv->new_scan_threshold_behaviour) - scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_DISABLED; - else - scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_NEVER; - - band = priv->scan_band; - - if (band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - /* transmit 2.4 GHz probes only on first antenna */ - scan_tx_antennas = first_antenna(scan_tx_antennas); - } - - priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, - priv->scan_tx_ant[band], - scan_tx_antennas); - rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); - scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); - - /* - * In power save mode while associated use one chain, - * otherwise use all chains - */ - if (test_bit(STATUS_POWER_PMI, &priv->status) && - !(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { - /* rx_ant has been set to all valid chains previously */ - active_chains = rx_ant & - ((u8)(priv->chain_noise_data.active_chains)); - if (!active_chains) - active_chains = rx_ant; - - IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", - priv->chain_noise_data.active_chains); - - rx_ant = first_antenna(active_chains); - } - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && - priv->bt_full_concurrent) { - /* operated as 1x1 in full concurrency mode */ - rx_ant = first_antenna(rx_ant); - } - - /* MIMO is not used here, but value is required */ - rx_chain |= - priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; - rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; - scan->rx_chain = cpu_to_le16(rx_chain); - switch (priv->scan_type) { - case IWL_SCAN_NORMAL: - cmd_len = iwl_fill_probe_req( - (struct ieee80211_mgmt *)scan->data, - vif->addr, - priv->scan_request->ie, - priv->scan_request->ie_len, - ssid, ssid_len, - scan_cmd_size - sizeof(*scan)); - break; - case IWL_SCAN_RADIO_RESET: - case IWL_SCAN_ROC: - /* use bcast addr, will not be transmitted but must be valid */ - cmd_len = iwl_fill_probe_req( - (struct ieee80211_mgmt *)scan->data, - iwl_bcast_addr, NULL, 0, - NULL, 0, - scan_cmd_size - sizeof(*scan)); - break; - default: - BUG(); - } - scan->tx_cmd.len = cpu_to_le16(cmd_len); - - scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | - RXON_FILTER_BCON_AWARE_MSK); - - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - scan->channel_count = - iwl_get_single_channel_for_scan(priv, vif, band, - (void *)&scan->data[cmd_len]); - break; - case IWL_SCAN_NORMAL: - scan->channel_count = - iwl_get_channels_for_scan(priv, vif, band, - is_active, n_probes, - (void *)&scan->data[cmd_len]); - break; - case IWL_SCAN_ROC: { - struct iwl_scan_channel *scan_ch; - int n_chan, i; - u16 dwell; - - dwell = iwl_limit_dwell(priv, priv->hw_roc_duration); - n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell); - - scan->channel_count = n_chan; - - scan_ch = (void *)&scan->data[cmd_len]; - - for (i = 0; i < n_chan; i++) { - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->channel = - cpu_to_le16(priv->hw_roc_channel->hw_value); - - if (i == n_chan - 1) - dwell = priv->hw_roc_duration - i * dwell; - - scan_ch->active_dwell = - scan_ch->passive_dwell = cpu_to_le16(dwell); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - - scan_ch++; - } - } - - break; - } - - if (scan->channel_count == 0) { - IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); - return -EIO; - } - - cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + - scan->channel_count * sizeof(struct iwl_scan_channel); - cmd.data[0] = scan; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - scan->len = cpu_to_le16(cmd.len[0]); - - /* set scan bit here for PAN params */ - set_bit(STATUS_SCAN_HW, &priv->status); - - ret = iwlagn_set_pan_params(priv); - if (ret) { - clear_bit(STATUS_SCAN_HW, &priv->status); - return ret; - } - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) { - clear_bit(STATUS_SCAN_HW, &priv->status); - iwlagn_set_pan_params(priv); - } - - return ret; -} - -void iwl_init_scan_params(struct iwl_priv *priv) -{ - u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1; - if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) - priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; - if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) - priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; -} - -int __must_check iwl_scan_initiate(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum iwl_scan_type scan_type, - enum ieee80211_band band) -{ - int ret; - - lockdep_assert_held(&priv->mutex); - - cancel_delayed_work(&priv->scan_check); - - if (!iwl_is_ready_rf(priv)) { - IWL_WARN(priv, "Request scan called when driver not ready.\n"); - return -EIO; - } - - if (test_bit(STATUS_SCAN_HW, &priv->status)) { - IWL_DEBUG_SCAN(priv, - "Multiple concurrent scan requests in parallel.\n"); - return -EBUSY; - } - - if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); - return -EBUSY; - } - - IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", - scan_type == IWL_SCAN_NORMAL ? "" : - scan_type == IWL_SCAN_ROC ? "remain-on-channel " : - "internal short "); - - set_bit(STATUS_SCANNING, &priv->status); - priv->scan_type = scan_type; - priv->scan_start = jiffies; - priv->scan_band = band; - - ret = iwlagn_request_scan(priv, vif); - if (ret) { - clear_bit(STATUS_SCANNING, &priv->status); - priv->scan_type = IWL_SCAN_NORMAL; - return ret; - } - - queue_delayed_work(priv->workqueue, &priv->scan_check, - IWL_SCAN_CHECK_WATCHDOG); - - return 0; -} - - -/* - * internal short scan, this function should only been called while associated. - * It will reset and tune the radio to prevent possible RF related problem - */ -void iwl_internal_short_hw_scan(struct iwl_priv *priv) -{ - queue_work(priv->workqueue, &priv->start_internal_scan); -} - -static void iwl_bg_start_internal_scan(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, start_internal_scan); - - IWL_DEBUG_SCAN(priv, "Start internal scan\n"); - - mutex_lock(&priv->mutex); - - if (priv->scan_type == IWL_SCAN_RADIO_RESET) { - IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); - goto unlock; - } - - if (test_bit(STATUS_SCANNING, &priv->status)) { - IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); - goto unlock; - } - - if (iwl_scan_initiate(priv, NULL, IWL_SCAN_RADIO_RESET, priv->band)) - IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); - unlock: - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_scan_check(struct work_struct *data) -{ - struct iwl_priv *priv = - container_of(data, struct iwl_priv, scan_check.work); - - IWL_DEBUG_SCAN(priv, "Scan check work\n"); - - /* Since we are here firmware does not finish scan and - * most likely is in bad shape, so we don't bother to - * send abort command, just force scan complete to mac80211 */ - mutex_lock(&priv->mutex); - iwl_force_scan_end(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_abort_scan(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); - - IWL_DEBUG_SCAN(priv, "Abort scan work\n"); - - /* We keep scan_check work queued in case when firmware will not - * report back scan completed notification */ - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 200); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_scan_completed(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, scan_completed); - - mutex_lock(&priv->mutex); - iwl_process_scan_complete(priv); - mutex_unlock(&priv->mutex); -} - -void iwl_setup_scan_deferred_work(struct iwl_priv *priv) -{ - INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); - INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); - INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); - INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); -} - -void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) -{ - cancel_work_sync(&priv->start_internal_scan); - cancel_work_sync(&priv->abort_scan); - cancel_work_sync(&priv->scan_completed); - - if (cancel_delayed_work_sync(&priv->scan_check)) { - mutex_lock(&priv->mutex); - iwl_force_scan_end(priv); - mutex_unlock(&priv->mutex); - } -} - -void iwl_scan_roc_expired(struct iwl_priv *priv) -{ - /* - * The status bit should be set here, to prevent a race - * where the atomic_read returns 1, but before the execution continues - * iwl_scan_offchannel_skb_status() checks if the status bit is set - */ - set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); - - if (atomic_read(&priv->num_aux_in_flight) == 0) { - ieee80211_remain_on_channel_expired(priv->hw); - priv->hw_roc_channel = NULL; - schedule_delayed_work(&priv->hw_roc_disable_work, - 10 * HZ); - - clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status); - } else { - IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n", - atomic_read(&priv->num_aux_in_flight)); - } -} - -void iwl_scan_offchannel_skb(struct iwl_priv *priv) -{ - WARN_ON(!priv->hw_roc_start_notified); - atomic_inc(&priv->num_aux_in_flight); -} - -void iwl_scan_offchannel_skb_status(struct iwl_priv *priv) -{ - if (atomic_dec_return(&priv->num_aux_in_flight) == 0 && - test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) { - IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n"); - iwl_scan_roc_expired(priv); - } -} diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.c b/drivers/net/wireless/iwlwifi/iwl-testmode.c deleted file mode 100644 index 060aac3e22f1..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-testmode.c +++ /dev/null @@ -1,1114 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iwl-dev.h" -#include "iwl-debug.h" -#include "iwl-io.h" -#include "iwl-agn.h" -#include "iwl-testmode.h" -#include "iwl-trans.h" -#include "iwl-fh.h" -#include "iwl-prph.h" - - -/* Periphery registers absolute lower bound. This is used in order to - * differentiate registery access through HBUS_TARG_PRPH_* and - * HBUS_TARG_MEM_* accesses. - */ -#define IWL_TM_ABS_PRPH_START (0xA00000) - -/* The TLVs used in the gnl message policy between the kernel module and - * user space application. iwl_testmode_gnl_msg_policy is to be carried - * through the NL80211_CMD_TESTMODE channel regulated by nl80211. - * See iwl-testmode.h - */ -static -struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { - [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, }, - [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, }, - [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, }, - [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, - - [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, }, - [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, - [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, }, -}; - -/* - * See the struct iwl_rx_packet in iwl-commands.h for the format of the - * received events from the device - */ -static inline int get_event_length(struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - if (pkt) - return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - else - return 0; -} - - -/* - * This function multicasts the spontaneous messages from the device to the - * user space. It is invoked whenever there is a received messages - * from the device. This function is called within the ISR of the rx handlers - * in iwlagn driver. - * - * The parsing of the message content is left to the user space application, - * The message content is treated as unattacked raw data and is encapsulated - * with IWL_TM_ATTR_UCODE_RX_PKT multicasting to the user space. - * - * @priv: the instance of iwlwifi device - * @rxb: pointer to rx data content received by the ISR - * - * See the message policies and TLVs in iwl_testmode_gnl_msg_policy[]. - * For the messages multicasting to the user application, the mandatory - * TLV fields are : - * IWL_TM_ATTR_COMMAND must be IWL_TM_CMD_DEV2APP_UCODE_RX_PKT - * IWL_TM_ATTR_UCODE_RX_PKT for carrying the message content - */ - -static void iwl_testmode_ucode_rx_pkt(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) -{ - struct ieee80211_hw *hw = priv->hw; - struct sk_buff *skb; - void *data; - int length; - - data = (void *)rxb_addr(rxb); - length = get_event_length(rxb); - - if (!data || length == 0) - return; - - skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length, - GFP_ATOMIC); - if (skb == NULL) { - IWL_ERR(priv, - "Run out of memory for messages to user space ?\n"); - return; - } - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || - /* the length doesn't include len_n_flags field, so add it manually */ - nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length + sizeof(__le32), data)) - goto nla_put_failure; - cfg80211_testmode_event(skb, GFP_ATOMIC); - return; - -nla_put_failure: - kfree_skb(skb); - IWL_ERR(priv, "Ouch, overran buffer, check allocation!\n"); -} - -void iwl_testmode_init(struct iwl_priv *priv) -{ - priv->pre_rx_handler = NULL; - priv->testmode_trace.trace_enabled = false; - priv->testmode_mem.read_in_progress = false; -} - -static void iwl_mem_cleanup(struct iwl_priv *priv) -{ - if (priv->testmode_mem.read_in_progress) { - kfree(priv->testmode_mem.buff_addr); - priv->testmode_mem.buff_addr = NULL; - priv->testmode_mem.buff_size = 0; - priv->testmode_mem.num_chunks = 0; - priv->testmode_mem.read_in_progress = false; - } -} - -static void iwl_trace_cleanup(struct iwl_priv *priv) -{ - if (priv->testmode_trace.trace_enabled) { - if (priv->testmode_trace.cpu_addr && - priv->testmode_trace.dma_addr) - dma_free_coherent(priv->trans->dev, - priv->testmode_trace.total_size, - priv->testmode_trace.cpu_addr, - priv->testmode_trace.dma_addr); - priv->testmode_trace.trace_enabled = false; - priv->testmode_trace.cpu_addr = NULL; - priv->testmode_trace.trace_addr = NULL; - priv->testmode_trace.dma_addr = 0; - priv->testmode_trace.buff_size = 0; - priv->testmode_trace.total_size = 0; - } -} - - -void iwl_testmode_cleanup(struct iwl_priv *priv) -{ - iwl_trace_cleanup(priv); - iwl_mem_cleanup(priv); -} - - -/* - * This function handles the user application commands to the ucode. - * - * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_CMD_ID and - * IWL_TM_ATTR_UCODE_CMD_DATA and calls to the handler to send the - * host command to the ucode. - * - * If any mandatory field is missing, -ENOMSG is replied to the user space - * application; otherwise, waits for the host command to be sent and checks - * the return code. In case or error, it is returned, otherwise a reply is - * allocated and the reply RX packet - * is returned. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_host_cmd cmd; - struct iwl_rx_packet *pkt; - struct sk_buff *skb; - void *reply_buf; - u32 reply_len; - int ret; - bool cmd_want_skb; - - memset(&cmd, 0, sizeof(struct iwl_host_cmd)); - - if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] || - !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) { - IWL_ERR(priv, "Missing ucode command mandatory fields\n"); - return -ENOMSG; - } - - cmd.flags = CMD_ON_DEMAND | CMD_SYNC; - cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]); - if (cmd_want_skb) - cmd.flags |= CMD_WANT_SKB; - - cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]); - cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); - cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - IWL_DEBUG_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x," - " len %d\n", cmd.id, cmd.flags, cmd.len[0]); - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) { - IWL_ERR(priv, "Failed to send hcmd\n"); - return ret; - } - if (!cmd_want_skb) - return ret; - - /* Handling return of SKB to the user */ - pkt = cmd.resp_pkt; - if (!pkt) { - IWL_ERR(priv, "HCMD received a null response packet\n"); - return ret; - } - - reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20); - reply_buf = kmalloc(reply_len, GFP_KERNEL); - if (!skb || !reply_buf) { - kfree_skb(skb); - kfree(reply_buf); - return -ENOMEM; - } - - /* The reply is in a page, that we cannot send to user space. */ - memcpy(reply_buf, &(pkt->hdr), reply_len); - iwl_free_resp(&cmd); - - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || - nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) - goto nla_put_failure; - return cfg80211_testmode_reply(skb); - -nla_put_failure: - IWL_DEBUG_INFO(priv, "Failed creating NL attributes\n"); - return -ENOMSG; -} - - -/* - * This function handles the user application commands for register access. - * - * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the - * handlers respectively. - * - * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the - * mandatory fields(IWL_TM_ATTR_REG_OFFSET,IWL_TM_ATTR_REG_VALUE32, - * IWL_TM_ATTR_REG_VALUE8) are missing; Otherwise 0 is replied indicating - * the success of the command execution. - * - * If IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_READ32, the register read - * value is returned with IWL_TM_ATTR_REG_VALUE32. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u32 ofs, val32, cmd; - u8 val8; - struct sk_buff *skb; - int status = 0; - - if (!tb[IWL_TM_ATTR_REG_OFFSET]) { - IWL_ERR(priv, "Missing register offset\n"); - return -ENOMSG; - } - ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]); - IWL_INFO(priv, "testmode register access command offset 0x%x\n", ofs); - - /* Allow access only to FH/CSR/HBUS in direct mode. - Since we don't have the upper bounds for the CSR and HBUS segments, - we will use only the upper bound of FH for sanity check. */ - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - if ((cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 || - cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 || - cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8) && - (ofs >= FH_MEM_UPPER_BOUND)) { - IWL_ERR(priv, "offset out of segment (0x0 - 0x%x)\n", - FH_MEM_UPPER_BOUND); - return -EINVAL; - } - - switch (cmd) { - case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - val32 = iwl_read_direct32(priv->trans, ofs); - IWL_INFO(priv, "32bit value to read 0x%x\n", val32); - - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - if (!tb[IWL_TM_ATTR_REG_VALUE32]) { - IWL_ERR(priv, "Missing value to write\n"); - return -ENOMSG; - } else { - val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); - IWL_INFO(priv, "32bit value to write 0x%x\n", val32); - iwl_write_direct32(priv->trans, ofs, val32); - } - break; - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - if (!tb[IWL_TM_ATTR_REG_VALUE8]) { - IWL_ERR(priv, "Missing value to write\n"); - return -ENOMSG; - } else { - val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]); - IWL_INFO(priv, "8bit value to write 0x%x\n", val8); - iwl_write8(priv->trans, ofs, val8); - } - break; - default: - IWL_ERR(priv, "Unknown testmode register command ID\n"); - return -ENOSYS; - } - - return status; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; -} - - -static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv) -{ - struct iwl_notification_wait calib_wait; - static const u8 calib_complete[] = { - CALIBRATION_COMPLETE_NOTIFICATION - }; - int ret; - - iwl_init_notification_wait(&priv->notif_wait, &calib_wait, - calib_complete, ARRAY_SIZE(calib_complete), - NULL, NULL); - ret = iwl_init_alive_start(priv); - if (ret) { - IWL_ERR(priv, "Fail init calibration: %d\n", ret); - goto cfg_init_calib_error; - } - - ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, 2 * HZ); - if (ret) - IWL_ERR(priv, "Error detecting" - " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret); - return ret; - -cfg_init_calib_error: - iwl_remove_notification(&priv->notif_wait, &calib_wait); - return ret; -} - -/* - * This function handles the user application commands for driver. - * - * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the - * handlers respectively. - * - * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned - * value of the actual command execution is replied to the user application. - * - * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP - * is used for carry the message while IWL_TM_ATTR_COMMAND must set to - * IWL_TM_CMD_DEV2APP_SYNC_RSP. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_trans *trans = priv->trans; - struct sk_buff *skb; - unsigned char *rsp_data_ptr = NULL; - int status = 0, rsp_data_len = 0; - u32 devid, inst_size = 0, data_size = 0; - const struct fw_img *img; - - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: - rsp_data_ptr = (unsigned char *)priv->cfg->name; - rsp_data_len = strlen(priv->cfg->name); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - rsp_data_len + 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, - IWL_TM_CMD_DEV2APP_SYNC_RSP) || - nla_put(skb, IWL_TM_ATTR_SYNC_RSP, - rsp_data_len, rsp_data_ptr)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: - status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); - if (status) - IWL_ERR(priv, "Error loading init ucode: %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: - iwl_testmode_cfg_init_calib(priv); - priv->ucode_loaded = false; - iwl_trans_stop_device(trans); - break; - - case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: - status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR); - if (status) { - IWL_ERR(priv, - "Error loading runtime ucode: %d\n", status); - break; - } - status = iwl_alive_start(priv); - if (status) - IWL_ERR(priv, - "Error starting the device: %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: - iwl_scan_cancel_timeout(priv, 200); - priv->ucode_loaded = false; - iwl_trans_stop_device(trans); - status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); - if (status) { - IWL_ERR(priv, - "Error loading WOWLAN ucode: %d\n", status); - break; - } - status = iwl_alive_start(priv); - if (status) - IWL_ERR(priv, - "Error starting the device: %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_GET_EEPROM: - if (priv->eeprom) { - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - priv->cfg->base_params->eeprom_size + 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, - IWL_TM_CMD_DEV2APP_EEPROM_RSP) || - nla_put(skb, IWL_TM_ATTR_EEPROM, - priv->cfg->base_params->eeprom_size, - priv->eeprom)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", - status); - } else - return -EFAULT; - break; - - case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: - if (!tb[IWL_TM_ATTR_FIXRATE]) { - IWL_ERR(priv, "Missing fixrate setting\n"); - return -ENOMSG; - } - priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]); - break; - - case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: - IWL_INFO(priv, "uCode version raw: 0x%x\n", - priv->fw->ucode_ver); - - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, - priv->fw->ucode_ver)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: - devid = priv->trans->hw_id; - IWL_INFO(priv, "hw version: 0x%x\n", devid); - - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_GET_FW_INFO: - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (!priv->ucode_loaded) { - IWL_ERR(priv, "No uCode has not been loaded\n"); - return -EINVAL; - } else { - img = &priv->fw->img[priv->cur_ucode]; - inst_size = img->sec[IWL_UCODE_SECTION_INST].len; - data_size = img->sec[IWL_UCODE_SECTION_DATA].len; - } - if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) || - nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) || - nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - default: - IWL_ERR(priv, "Unknown testmode driver command ID\n"); - return -ENOSYS; - } - return status; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; -} - - -/* - * This function handles the user application commands for uCode trace - * - * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the - * handlers respectively. - * - * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned - * value of the actual command execution is replied to the user application. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct sk_buff *skb; - int status = 0; - struct device *dev = priv->trans->dev; - - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - if (priv->testmode_trace.trace_enabled) - return -EBUSY; - - if (!tb[IWL_TM_ATTR_TRACE_SIZE]) - priv->testmode_trace.buff_size = TRACE_BUFF_SIZE_DEF; - else - priv->testmode_trace.buff_size = - nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]); - if (!priv->testmode_trace.buff_size) - return -EINVAL; - if (priv->testmode_trace.buff_size < TRACE_BUFF_SIZE_MIN || - priv->testmode_trace.buff_size > TRACE_BUFF_SIZE_MAX) - return -EINVAL; - - priv->testmode_trace.total_size = - priv->testmode_trace.buff_size + TRACE_BUFF_PADD; - priv->testmode_trace.cpu_addr = - dma_alloc_coherent(dev, - priv->testmode_trace.total_size, - &priv->testmode_trace.dma_addr, - GFP_KERNEL); - if (!priv->testmode_trace.cpu_addr) - return -ENOMEM; - priv->testmode_trace.trace_enabled = true; - priv->testmode_trace.trace_addr = (u8 *)PTR_ALIGN( - priv->testmode_trace.cpu_addr, 0x100); - memset(priv->testmode_trace.trace_addr, 0x03B, - priv->testmode_trace.buff_size); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - sizeof(priv->testmode_trace.dma_addr) + 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - iwl_trace_cleanup(priv); - return -ENOMEM; - } - if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR, - sizeof(priv->testmode_trace.dma_addr), - (u64 *)&priv->testmode_trace.dma_addr)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) { - IWL_ERR(priv, "Error sending msg : %d\n", status); - } - priv->testmode_trace.num_chunks = - DIV_ROUND_UP(priv->testmode_trace.buff_size, - DUMP_CHUNK_SIZE); - break; - - case IWL_TM_CMD_APP2DEV_END_TRACE: - iwl_trace_cleanup(priv); - break; - default: - IWL_ERR(priv, "Unknown testmode mem command ID\n"); - return -ENOSYS; - } - return status; - -nla_put_failure: - kfree_skb(skb); - if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) == - IWL_TM_CMD_APP2DEV_BEGIN_TRACE) - iwl_trace_cleanup(priv); - return -EMSGSIZE; -} - -static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int idx, length; - - if (priv->testmode_trace.trace_enabled && - priv->testmode_trace.trace_addr) { - idx = cb->args[4]; - if (idx >= priv->testmode_trace.num_chunks) - return -ENOENT; - length = DUMP_CHUNK_SIZE; - if (((idx + 1) == priv->testmode_trace.num_chunks) && - (priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE)) - length = priv->testmode_trace.buff_size % - DUMP_CHUNK_SIZE; - - if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length, - priv->testmode_trace.trace_addr + - (DUMP_CHUNK_SIZE * idx))) - goto nla_put_failure; - idx++; - cb->args[4] = idx; - return 0; - } else - return -EFAULT; - - nla_put_failure: - return -ENOBUFS; -} - -/* - * This function handles the user application switch ucode ownership. - * - * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and - * decide who the current owner of the uCode - * - * If the current owner is OWNERSHIP_TM, then the only host command - * can deliver to uCode is from testmode, all the other host commands - * will dropped. - * - * default driver is the owner of uCode in normal operational mode - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u8 owner; - - if (!tb[IWL_TM_ATTR_UCODE_OWNER]) { - IWL_ERR(priv, "Missing ucode owner\n"); - return -ENOMSG; - } - - owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]); - if (owner == IWL_OWNERSHIP_DRIVER) { - priv->ucode_owner = owner; - priv->pre_rx_handler = NULL; - } else if (owner == IWL_OWNERSHIP_TM) { - priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; - priv->ucode_owner = owner; - } else { - IWL_ERR(priv, "Invalid owner\n"); - return -EINVAL; - } - return 0; -} - -static int iwl_testmode_indirect_read(struct iwl_priv *priv, u32 addr, u32 size) -{ - struct iwl_trans *trans = priv->trans; - unsigned long flags; - int i; - - if (size & 0x3) - return -EINVAL; - priv->testmode_mem.buff_size = size; - priv->testmode_mem.buff_addr = - kmalloc(priv->testmode_mem.buff_size, GFP_KERNEL); - if (priv->testmode_mem.buff_addr == NULL) - return -ENOMEM; - - /* Hard-coded periphery absolute address */ - if (IWL_TM_ABS_PRPH_START <= addr && - addr < IWL_TM_ABS_PRPH_START + PRPH_END) { - spin_lock_irqsave(&trans->reg_lock, flags); - iwl_grab_nic_access(trans); - iwl_write32(trans, HBUS_TARG_PRPH_RADDR, - addr | (3 << 24)); - for (i = 0; i < size; i += 4) - *(u32 *)(priv->testmode_mem.buff_addr + i) = - iwl_read32(trans, HBUS_TARG_PRPH_RDAT); - iwl_release_nic_access(trans); - spin_unlock_irqrestore(&trans->reg_lock, flags); - } else { /* target memory (SRAM) */ - _iwl_read_targ_mem_words(trans, addr, - priv->testmode_mem.buff_addr, - priv->testmode_mem.buff_size / 4); - } - - priv->testmode_mem.num_chunks = - DIV_ROUND_UP(priv->testmode_mem.buff_size, DUMP_CHUNK_SIZE); - priv->testmode_mem.read_in_progress = true; - return 0; - -} - -static int iwl_testmode_indirect_write(struct iwl_priv *priv, u32 addr, - u32 size, unsigned char *buf) -{ - struct iwl_trans *trans = priv->trans; - u32 val, i; - unsigned long flags; - - if (IWL_TM_ABS_PRPH_START <= addr && - addr < IWL_TM_ABS_PRPH_START + PRPH_END) { - /* Periphery writes can be 1-3 bytes long, or DWORDs */ - if (size < 4) { - memcpy(&val, buf, size); - spin_lock_irqsave(&trans->reg_lock, flags); - iwl_grab_nic_access(trans); - iwl_write32(trans, HBUS_TARG_PRPH_WADDR, - (addr & 0x0000FFFF) | - ((size - 1) << 24)); - iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); - iwl_release_nic_access(trans); - /* needed after consecutive writes w/o read */ - mmiowb(); - spin_unlock_irqrestore(&trans->reg_lock, flags); - } else { - if (size % 4) - return -EINVAL; - for (i = 0; i < size; i += 4) - iwl_write_prph(trans, addr+i, - *(u32 *)(buf+i)); - } - } else if (iwlagn_hw_valid_rtc_data_addr(addr) || - (IWLAGN_RTC_INST_LOWER_BOUND <= addr && - addr < IWLAGN_RTC_INST_UPPER_BOUND)) { - _iwl_write_targ_mem_words(trans, addr, buf, size/4); - } else - return -EINVAL; - return 0; -} - -/* - * This function handles the user application commands for SRAM data dump - * - * It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and - * IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading - * - * Several error will be retured, -EBUSY if the SRAM data retrieved by - * previous command has not been delivered to userspace, or -ENOMSG if - * the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE) - * are missing, or -ENOMEM if the buffer allocation fails. - * - * Otherwise 0 is replied indicating the success of the SRAM reading. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_indirect_mem(struct ieee80211_hw *hw, - struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u32 addr, size, cmd; - unsigned char *buf; - - /* Both read and write should be blocked, for atomicity */ - if (priv->testmode_mem.read_in_progress) - return -EBUSY; - - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - if (!tb[IWL_TM_ATTR_MEM_ADDR]) { - IWL_ERR(priv, "Error finding memory offset address\n"); - return -ENOMSG; - } - addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]); - if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) { - IWL_ERR(priv, "Error finding size for memory reading\n"); - return -ENOMSG; - } - size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]); - - if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) - return iwl_testmode_indirect_read(priv, addr, size); - else { - if (!tb[IWL_TM_ATTR_BUFFER_DUMP]) - return -EINVAL; - buf = (unsigned char *) nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]); - return iwl_testmode_indirect_write(priv, addr, size, buf); - } -} - -static int iwl_testmode_buffer_dump(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int idx, length; - - if (priv->testmode_mem.read_in_progress) { - idx = cb->args[4]; - if (idx >= priv->testmode_mem.num_chunks) { - iwl_mem_cleanup(priv); - return -ENOENT; - } - length = DUMP_CHUNK_SIZE; - if (((idx + 1) == priv->testmode_mem.num_chunks) && - (priv->testmode_mem.buff_size % DUMP_CHUNK_SIZE)) - length = priv->testmode_mem.buff_size % - DUMP_CHUNK_SIZE; - - if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length, - priv->testmode_mem.buff_addr + - (DUMP_CHUNK_SIZE * idx))) - goto nla_put_failure; - idx++; - cb->args[4] = idx; - return 0; - } else - return -EFAULT; - - nla_put_failure: - return -ENOBUFS; -} - -static int iwl_testmode_notifications(struct ieee80211_hw *hw, - struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - bool enable; - - enable = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]); - if (enable) - priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; - else - priv->pre_rx_handler = NULL; - return 0; -} - - -/* The testmode gnl message handler that takes the gnl message from the - * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then - * invoke the corresponding handlers. - * - * This function is invoked when there is user space application sending - * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated - * by nl80211. - * - * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before - * dispatching it to the corresponding handler. - * - * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application; - * -ENOSYS is replied to the user application if the command is unknown; - * Otherwise, the command is dispatched to the respective handler. - * - * @hw: ieee80211_hw object that represents the device - * @data: pointer to user space message - * @len: length in byte of @data - */ -int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) -{ - struct nlattr *tb[IWL_TM_ATTR_MAX]; - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int result; - - result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, - iwl_testmode_gnl_msg_policy); - if (result != 0) { - IWL_ERR(priv, "Error parsing the gnl message : %d\n", result); - return result; - } - - /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ - if (!tb[IWL_TM_ATTR_COMMAND]) { - IWL_ERR(priv, "Missing testmode command type\n"); - return -ENOMSG; - } - /* in case multiple accesses to the device happens */ - mutex_lock(&priv->mutex); - - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_UCODE: - IWL_DEBUG_INFO(priv, "testmode cmd to uCode\n"); - result = iwl_testmode_ucode(hw, tb); - break; - case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - IWL_DEBUG_INFO(priv, "testmode cmd to register\n"); - result = iwl_testmode_reg(hw, tb); - break; - case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: - case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: - case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: - case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: - case IWL_TM_CMD_APP2DEV_GET_EEPROM: - case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: - case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: - case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: - case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: - case IWL_TM_CMD_APP2DEV_GET_FW_INFO: - IWL_DEBUG_INFO(priv, "testmode cmd to driver\n"); - result = iwl_testmode_driver(hw, tb); - break; - - case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - case IWL_TM_CMD_APP2DEV_END_TRACE: - case IWL_TM_CMD_APP2DEV_READ_TRACE: - IWL_DEBUG_INFO(priv, "testmode uCode trace cmd to driver\n"); - result = iwl_testmode_trace(hw, tb); - break; - - case IWL_TM_CMD_APP2DEV_OWNERSHIP: - IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n"); - result = iwl_testmode_ownership(hw, tb); - break; - - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - IWL_DEBUG_INFO(priv, "testmode indirect memory cmd " - "to driver\n"); - result = iwl_testmode_indirect_mem(hw, tb); - break; - - case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - IWL_DEBUG_INFO(priv, "testmode notifications cmd " - "to driver\n"); - result = iwl_testmode_notifications(hw, tb); - break; - - default: - IWL_ERR(priv, "Unknown testmode command\n"); - result = -ENOSYS; - break; - } - - mutex_unlock(&priv->mutex); - return result; -} - -int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, - struct netlink_callback *cb, - void *data, int len) -{ - struct nlattr *tb[IWL_TM_ATTR_MAX]; - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int result; - u32 cmd; - - if (cb->args[3]) { - /* offset by 1 since commands start at 0 */ - cmd = cb->args[3] - 1; - } else { - result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, - iwl_testmode_gnl_msg_policy); - if (result) { - IWL_ERR(priv, - "Error parsing the gnl message : %d\n", result); - return result; - } - - /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ - if (!tb[IWL_TM_ATTR_COMMAND]) { - IWL_ERR(priv, "Missing testmode command type\n"); - return -ENOMSG; - } - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - cb->args[3] = cmd + 1; - } - - /* in case multiple accesses to the device happens */ - mutex_lock(&priv->mutex); - switch (cmd) { - case IWL_TM_CMD_APP2DEV_READ_TRACE: - IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n"); - result = iwl_testmode_trace_dump(hw, skb, cb); - break; - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: - IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n"); - result = iwl_testmode_buffer_dump(hw, skb, cb); - break; - default: - result = -EINVAL; - break; - } - - mutex_unlock(&priv->mutex); - return result; -} diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h deleted file mode 100644 index 6ba211b09426..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-testmode.h +++ /dev/null @@ -1,309 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __IWL_TESTMODE_H__ -#define __IWL_TESTMODE_H__ - -#include - - -/* - * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and - * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX). - * The command ID is carried with IWL_TM_ATTR_COMMAND. - * - * @IWL_TM_CMD_APP2DEV_UCODE: - * commands from user application to the uCode, - * the actual uCode host command ID is carried with - * IWL_TM_ATTR_UCODE_CMD_ID - * - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - * commands from user applicaiton to access register - * - * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name - * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image - * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration - * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image - * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data - * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS - * commands fom user space for pure driver level operations - * - * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - * @IWL_TM_CMD_APP2DEV_END_TRACE: - * @IWL_TM_CMD_APP2DEV_READ_TRACE: - * commands fom user space for uCode trace operations - * - * @IWL_TM_CMD_DEV2APP_SYNC_RSP: - * commands from kernel space to carry the synchronous response - * to user application - * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT: - * commands from kernel space to multicast the spontaneous messages - * to user application, or reply of host commands - * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: - * commands from kernel space to carry the eeprom response - * to user application - * - * @IWL_TM_CMD_APP2DEV_OWNERSHIP: - * commands from user application to own change the ownership of the uCode - * if application has the ownership, the only host command from - * testmode will deliver to uCode. Default owner is driver - * - * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image - * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version - * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device - * @IWL_TM_CMD_APP2DEV_GET_FW_INFO: - * retrieve information of existing loaded uCode image - * - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - * Commands to read/write data from periphery or SRAM memory ranges. - * Fore reading, a READ command is sent from the userspace and the data - * is returned when the user calls a DUMP command. - * For writing, only a WRITE command is used. - * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - * Command to enable/disable notifications (currently RX packets) from the - * driver to userspace. - */ -enum iwl_tm_cmd_t { - IWL_TM_CMD_APP2DEV_UCODE = 1, - IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, - IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, - IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, - IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, - IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, - IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, - IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8, - IWL_TM_CMD_APP2DEV_GET_EEPROM = 9, - IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10, - IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11, - IWL_TM_CMD_APP2DEV_END_TRACE = 12, - IWL_TM_CMD_APP2DEV_READ_TRACE = 13, - IWL_TM_CMD_DEV2APP_SYNC_RSP = 14, - IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, - IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, - IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, - RESERVED_18 = 18, - RESERVED_19 = 19, - RESERVED_20 = 20, - RESERVED_21 = 21, - IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, - IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, - IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, - IWL_TM_CMD_APP2DEV_GET_FW_INFO = 25, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28, - IWL_TM_CMD_APP2DEV_NOTIFICATIONS = 29, - IWL_TM_CMD_MAX = 30, -}; - -/* - * Atrribute filed in testmode command - * See enum iwl_tm_cmd_t. - * - * @IWL_TM_ATTR_NOT_APPLICABLE: - * The attribute is not applicable or invalid - * @IWL_TM_ATTR_COMMAND: - * From user space to kernel space: - * the command either destines to ucode, driver, or register; - * From kernel space to user space: - * the command either carries synchronous response, - * or the spontaneous message multicast from the device; - * - * @IWL_TM_ATTR_UCODE_CMD_ID: - * @IWL_TM_ATTR_UCODE_CMD_DATA: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE, - * The mandatory fields are : - * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID; - * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload - * to the ucode - * - * @IWL_TM_ATTR_REG_OFFSET: - * @IWL_TM_ATTR_REG_VALUE8: - * @IWL_TM_ATTR_REG_VALUE32: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX, - * The mandatory fields are: - * IWL_TM_ATTR_REG_OFFSET for the offset of the target register; - * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value - * - * @IWL_TM_ATTR_SYNC_RSP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP, - * The mandatory fields are: - * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user - * application command - * - * @IWL_TM_ATTR_UCODE_RX_PKT: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT, - * The mandatory fields are: - * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user - * application - * - * @IWL_TM_ATTR_EEPROM: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM, - * The mandatory fields are: - * IWL_TM_ATTR_EEPROM for the data content responging to the user - * application - * - * @IWL_TM_ATTR_TRACE_ADDR: - * @IWL_TM_ATTR_TRACE_SIZE: - * @IWL_TM_ATTR_TRACE_DUMP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE, - * The mandatory fields are: - * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address - * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size - * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump - * - * @IWL_TM_ATTR_FIXRATE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ, - * The mandatory fields are: - * IWL_TM_ATTR_FIXRATE for the fixed rate - * - * @IWL_TM_ATTR_UCODE_OWNER: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, - * The mandatory fields are: - * IWL_TM_ATTR_UCODE_OWNER for the new owner - * - * @IWL_TM_ATTR_MEM_ADDR: - * @IWL_TM_ATTR_BUFFER_SIZE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ - * or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE. - * The mandatory fields are: - * IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write - * IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write. - * - * @IWL_TM_ATTR_BUFFER_DUMP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP, - * IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read. - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE, - * this attribute contains the data to write. - * - * @IWL_TM_ATTR_FW_VERSION: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, - * IWL_TM_ATTR_FW_VERSION for the uCode version - * - * @IWL_TM_ATTR_DEVICE_ID: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, - * IWL_TM_ATTR_DEVICE_ID for the device ID information - * - * @IWL_TM_ATTR_FW_TYPE: - * @IWL_TM_ATTR_FW_INST_SIZE: - * @IWL_TM_ATTR_FW_DATA_SIZE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO, - * The mandatory fields are: - * IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...) - * IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section - * IWL_TM_ATTR_FW_DATA_SIZE for the size of data section - * - * @IWL_TM_ATTR_UCODE_CMD_SKB: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag - * indicates that the user wants to receive the response of the command - * in a reply SKB. If it's not present, the response is not returned. - * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this - * flag enables (if present) or disables (if not) the forwarding - * to userspace. - */ -enum iwl_tm_attr_t { - IWL_TM_ATTR_NOT_APPLICABLE = 0, - IWL_TM_ATTR_COMMAND = 1, - IWL_TM_ATTR_UCODE_CMD_ID = 2, - IWL_TM_ATTR_UCODE_CMD_DATA = 3, - IWL_TM_ATTR_REG_OFFSET = 4, - IWL_TM_ATTR_REG_VALUE8 = 5, - IWL_TM_ATTR_REG_VALUE32 = 6, - IWL_TM_ATTR_SYNC_RSP = 7, - IWL_TM_ATTR_UCODE_RX_PKT = 8, - IWL_TM_ATTR_EEPROM = 9, - IWL_TM_ATTR_TRACE_ADDR = 10, - IWL_TM_ATTR_TRACE_SIZE = 11, - IWL_TM_ATTR_TRACE_DUMP = 12, - IWL_TM_ATTR_FIXRATE = 13, - IWL_TM_ATTR_UCODE_OWNER = 14, - IWL_TM_ATTR_MEM_ADDR = 15, - IWL_TM_ATTR_BUFFER_SIZE = 16, - IWL_TM_ATTR_BUFFER_DUMP = 17, - IWL_TM_ATTR_FW_VERSION = 18, - IWL_TM_ATTR_DEVICE_ID = 19, - IWL_TM_ATTR_FW_TYPE = 20, - IWL_TM_ATTR_FW_INST_SIZE = 21, - IWL_TM_ATTR_FW_DATA_SIZE = 22, - IWL_TM_ATTR_UCODE_CMD_SKB = 23, - IWL_TM_ATTR_ENABLE_NOTIFICATION = 24, - IWL_TM_ATTR_MAX = 25, -}; - -/* uCode trace buffer */ -#define TRACE_BUFF_SIZE_MAX 0x200000 -#define TRACE_BUFF_SIZE_MIN 0x20000 -#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN -#define TRACE_BUFF_PADD 0x2000 - -/* Maximum data size of each dump it packet */ -#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) - -/* Address offset of data segment in SRAM */ -#define SRAM_DATA_SEG_OFFSET 0x800000 - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-ucode.c b/drivers/net/wireless/iwlwifi/iwl-ucode.c deleted file mode 100644 index d14e633bebc9..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-ucode.c +++ /dev/null @@ -1,532 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include - -#include "iwl-dev.h" -#include "iwl-io.h" -#include "iwl-agn-hw.h" -#include "iwl-agn.h" -#include "iwl-agn-calib.h" -#include "iwl-trans.h" -#include "iwl-fh.h" -#include "iwl-op-mode.h" - -/****************************************************************************** - * - * uCode download functions - * - ******************************************************************************/ - -static inline const struct fw_img * -iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) -{ - if (ucode_type >= IWL_UCODE_TYPE_MAX) - return NULL; - - return &priv->fw->img[ucode_type]; -} - -/* - * Calibration - */ -static int iwl_set_Xtal_calib(struct iwl_priv *priv) -{ - struct iwl_calib_xtal_freq_cmd cmd; - __le16 *xtal_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_XTAL); - - iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); - cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); - cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]); - return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); -} - -static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) -{ - struct iwl_calib_temperature_offset_cmd cmd; - __le16 *offset_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); - - memset(&cmd, 0, sizeof(cmd)); - iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - memcpy(&cmd.radio_sensor_offset, offset_calib, sizeof(*offset_calib)); - if (!(cmd.radio_sensor_offset)) - cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; - - IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n", - le16_to_cpu(cmd.radio_sensor_offset)); - return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); -} - -static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv) -{ - struct iwl_calib_temperature_offset_v2_cmd cmd; - __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv, - EEPROM_KELVIN_TEMPERATURE); - __le16 *offset_calib_low = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); - struct iwl_eeprom_calib_hdr *hdr; - - memset(&cmd, 0, sizeof(cmd)); - iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, - EEPROM_CALIB_ALL); - memcpy(&cmd.radio_sensor_offset_high, offset_calib_high, - sizeof(*offset_calib_high)); - memcpy(&cmd.radio_sensor_offset_low, offset_calib_low, - sizeof(*offset_calib_low)); - if (!(cmd.radio_sensor_offset_low)) { - IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); - cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; - cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; - } - memcpy(&cmd.burntVoltageRef, &hdr->voltage, - sizeof(hdr->voltage)); - - IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", - le16_to_cpu(cmd.radio_sensor_offset_high)); - IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", - le16_to_cpu(cmd.radio_sensor_offset_low)); - IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", - le16_to_cpu(cmd.burntVoltageRef)); - - return iwl_calib_set(priv, (void *)&cmd, sizeof(cmd)); -} - -static int iwl_send_calib_cfg(struct iwl_priv *priv) -{ - struct iwl_calib_cfg_cmd calib_cfg_cmd; - struct iwl_host_cmd cmd = { - .id = CALIBRATION_CFG_CMD, - .len = { sizeof(struct iwl_calib_cfg_cmd), }, - .data = { &calib_cfg_cmd, }, - }; - - memset(&calib_cfg_cmd, 0, sizeof(calib_cfg_cmd)); - calib_cfg_cmd.ucd_calib_cfg.once.is_enable = IWL_CALIB_INIT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.once.start = IWL_CALIB_INIT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.once.send_res = IWL_CALIB_INIT_CFG_ALL; - calib_cfg_cmd.ucd_calib_cfg.flags = - IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK; - - return iwl_dvm_send_cmd(priv, &cmd); -} - -int iwl_init_alive_start(struct iwl_priv *priv) -{ - int ret; - - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - /* - * Tell uCode we are ready to perform calibration - * need to perform this before any calibration - * no need to close the envlope since we are going - * to load the runtime uCode later. - */ - ret = iwl_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - - } - - ret = iwl_send_calib_cfg(priv); - if (ret) - return ret; - - /** - * temperature offset calibration is only needed for runtime ucode, - * so prepare the value now. - */ - if (priv->cfg->need_temp_offset_calib) { - if (priv->cfg->temp_offset_v2) - return iwl_set_temperature_offset_calib_v2(priv); - else - return iwl_set_temperature_offset_calib(priv); - } - - return 0; -} - -static int iwl_send_wimax_coex(struct iwl_priv *priv) -{ - struct iwl_wimax_coex_cmd coex_cmd; - - /* coexistence is disabled */ - memset(&coex_cmd, 0, sizeof(coex_cmd)); - - return iwl_dvm_send_cmd_pdu(priv, - COEX_PRIORITY_TABLE_CMD, CMD_SYNC, - sizeof(coex_cmd), &coex_cmd); -} - -static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { - ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_LOW << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_HIGH << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (1 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_COEX_OFF << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - ((BT_COEX_PRIO_TBL_PRIO_COEX_ON << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | - (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), - 0, 0, 0, 0, 0, 0, 0 -}; - -void iwl_send_prio_tbl(struct iwl_priv *priv) -{ - struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd; - - memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, - sizeof(iwl_bt_prio_tbl)); - if (iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC, - sizeof(prio_tbl_cmd), &prio_tbl_cmd)) - IWL_ERR(priv, "failed to send BT prio tbl command\n"); -} - -int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) -{ - struct iwl_bt_coex_prot_env_cmd env_cmd; - int ret; - - env_cmd.action = action; - env_cmd.type = type; - ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PROT_ENV, CMD_SYNC, - sizeof(env_cmd), &env_cmd); - if (ret) - IWL_ERR(priv, "failed to send BT env command\n"); - return ret; -} - - -static int iwl_alive_notify(struct iwl_priv *priv) -{ - int ret; - - iwl_trans_fw_alive(priv->trans); - - priv->passive_no_rx = false; - priv->transport_queue_stop = 0; - - ret = iwl_send_wimax_coex(priv); - if (ret) - return ret; - - if (!priv->cfg->no_xtal_calib) { - ret = iwl_set_Xtal_calib(priv); - if (ret) - return ret; - } - - return iwl_send_calib_results(priv); -} - - -/** - * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host, - * using sample data 100 bytes apart. If these sample points are good, - * it's a pretty good bet that everything between them is good, too. - */ -static int iwl_verify_sec_sparse(struct iwl_priv *priv, - const struct fw_desc *fw_desc) -{ - __le32 *image = (__le32 *)fw_desc->v_addr; - u32 len = fw_desc->len; - u32 val; - u32 i; - - IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); - - for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) { - /* read data comes through single port, auto-incr addr */ - /* NOTE: Use the debugless read so we don't flood kernel log - * if IWL_DL_IO is set */ - iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR, - i + fw_desc->offset); - val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - if (val != le32_to_cpu(*image)) - return -EIO; - } - - return 0; -} - -static void iwl_print_mismatch_sec(struct iwl_priv *priv, - const struct fw_desc *fw_desc) -{ - __le32 *image = (__le32 *)fw_desc->v_addr; - u32 len = fw_desc->len; - u32 val; - u32 offs; - int errors = 0; - - IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); - - iwl_write_direct32(priv->trans, HBUS_TARG_MEM_RADDR, - fw_desc->offset); - - for (offs = 0; - offs < len && errors < 20; - offs += sizeof(u32), image++) { - /* read data comes through single port, auto-incr addr */ - val = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); - if (val != le32_to_cpu(*image)) { - IWL_ERR(priv, "uCode INST section at " - "offset 0x%x, is 0x%x, s/b 0x%x\n", - offs, val, le32_to_cpu(*image)); - errors++; - } - } -} - -/** - * iwl_verify_ucode - determine which instruction image is in SRAM, - * and verify its contents - */ -static int iwl_verify_ucode(struct iwl_priv *priv, - enum iwl_ucode_type ucode_type) -{ - const struct fw_img *img = iwl_get_ucode_image(priv, ucode_type); - - if (!img) { - IWL_ERR(priv, "Invalid ucode requested (%d)\n", ucode_type); - return -EINVAL; - } - - if (!iwl_verify_sec_sparse(priv, &img->sec[IWL_UCODE_SECTION_INST])) { - IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n"); - return 0; - } - - IWL_ERR(priv, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n"); - - iwl_print_mismatch_sec(priv, &img->sec[IWL_UCODE_SECTION_INST]); - return -EIO; -} - -struct iwl_alive_data { - bool valid; - u8 subtype; -}; - -static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_priv *priv = - container_of(notif_wait, struct iwl_priv, notif_wait); - struct iwl_alive_data *alive_data = data; - struct iwl_alive_resp *palive; - - palive = (void *)pkt->data; - - IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision " - "0x%01X 0x%01X\n", - palive->is_valid, palive->ver_type, - palive->ver_subtype); - - priv->device_pointers.error_event_table = - le32_to_cpu(palive->error_event_table_ptr); - priv->device_pointers.log_event_table = - le32_to_cpu(palive->log_event_table_ptr); - - alive_data->subtype = palive->ver_subtype; - alive_data->valid = palive->is_valid == UCODE_VALID_OK; - - return true; -} - -#define UCODE_ALIVE_TIMEOUT HZ -#define UCODE_CALIB_TIMEOUT (2*HZ) - -int iwl_load_ucode_wait_alive(struct iwl_priv *priv, - enum iwl_ucode_type ucode_type) -{ - struct iwl_notification_wait alive_wait; - struct iwl_alive_data alive_data; - const struct fw_img *fw; - int ret; - enum iwl_ucode_type old_type; - static const u8 alive_cmd[] = { REPLY_ALIVE }; - - old_type = priv->cur_ucode; - priv->cur_ucode = ucode_type; - fw = iwl_get_ucode_image(priv, ucode_type); - - priv->ucode_loaded = false; - - if (!fw) - return -EINVAL; - - iwl_init_notification_wait(&priv->notif_wait, &alive_wait, - alive_cmd, ARRAY_SIZE(alive_cmd), - iwl_alive_fn, &alive_data); - - ret = iwl_trans_start_fw(priv->trans, fw); - if (ret) { - priv->cur_ucode = old_type; - iwl_remove_notification(&priv->notif_wait, &alive_wait); - return ret; - } - - /* - * Some things may run in the background now, but we - * just wait for the ALIVE notification here. - */ - ret = iwl_wait_notification(&priv->notif_wait, &alive_wait, - UCODE_ALIVE_TIMEOUT); - if (ret) { - priv->cur_ucode = old_type; - return ret; - } - - if (!alive_data.valid) { - IWL_ERR(priv, "Loaded ucode is not valid!\n"); - priv->cur_ucode = old_type; - return -EIO; - } - - /* - * This step takes a long time (60-80ms!!) and - * WoWLAN image should be loaded quickly, so - * skip it for WoWLAN. - */ - if (ucode_type != IWL_UCODE_WOWLAN) { - ret = iwl_verify_ucode(priv, ucode_type); - if (ret) { - priv->cur_ucode = old_type; - return ret; - } - - /* delay a bit to give rfkill time to run */ - msleep(5); - } - - ret = iwl_alive_notify(priv); - if (ret) { - IWL_WARN(priv, - "Could not complete ALIVE transition: %d\n", ret); - priv->cur_ucode = old_type; - return ret; - } - - priv->ucode_loaded = true; - - return 0; -} - -static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, - struct iwl_rx_packet *pkt, void *data) -{ - struct iwl_priv *priv = data; - struct iwl_calib_hdr *hdr; - int len; - - if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { - WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); - return true; - } - - hdr = (struct iwl_calib_hdr *)pkt->data; - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - - /* reduce the size by the length field itself */ - len -= sizeof(__le32); - - if (iwl_calib_set(priv, hdr, len)) - IWL_ERR(priv, "Failed to record calibration data %d\n", - hdr->op_code); - - return false; -} - -int iwl_run_init_ucode(struct iwl_priv *priv) -{ - struct iwl_notification_wait calib_wait; - static const u8 calib_complete[] = { - CALIBRATION_RES_NOTIFICATION, - CALIBRATION_COMPLETE_NOTIFICATION - }; - int ret; - - lockdep_assert_held(&priv->mutex); - - /* No init ucode required? Curious, but maybe ok */ - if (!priv->fw->img[IWL_UCODE_INIT].sec[0].len) - return 0; - - if (priv->init_ucode_run) - return 0; - - iwl_init_notification_wait(&priv->notif_wait, &calib_wait, - calib_complete, ARRAY_SIZE(calib_complete), - iwlagn_wait_calib, priv); - - /* Will also start the device */ - ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT); - if (ret) - goto error; - - ret = iwl_init_alive_start(priv); - if (ret) - goto error; - - /* - * Some things may run in the background now, but we - * just wait for the calibration complete notification. - */ - ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, - UCODE_CALIB_TIMEOUT); - if (!ret) - priv->init_ucode_run = true; - - goto out; - - error: - iwl_remove_notification(&priv->notif_wait, &calib_wait); - out: - /* Whatever happened, stop the device */ - iwl_trans_stop_device(priv->trans); - priv->ucode_loaded = false; - - return ret; -} diff --git a/drivers/net/wireless/iwlwifi/pcie/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c index f6efa122336b..062c07b17056 100644 --- a/drivers/net/wireless/iwlwifi/pcie/2000.c +++ b/drivers/net/wireless/iwlwifi/pcie/2000.c @@ -29,7 +29,7 @@ #include "iwl-config.h" #include "iwl-agn-hw.h" #include "cfg.h" -#include "iwl-commands.h" /* needed for BT for now */ +#include "dvm/commands.h" /* needed for BT for now */ /* Highest firmware API version supported */ #define IWL2030_UCODE_API_MAX 6 diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c index 7360cd9e0148..f9e396182ef3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/6000.c +++ b/drivers/net/wireless/iwlwifi/pcie/6000.c @@ -29,7 +29,7 @@ #include "iwl-config.h" #include "iwl-agn-hw.h" #include "cfg.h" -#include "iwl-commands.h" /* needed for BT for now */ +#include "dvm/commands.h" /* needed for BT for now */ /* Highest firmware API version supported */ #define IWL6000_UCODE_API_MAX 6 diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index e8a04a18a7d0..a19ce40c0092 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -72,11 +72,10 @@ #include "iwl-trans.h" #include "iwl-csr.h" #include "iwl-prph.h" -#include "iwl-eeprom.h" #include "iwl-agn-hw.h" #include "internal.h" /* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "iwl-commands.h" +#include "dvm/commands.h" #define SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie) \ (((1<cfg->base_params->num_of_queues) - 1) &\ diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 0b8bbb0afcab..583451c710e3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -34,11 +34,10 @@ #include "iwl-csr.h" #include "iwl-prph.h" #include "iwl-io.h" -#include "iwl-agn-hw.h" #include "iwl-op-mode.h" #include "internal.h" /* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "iwl-commands.h" +#include "dvm/commands.h" #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 -- cgit v1.2.3 From 2587d36b1e121b84993e66dc8624c60793e25921 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 May 2012 20:18:19 +0200 Subject: iwlwifi: unify tx_chains_num setting There's no need to copy the same code for all devices since none of the 5000 series devices (that don't have the RX SISO override) don't set the rx_with_siso_diversity variable. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/devices.c | 34 ------------------------------ drivers/net/wireless/iwlwifi/dvm/eeprom.c | 8 +++++++ 2 files changed, 8 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index ac5675d86ec3..7d3bb1e2d196 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -173,14 +173,6 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) { priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - iwl1000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -254,14 +246,6 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) { priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - iwl2000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -408,11 +392,6 @@ static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - iwl5000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -424,11 +403,6 @@ static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - iwl5150_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -623,14 +597,6 @@ static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - iwl6000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ diff --git a/drivers/net/wireless/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/iwlwifi/dvm/eeprom.c index 69d280f8db33..b4da76d5d97e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/eeprom.c +++ b/drivers/net/wireless/iwlwifi/dvm/eeprom.c @@ -285,6 +285,14 @@ int iwl_eeprom_init_hw_params(struct iwl_priv *priv) return -EINVAL; } + priv->hw_params.tx_chains_num = + num_of_ant(priv->hw_params.valid_tx_ant); + if (priv->cfg->rx_with_siso_diversity) + priv->hw_params.rx_chains_num = 1; + else + priv->hw_params.rx_chains_num = + num_of_ant(priv->hw_params.valid_rx_ant); + IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", priv->hw_params.valid_tx_ant, priv->hw_params.valid_rx_ant); -- cgit v1.2.3 From d370493f9be79530950dfab23b980ce60bc84f34 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 May 2012 20:03:01 +0200 Subject: iwlwifi: move ht40 bitmap into config There's no need to dynamically fill the HT40 band bitmap as it's a device parameter, just put it into the HT configuration. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/dev.h | 2 -- drivers/net/wireless/iwlwifi/dvm/devices.c | 13 ------------- drivers/net/wireless/iwlwifi/dvm/main.c | 9 ++++++--- drivers/net/wireless/iwlwifi/iwl-config.h | 5 ++++- drivers/net/wireless/iwlwifi/pcie/1000.c | 1 + drivers/net/wireless/iwlwifi/pcie/2000.c | 1 + drivers/net/wireless/iwlwifi/pcie/5000.c | 1 + drivers/net/wireless/iwlwifi/pcie/6000.c | 1 + 8 files changed, 14 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 2880a0a3da68..af9de9542922 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -635,7 +635,6 @@ enum iwl_scan_type { * @rx_chains_num: Number of RX chains * @valid_tx_ant: usable antennas for TX * @valid_rx_ant: usable antennas for RX - * @ht40_channel: is 40MHz width possible: BIT(IEEE80211_BAND_XXX) * @sku: sku read from EEPROM * @ct_kill_threshold: temperature threshold - in hw dependent unit * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit @@ -648,7 +647,6 @@ struct iwl_hw_params { u8 rx_chains_num; u8 valid_tx_ant; u8 valid_rx_ant; - u8 ht40_channel; bool use_rts_for_aggregation; u16 sku; u32 ct_kill_threshold; diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 7d3bb1e2d196..ddf053c5713d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -171,8 +171,6 @@ static const struct iwl_sensitivity_ranges iwl1000_sensitivity = { static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); - iwl1000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -244,8 +242,6 @@ static const struct iwl_sensitivity_ranges iwl2000_sensitivity = { static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ); - iwl2000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -389,9 +385,6 @@ static void iwl5000_set_ct_threshold(struct iwl_priv *priv) static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | - BIT(IEEE80211_BAND_5GHZ); - iwl5000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -400,9 +393,6 @@ static void iwl5000_hw_set_hw_params(struct iwl_priv *priv) static void iwl5150_hw_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | - BIT(IEEE80211_BAND_5GHZ); - iwl5150_set_ct_threshold(priv); /* Set initial sensitivity parameters */ @@ -594,9 +584,6 @@ static const struct iwl_sensitivity_ranges iwl6000_sensitivity = { static void iwl6000_hw_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | - BIT(IEEE80211_BAND_5GHZ); - iwl6000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index d67ae99c00b9..441188725985 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1157,16 +1157,19 @@ static void iwl_init_ht_hw_capab(const struct iwl_priv *priv, u8 tx_chains_num = priv->hw_params.tx_chains_num; ht_info->cap = 0; + ht_info->ht_supported = false; memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + if (!priv->cfg->ht_params) + return; + ht_info->ht_supported = true; - if (priv->cfg->ht_params && - priv->cfg->ht_params->ht_greenfield_support) + if (priv->cfg->ht_params->ht_greenfield_support) ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (priv->hw_params.ht40_channel & BIT(band)) { + if (priv->cfg->ht_params->ht40_bands & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; ht_info->mcs.rx_mask[4] = 0x01; diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index a52818bbfe98..af3a46126f01 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -182,13 +182,16 @@ struct iwl_bt_params { bool bt_sco_disable; bool bt_session_2; }; + /* * @use_rts_for_aggregation: use rts/cts protection for HT traffic + * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40 */ struct iwl_ht_params { + enum ieee80211_smps_mode smps_mode; const bool ht_greenfield_support; /* if used set to true */ bool use_rts_for_aggregation; - enum ieee80211_smps_mode smps_mode; + u8 ht40_bands; }; /** diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c index 0cfbf9fb74d5..174cf5e38962 100644 --- a/drivers/net/wireless/iwlwifi/pcie/1000.c +++ b/drivers/net/wireless/iwlwifi/pcie/1000.c @@ -71,6 +71,7 @@ static const struct iwl_base_params iwl1000_base_params = { static const struct iwl_ht_params iwl1000_ht_params = { .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; #define IWL_DEVICE_1000 \ diff --git a/drivers/net/wireless/iwlwifi/pcie/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c index 062c07b17056..8d4d7295aa46 100644 --- a/drivers/net/wireless/iwlwifi/pcie/2000.c +++ b/drivers/net/wireless/iwlwifi/pcie/2000.c @@ -104,6 +104,7 @@ static const struct iwl_base_params iwl2030_base_params = { static const struct iwl_ht_params iwl2000_ht_params = { .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; static const struct iwl_bt_params iwl2030_bt_params = { diff --git a/drivers/net/wireless/iwlwifi/pcie/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c index 92e502e6e7e5..75d90a9c184a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/5000.c +++ b/drivers/net/wireless/iwlwifi/pcie/5000.c @@ -69,6 +69,7 @@ static const struct iwl_base_params iwl5000_base_params = { static const struct iwl_ht_params iwl5000_ht_params = { .ht_greenfield_support = true, + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; #define IWL_DEVICE_5000 \ diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c index f9e396182ef3..7b9c02cc67e5 100644 --- a/drivers/net/wireless/iwlwifi/pcie/6000.c +++ b/drivers/net/wireless/iwlwifi/pcie/6000.c @@ -124,6 +124,7 @@ static const struct iwl_base_params iwl6000_g2_base_params = { static const struct iwl_ht_params iwl6000_ht_params = { .ht_greenfield_support = true, .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; static const struct iwl_bt_params iwl6000_bt_params = { -- cgit v1.2.3 From 588d932044169979bf028eab2155f9acd1d0418d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 28 May 2012 16:33:48 +0300 Subject: iwlwifi: SCD_INT_MSK is obsolete - don't update it The scheduler can issue an interrupt when moving the read pointer. To get this interrupt, the driver needs to configure what queue can issue an interrupt when its read pointer moves in the scheduler: this is the SCD_INT_MSK. The driver also needs to enable the interrupt in CSR_INT_MASK (bit CSR_INT_BIT_SCD). Since we don't enable the scheduler interrupt in CSR_INT_MASK, there is no point in requesting an interrupt from the scheduler: it will be masked anyway. So don't configure the scheduler to issue interrupts at all. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 3 --- drivers/net/wireless/iwlwifi/pcie/tx.c | 4 ---- 2 files changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index a19ce40c0092..a267fda69f36 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1082,9 +1082,6 @@ static void iwl_tx_start(struct iwl_trans *trans) SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); } - iwl_write_prph(trans, SCD_INTERRUPT_MASK, - IWL_MASK(0, trans->cfg->base_params->num_of_queues)); - /* Activate all Tx DMA/FIFO channels */ iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 583451c710e3..8a331a90f28e 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -480,8 +480,6 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, ((frame_limit << SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - iwl_set_bits_prph(trans, SCD_INTERRUPT_MASK, (1 << txq_id)); - /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], fifo, true); @@ -506,8 +504,6 @@ void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int txq_id) trans_pcie->txq[txq_id].q.write_ptr = 0; iwl_trans_set_wr_ptrs(trans, txq_id, 0); - iwl_clear_bits_prph(trans, SCD_INTERRUPT_MASK, BIT(txq_id)); - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, false); } -- cgit v1.2.3 From fc248615314c41abc2d6957e095c418ea9b12412 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 28 May 2012 16:46:20 +0300 Subject: iwlwifi: first config the SCD queues, then activate them The DMA channels of the FH should be activated after the configuration of the SCD queues too. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index a267fda69f36..2d330158e7b9 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1030,6 +1030,10 @@ static void iwl_tx_start(struct iwl_trans *trans) spin_lock_irqsave(&trans_pcie->irq_lock, flags); + /* make sure all queue are not stopped/used */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + trans_pcie->scd_base_addr = iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; @@ -1050,17 +1054,6 @@ static void iwl_tx_start(struct iwl_trans *trans) iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, trans_pcie->scd_bc_tbls.dma >> 10); - /* Enable DMA channel */ - for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) - iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); - - /* Update FH chicken bits */ - reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); - iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, - reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - iwl_write_prph(trans, SCD_QUEUECHAIN_SEL, SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie)); iwl_write_prph(trans, SCD_AGGR_SEL, 0); @@ -1082,15 +1075,8 @@ static void iwl_tx_start(struct iwl_trans *trans) SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); } - /* Activate all Tx DMA/FIFO channels */ - iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); - iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0); - /* make sure all queue are not stopped/used */ - memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); - memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); - for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { int fifo = trans_pcie->setup_q_to_fifo[i]; @@ -1100,6 +1086,20 @@ static void iwl_tx_start(struct iwl_trans *trans) fifo, true); } + /* Activate all Tx DMA/FIFO channels */ + iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); + + /* Enable DMA channel */ + for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) + iwl_write_direct32(trans, FH_TCSR_CHNL_TX_CONFIG_REG(chan), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); + + /* Update FH chicken bits */ + reg_val = iwl_read_direct32(trans, FH_TX_CHICKEN_BITS_REG); + iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, + reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); /* Enable L1-Active */ -- cgit v1.2.3 From 559b1a257ddeac637b8bb3a2c2469aaa2bde5383 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 28 May 2012 18:49:40 +0300 Subject: iwlwifi: use iwl_trans_set_wr_ptrs to init the queues This functions does the job so use it instead of duplicating the code. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 2d330158e7b9..0d0c7d8fea46 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1060,8 +1060,7 @@ static void iwl_tx_start(struct iwl_trans *trans) /* initiate the queues */ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - iwl_write_prph(trans, SCD_QUEUE_RDPTR(i), 0); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, 0 | (i << 8)); + iwl_trans_set_wr_ptrs(trans, i, 0); iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(i), 0); iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + @@ -1075,8 +1074,6 @@ static void iwl_tx_start(struct iwl_trans *trans) SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); } - iwl_trans_set_wr_ptrs(trans, trans_pcie->cmd_queue, 0); - for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { int fifo = trans_pcie->setup_q_to_fifo[i]; -- cgit v1.2.3 From d0624be65ade709ef1a4220451a474be1ad01af9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 29 May 2012 13:07:30 +0300 Subject: iwlwifi: s/txq_agg_disable/txq_disable We need to be able to enable / disable Tx queues in HW dynamically. So this function is no longer related to AGG only. It can do the job for any queue, even AC ones. Change the name to better reflect its role. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/tx.c | 6 +++--- drivers/net/wireless/iwlwifi/iwl-trans.h | 8 ++++---- drivers/net/wireless/iwlwifi/pcie/internal.h | 2 +- drivers/net/wireless/iwlwifi/pcie/trans.c | 2 +- drivers/net/wireless/iwlwifi/pcie/tx.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 52f2cae080c3..494a25c2f8aa 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -599,7 +599,7 @@ turn_off: * time, or we hadn't time to drain the AC queues. */ if (agg_state == IWL_AGG_ON) - iwl_trans_tx_agg_disable(priv->trans, txq_id); + iwl_trans_txq_disable(priv->trans, txq_id); else IWL_DEBUG_TX_QUEUES(priv, "Don't disable tx agg: %d\n", agg_state); @@ -755,8 +755,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) IWL_DEBUG_TX_QUEUES(priv, "Can continue DELBA flow ssn = next_recl =" " %d", tid_data->next_reclaimed); - iwl_trans_tx_agg_disable(priv->trans, - tid_data->agg.txq_id); + iwl_trans_txq_disable(priv->trans, + tid_data->agg.txq_id); iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); tid_data->agg.state = IWL_AGG_OFF; ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 0aeeb7ce91c7..1fff285b6435 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -358,7 +358,7 @@ struct iwl_trans; * @tx_agg_setup: setup a tx queue for AMPDU - will be called once the HW is * ready and a successful ADDBA response has been received. * May sleep - * @tx_agg_disable: de-configure a Tx queue to send AMPDUs + * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic * @wait_tx_queue_empty: wait until all tx queues are empty * May sleep @@ -393,7 +393,7 @@ struct iwl_trans_ops { void (*tx_agg_setup)(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); - void (*tx_agg_disable)(struct iwl_trans *trans, int queue); + void (*txq_disable)(struct iwl_trans *trans, int queue); int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*wait_tx_queue_empty)(struct iwl_trans *trans); @@ -543,12 +543,12 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, trans->ops->reclaim(trans, queue, ssn, skbs); } -static inline void iwl_trans_tx_agg_disable(struct iwl_trans *trans, int queue) +static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); - trans->ops->tx_agg_disable(trans, queue); + trans->ops->txq_disable(trans, queue); } static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans, int queue, diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index f027769933d9..f802b5ac4a68 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -339,7 +339,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt); -void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int queue); +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, struct iwl_tx_queue *txq, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 0d0c7d8fea46..7c7702a48399 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -2039,7 +2039,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { .tx = iwl_trans_pcie_tx, .reclaim = iwl_trans_pcie_reclaim, - .tx_agg_disable = iwl_trans_pcie_tx_agg_disable, + .txq_disable = iwl_trans_pcie_txq_disable, .tx_agg_setup = iwl_trans_pcie_tx_agg_setup, .dbgfs_register = iwl_trans_pcie_dbgfs_register, diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8a331a90f28e..98ddc802213e 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -487,7 +487,7 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } -void iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int txq_id) +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); -- cgit v1.2.3 From 4beaf6c2f8af52902bcd55b51f9ff8c8f547d485 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 29 May 2012 11:29:10 +0300 Subject: iwlwifi: s/txq_setup/txq_enable We need to be able to enable / disable Tx queues in HW dynamically. So this function is no longer related to AGG only. It can do the job for any queue, even AC ones. Change the name to better reflect its role. Also use the new function to configure the AC / CMD queues in tx_start. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +- drivers/net/wireless/iwlwifi/dvm/tx.c | 5 ++-- drivers/net/wireless/iwlwifi/iwl-trans.h | 14 ++++----- drivers/net/wireless/iwlwifi/pcie/internal.h | 7 +++-- drivers/net/wireless/iwlwifi/pcie/trans.c | 29 +++---------------- drivers/net/wireless/iwlwifi/pcie/tx.c | 43 ++++++++++++++++++++-------- 6 files changed, 50 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 33603c5a24a8..32a823d7497f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -665,7 +665,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, ret = iwl_sta_rx_agg_stop(priv, sta, tid); break; case IEEE80211_AMPDU_TX_START: - if (!priv->trans->ops->tx_agg_setup) + if (!priv->trans->ops->txq_enable) break; if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) break; diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 494a25c2f8aa..7fdbd175c551 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -688,9 +688,8 @@ int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; - iwl_trans_tx_agg_setup(priv->trans, q, fifo, - sta_priv->sta_id, tid, - buf_size, ssn); + iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid, + buf_size, ssn); /* * If the limit is 0, then it wasn't initialised yet, diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 1fff285b6435..90ef0dc5f275 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -355,7 +355,7 @@ struct iwl_trans; * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. * Must be atomic - * @tx_agg_setup: setup a tx queue for AMPDU - will be called once the HW is + * @txq_enable: setup a tx queue for AMPDU - will be called once the HW is * ready and a successful ADDBA response has been received. * May sleep * @txq_disable: de-configure a Tx queue to send AMPDUs @@ -391,8 +391,8 @@ struct iwl_trans_ops { void (*reclaim)(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs); - void (*tx_agg_setup)(struct iwl_trans *trans, int queue, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn); + void (*txq_enable)(struct iwl_trans *trans, int queue, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn); void (*txq_disable)(struct iwl_trans *trans, int queue); int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); @@ -551,16 +551,16 @@ static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue) trans->ops->txq_disable(trans, queue); } -static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans, int queue, - int fifo, int sta_id, int tid, - int frame_limit, u16 ssn) +static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, + int fifo, int sta_id, int tid, + int frame_limit, u16 ssn) { might_sleep(); WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "%s bad state = %d", __func__, trans->state); - trans->ops->tx_agg_setup(trans, queue, fifo, sta_id, tid, + trans->ops->txq_enable(trans, queue, fifo, sta_id, tid, frame_limit, ssn); } diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index f802b5ac4a68..94201c4d6227 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -344,8 +344,11 @@ void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, struct iwl_tx_queue *txq, int tx_fifo_id, bool active); -void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn); +void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, + int fifo, int sta_id, int tid, + int frame_limit, u16 ssn); +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn); void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 7c7702a48399..0f59e1f2bcac 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1054,33 +1054,12 @@ static void iwl_tx_start(struct iwl_trans *trans) iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, trans_pcie->scd_bc_tbls.dma >> 10); - iwl_write_prph(trans, SCD_QUEUECHAIN_SEL, - SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie)); - iwl_write_prph(trans, SCD_AGGR_SEL, 0); - - /* initiate the queues */ - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - iwl_trans_set_wr_ptrs(trans, i, 0); - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(i), 0); - iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(i) + - sizeof(u32), - ((SCD_WIN_SIZE << - SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((SCD_FRAME_LIMIT << - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - } - for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { int fifo = trans_pcie->setup_q_to_fifo[i]; - set_bit(i, trans_pcie->queue_used); - - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i], - fifo, true); + __iwl_trans_pcie_txq_enable(trans, i, fifo, IWL_INVALID_STATION, + IWL_TID_NON_QOS, + SCD_FRAME_LIMIT, 0); } /* Activate all Tx DMA/FIFO channels */ @@ -2040,7 +2019,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { .reclaim = iwl_trans_pcie_reclaim, .txq_disable = iwl_trans_pcie_txq_disable, - .tx_agg_setup = iwl_trans_pcie_tx_agg_setup, + .txq_enable = iwl_trans_pcie_txq_enable, .dbgfs_register = iwl_trans_pcie_dbgfs_register, diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 98ddc802213e..35e82161ca43 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -442,29 +442,34 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } -void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn) +void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, + int fifo, int sta_id, int tid, + int frame_limit, u16 ssn) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - u16 ra_tid = BUILD_RAxTID(sta_id, tid); + + lockdep_assert_held(&trans_pcie->irq_lock); if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - /* Stop this Tx queue before configuring it */ iwlagn_tx_queue_stop_scheduler(trans, txq_id); - /* Map receiver-address / traffic-ID to this queue */ - iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id); + /* Set this queue as a chain-building queue unless it is CMD queue */ + if (txq_id != trans_pcie->cmd_queue) + iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); + + /* If this queue is mapped to a certain station: it is an AGG queue */ + if (sta_id != IWL_INVALID_STATION) { + u16 ra_tid = BUILD_RAxTID(sta_id, tid); - /* Set this queue as a chain-building queue */ - iwl_set_bits_prph(trans, SCD_QUEUECHAIN_SEL, BIT(txq_id)); + /* Map receiver-address / traffic-ID to this queue */ + iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id); - /* enable aggregations for the queue */ - iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + /* enable aggregations for the queue */ + iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + } /* Place first TFD at index corresponding to start sequence number. * Assumes that ssn_idx is valid (!= 0xFFF) */ @@ -473,6 +478,8 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, iwl_trans_set_wr_ptrs(trans, txq_id, ssn); /* Set up Tx window size and frame limit for this queue */ + iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(txq_id), 0); iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), ((frame_limit << SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & @@ -483,6 +490,18 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int txq_id, int fifo, /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], fifo, true); +} + +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + __iwl_trans_pcie_txq_enable(trans, txq_id, fifo, sta_id, + tid, frame_limit, ssn); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } -- cgit v1.2.3 From cde5b48700bde6edc24c26d3d1bbec219e0ef2a1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 28 May 2012 16:55:34 +0300 Subject: iwlwifi: update mask value for SCD queue config Although we don't use bit 24, this bit is valid, but bit 23 is not. Update the mask accordingly. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-prph.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 3b1069290fa9..a9f0415916c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -187,7 +187,7 @@ #define SCD_QUEUE_STTS_REG_POS_ACTIVE (3) #define SCD_QUEUE_STTS_REG_POS_WSL (4) #define SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19) -#define SCD_QUEUE_STTS_REG_MSK (0x00FF0000) +#define SCD_QUEUE_STTS_REG_MSK (0x017F0000) #define SCD_QUEUE_CTX_REG1_CREDIT_POS (8) #define SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00) -- cgit v1.2.3 From 59c647b6266506cbbceadfabfd01168148f026c5 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 24 May 2012 19:24:34 +0300 Subject: iwlwifi: allocate Tx cmd pool per device Different transports will have different needs: New tranports need headroom for their own use before the Tx cmd. So allocate the Tx cmd pool in the transport and give it a unique name based on dev_name. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/dev.h | 2 -- drivers/net/wireless/iwlwifi/dvm/main.c | 22 ++++------------------ drivers/net/wireless/iwlwifi/dvm/tx.c | 12 ++++++------ drivers/net/wireless/iwlwifi/iwl-trans.h | 29 +++++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/pcie/trans.c | 19 +++++++++++++++++++ 5 files changed, 58 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index af9de9542922..c29e6378a11d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -1001,8 +1001,6 @@ struct iwl_priv { enum iwl_ucode_type cur_ucode; }; /*iwl_priv */ -extern struct kmem_cache *iwl_tx_cmd_pool; - static inline struct iwl_rxon_context * iwl_rxon_ctx_from_vif(struct ieee80211_vif *vif) { diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 441188725985..6cca1f403da3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -2290,10 +2290,11 @@ void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) static void iwl_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) { + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); struct ieee80211_tx_info *info; info = IEEE80211_SKB_CB(skb); - kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1])); + iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); dev_kfree_skb_any(skb); } @@ -2328,9 +2329,6 @@ const struct iwl_op_mode_ops iwl_dvm_ops = { * driver and module entry point * *****************************************************************************/ - -struct kmem_cache *iwl_tx_cmd_pool; - static int __init iwl_init(void) { @@ -2338,29 +2336,18 @@ static int __init iwl_init(void) pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); pr_info(DRV_COPYRIGHT "\n"); - iwl_tx_cmd_pool = kmem_cache_create("iwl_dev_cmd", - sizeof(struct iwl_device_cmd), - sizeof(void *), 0, NULL); - if (!iwl_tx_cmd_pool) - return -ENOMEM; - ret = iwlagn_rate_control_register(); if (ret) { pr_err("Unable to register rate control algorithm: %d\n", ret); - goto error_rc_register; + return ret; } ret = iwl_opmode_register("iwldvm", &iwl_dvm_ops); if (ret) { pr_err("Unable to register op_mode: %d\n", ret); - goto error_opmode_register; + iwlagn_rate_control_unregister(); } - return ret; -error_opmode_register: - iwlagn_rate_control_unregister(); -error_rc_register: - kmem_cache_destroy(iwl_tx_cmd_pool); return ret; } module_init(iwl_init); @@ -2369,6 +2356,5 @@ static void __exit iwl_exit(void) { iwl_opmode_deregister("iwldvm"); iwlagn_rate_control_unregister(); - kmem_cache_destroy(iwl_tx_cmd_pool); } module_exit(iwl_exit); diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 7fdbd175c551..7e3bd6281551 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -295,7 +295,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_station_priv *sta_priv = NULL; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_device_cmd *dev_cmd = NULL; + struct iwl_device_cmd *dev_cmd; struct iwl_tx_cmd *tx_cmd; __le16 fc; u8 hdr_len; @@ -377,7 +377,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (info->flags & IEEE80211_TX_CTL_AMPDU) is_agg = true; - dev_cmd = kmem_cache_alloc(iwl_tx_cmd_pool, GFP_ATOMIC); + dev_cmd = iwl_trans_alloc_tx_cmd(priv->trans); if (unlikely(!dev_cmd)) goto drop_unlock_priv; @@ -492,7 +492,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) drop_unlock_sta: if (dev_cmd) - kmem_cache_free(iwl_tx_cmd_pool, dev_cmd); + iwl_trans_free_tx_cmd(priv->trans, dev_cmd); spin_unlock(&priv->sta_lock); drop_unlock_priv: return -1; @@ -1193,8 +1193,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, info = IEEE80211_SKB_CB(skb); ctx = info->driver_data[0]; - kmem_cache_free(iwl_tx_cmd_pool, - (info->driver_data[1])); + iwl_trans_free_tx_cmd(priv->trans, + info->driver_data[1]); memset(&info->status, 0, sizeof(info->status)); @@ -1357,7 +1357,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, WARN_ON_ONCE(1); info = IEEE80211_SKB_CB(skb); - kmem_cache_free(iwl_tx_cmd_pool, (info->driver_data[1])); + iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); if (freed == 1) { /* this is the first skb we deliver in this batch */ diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 90ef0dc5f275..00efde8e5536 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -433,6 +433,11 @@ enum iwl_trans_state { * @hw_id_str: a string with info about HW ID. Set during transport allocation. * @pm_support: set to true in start_hw if link pm is supported * @wait_command_queue: the wait_queue for SYNC host commands + * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. + * The user should use iwl_trans_{alloc,free}_tx_cmd. + * @dev_cmd_headroom: room needed for the transport's private use before the + * device_cmd for Tx - for internal use only + * The user should use iwl_trans_{alloc,free}_tx_cmd. */ struct iwl_trans { const struct iwl_trans_ops *ops; @@ -450,6 +455,10 @@ struct iwl_trans { wait_queue_head_t wait_command_queue; + /* The following fields are internal only */ + struct kmem_cache *dev_cmd_pool; + size_t dev_cmd_headroom; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); @@ -525,6 +534,26 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans, return trans->ops->send_cmd(trans, cmd); } +static inline struct iwl_device_cmd * +iwl_trans_alloc_tx_cmd(struct iwl_trans *trans) +{ + u8 *dev_cmd_ptr = kmem_cache_alloc(trans->dev_cmd_pool, GFP_ATOMIC); + + if (unlikely(dev_cmd_ptr == NULL)) + return NULL; + + return (struct iwl_device_cmd *) + (dev_cmd_ptr + trans->dev_cmd_headroom); +} + +static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, + struct iwl_device_cmd *dev_cmd) +{ + u8 *dev_cmd_ptr = (u8 *)dev_cmd - trans->dev_cmd_headroom; + + kmem_cache_free(trans->dev_cmd_pool, dev_cmd_ptr); +} + static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int queue) { diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 0f59e1f2bcac..1eabb834e32a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1528,6 +1528,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) iounmap(trans_pcie->hw_base); pci_release_regions(trans_pcie->pci_dev); pci_disable_device(trans_pcie->pci_dev); + kmem_cache_destroy(trans->dev_cmd_pool); kfree(trans); } @@ -2042,6 +2043,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, { struct iwl_trans_pcie *trans_pcie; struct iwl_trans *trans; + char cmd_pool_name[100]; u16 pci_cmd; int err; @@ -2138,8 +2140,25 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, init_waitqueue_head(&trans->wait_command_queue); spin_lock_init(&trans->reg_lock); + snprintf(cmd_pool_name, sizeof(cmd_pool_name), "iwl_cmd_pool:%s", + dev_name(trans->dev)); + + trans->dev_cmd_headroom = 0; + trans->dev_cmd_pool = + kmem_cache_create(cmd_pool_name, + sizeof(struct iwl_device_cmd) + + trans->dev_cmd_headroom, + sizeof(void *), + SLAB_HWCACHE_ALIGN, + NULL); + + if (!trans->dev_cmd_pool) + goto out_pci_disable_msi; + return trans; +out_pci_disable_msi: + pci_disable_msi(pdev); out_pci_release_regions: pci_release_regions(pdev); out_pci_disable_device: -- cgit v1.2.3 From 2baa2e57a9add3f6e5add31f28404ca7b8efecb4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 May 2012 14:47:30 +0200 Subject: iwlwifi: add iwl_set_bits_mask In a few cases we need to set a value in a certain mask inside a register, add the function iwl_set_bits_mask() to make such code easy. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-io.c | 18 ++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-io.h | 2 ++ 2 files changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index ee93274214d6..5f2df70b73c1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -65,6 +65,24 @@ void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask) } EXPORT_SYMBOL_GPL(iwl_clear_bit); +void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value) +{ + unsigned long flags; + u32 v; + +#ifdef CONFIG_IWLWIFI_DEBUG + WARN_ON_ONCE(value & ~mask); +#endif + + spin_lock_irqsave(&trans->reg_lock, flags); + v = iwl_read32(trans, reg); + v &= ~mask; + v |= value; + iwl_write32(trans, reg, v); + spin_unlock_irqrestore(&trans->reg_lock, flags); +} +EXPORT_SYMBOL_GPL(iwl_set_bits_mask); + int iwl_poll_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout) { diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index abb3250164ba..4a9a45f771ed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -54,6 +54,8 @@ static inline u32 iwl_read32(struct iwl_trans *trans, u32 ofs) void iwl_set_bit(struct iwl_trans *trans, u32 reg, u32 mask); void iwl_clear_bit(struct iwl_trans *trans, u32 reg, u32 mask); +void iwl_set_bits_mask(struct iwl_trans *trans, u32 reg, u32 mask, u32 value); + int iwl_poll_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout); int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, -- cgit v1.2.3 From 7b6a2be9e40c9ff7909fdf39d20ad78ba7822040 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 29 May 2012 17:30:43 +0300 Subject: iwlwifi: fix rf configuration Johannes noticed this was completely messed up. We got confused between masks and bit position. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/eeprom.c | 17 +++++++++++++---- drivers/net/wireless/iwlwifi/iwl-csr.h | 16 ++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/iwlwifi/dvm/eeprom.c index b4da76d5d97e..b382b16b89d7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/eeprom.c +++ b/drivers/net/wireless/iwlwifi/dvm/eeprom.c @@ -1136,10 +1136,19 @@ void iwl_rf_config(struct iwl_priv *priv) /* write radio config values to register */ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | - EEPROM_RF_CFG_STEP_MSK(radio_cfg) | - EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + u32 reg_val = + EEPROM_RF_CFG_TYPE_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | + EEPROM_RF_CFG_STEP_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | + EEPROM_RF_CFG_DASH_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; + + iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | + CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | + CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val); + IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", EEPROM_RF_CFG_TYPE_MSK(radio_cfg), EEPROM_RF_CFG_STEP_MSK(radio_cfg), diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 59750543fce7..41dc6911b164 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -155,9 +155,21 @@ #define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) /* Bits for CSR_HW_IF_CONFIG_REG */ -#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00) -#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) +#define CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003) +#define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C) +#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0) +#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) #define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) +#define CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00) +#define CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000) +#define CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000) + +#define CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0) +#define CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2) +#define CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6) +#define CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10) +#define CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12) +#define CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14) #define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) #define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) -- cgit v1.2.3 From 08838cdeca65e754af5c755a05f6cdb1c632eda8 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 29 May 2012 17:48:08 +0300 Subject: iwlwifi: configure the SKU in the HW This was missing. Fix the mask of the REV_TYPE on the way. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 9 +++++++++ drivers/net/wireless/iwlwifi/iwl-csr.h | 12 ++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 6cca1f403da3..10a2800687bc 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -2218,6 +2218,15 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + /* SKU Control */ + iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | + CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP, + (CSR_HW_REV_STEP(priv->trans->hw_rev) << + CSR_HW_IF_CONFIG_REG_POS_MAC_STEP) | + (CSR_HW_REV_DASH(priv->trans->hw_rev) << + CSR_HW_IF_CONFIG_REG_POS_MAC_DASH)); + priv->lib->nic_config(priv); } diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 41dc6911b164..34a5287dfc2f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -97,13 +97,10 @@ /* * Hardware revision info * Bit fields: - * 31-8: Reserved - * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions + * 31-16: Reserved + * 15-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D * 1-0: "Dash" (-) value, as in A-1, etc. - * - * NOTE: Revision step affects calculation of CCK txpower for 4965. - * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965). */ #define CSR_HW_REV (CSR_BASE+0x028) @@ -282,7 +279,10 @@ /* HW REV */ -#define CSR_HW_REV_TYPE_MSK (0x00001F0) +#define CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0) +#define CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2) + +#define CSR_HW_REV_TYPE_MSK (0x000FFF0) #define CSR_HW_REV_TYPE_5300 (0x0000020) #define CSR_HW_REV_TYPE_5350 (0x0000030) #define CSR_HW_REV_TYPE_5100 (0x0000050) -- cgit v1.2.3 From 26a7ca9a71a3f7e1826de96b1a1e907123e11b07 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 May 2012 11:55:54 +0200 Subject: iwlwifi: refactor EEPROM reading/parsing The EEPROM reading/parsing code is all mixed in the driver today, and the EEPROM is parsed only when we access data from it. This is problematic because the NVM needs to be parsed and that is independent of reading it. Also, the NVM format for new devices will be different and probably require a new parser. Therefore refactor the reading and parsing and create two independent components. Reading the EEPROM requires direct hardware accesses and therefore access to the transport, but parsing is independent and can be done on an NVM blob. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/Makefile | 1 + drivers/net/wireless/iwlwifi/dvm/Makefile | 2 +- drivers/net/wireless/iwlwifi/dvm/agn.h | 5 +- drivers/net/wireless/iwlwifi/dvm/calib.c | 17 +- drivers/net/wireless/iwlwifi/dvm/debugfs.c | 23 +- drivers/net/wireless/iwlwifi/dvm/dev.h | 128 +-- drivers/net/wireless/iwlwifi/dvm/devices.c | 128 +-- drivers/net/wireless/iwlwifi/dvm/eeprom.c | 1163 ----------------------- drivers/net/wireless/iwlwifi/dvm/eeprom.h | 269 ------ drivers/net/wireless/iwlwifi/dvm/lib.c | 8 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 9 +- drivers/net/wireless/iwlwifi/dvm/main.c | 301 ++---- drivers/net/wireless/iwlwifi/dvm/power.c | 1 - drivers/net/wireless/iwlwifi/dvm/rs.c | 44 +- drivers/net/wireless/iwlwifi/dvm/rx.c | 1 - drivers/net/wireless/iwlwifi/dvm/rxon.c | 4 +- drivers/net/wireless/iwlwifi/dvm/scan.c | 10 +- drivers/net/wireless/iwlwifi/dvm/sta.c | 30 +- drivers/net/wireless/iwlwifi/dvm/testmode.c | 10 +- drivers/net/wireless/iwlwifi/dvm/tt.c | 1 - drivers/net/wireless/iwlwifi/dvm/tx.c | 10 +- drivers/net/wireless/iwlwifi/dvm/ucode.c | 25 +- drivers/net/wireless/iwlwifi/iwl-config.h | 19 + drivers/net/wireless/iwlwifi/iwl-debug.h | 5 +- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c | 900 ++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h | 138 +++ drivers/net/wireless/iwlwifi/iwl-eeprom-read.c | 463 +++++++++ drivers/net/wireless/iwlwifi/iwl-eeprom-read.h | 70 ++ drivers/net/wireless/iwlwifi/pcie/1000.c | 14 + drivers/net/wireless/iwlwifi/pcie/2000.c | 17 + drivers/net/wireless/iwlwifi/pcie/5000.c | 15 + drivers/net/wireless/iwlwifi/pcie/6000.c | 19 + 32 files changed, 1854 insertions(+), 1996 deletions(-) delete mode 100644 drivers/net/wireless/iwlwifi/dvm/eeprom.c delete mode 100644 drivers/net/wireless/iwlwifi/dvm/eeprom.h create mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c create mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h create mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-read.c create mode 100644 drivers/net/wireless/iwlwifi/iwl-eeprom-read.h (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 7ba6b8dc1cbd..98c8f6449649 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -8,6 +8,7 @@ iwlwifi-objs += iwl-io.o iwlwifi-objs += iwl-drv.o iwlwifi-objs += iwl-debug.o iwlwifi-objs += iwl-notif-wait.o +iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o iwlwifi-objs += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/iwlwifi/dvm/Makefile index 13f8282f4d0e..5ff76b204141 100644 --- a/drivers/net/wireless/iwlwifi/dvm/Makefile +++ b/drivers/net/wireless/iwlwifi/dvm/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_IWLDVM) += iwldvm.o iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o iwldvm-objs += lib.o calib.o tt.o sta.o rx.o -iwldvm-objs += eeprom.o power.o +iwldvm-objs += power.o iwldvm-objs += scan.o led.o iwldvm-objs += rxon.o devices.o diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 9a8dd1a579d0..2ae3608472a6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -92,7 +92,6 @@ extern struct iwl_lib_ops iwl6030_lib; #define STATUS_CT_KILL 1 #define STATUS_ALIVE 2 #define STATUS_READY 3 -#define STATUS_GEO_CONFIGURED 4 #define STATUS_EXIT_PENDING 5 #define STATUS_STATISTICS 6 #define STATUS_SCANNING 7 @@ -443,10 +442,8 @@ static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, static inline int iwl_is_ready(struct iwl_priv *priv) { - /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are - * set but EXIT_PENDING is not */ + /* The adapter is 'ready' if READY EXIT_PENDING is not set */ return test_bit(STATUS_READY, &priv->status) && - test_bit(STATUS_GEO_CONFIGURED, &priv->status) && !test_bit(STATUS_EXIT_PENDING, &priv->status); } diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index 7eb3fa3df7c4..f2dd671d7dc8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -833,14 +833,14 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, * To be safe, simply mask out any chains that we know * are not on the device. */ - active_chains &= priv->hw_params.valid_rx_ant; + active_chains &= priv->eeprom_data->valid_rx_ant; num_tx_chains = 0; for (i = 0; i < NUM_RX_CHAINS; i++) { /* loops on all the bits of * priv->hw_setting.valid_tx_ant */ u8 ant_msk = (1 << i); - if (!(priv->hw_params.valid_tx_ant & ant_msk)) + if (!(priv->eeprom_data->valid_tx_ant & ant_msk)) continue; num_tx_chains++; @@ -854,7 +854,7 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, * connect the first valid tx chain */ first_chain = - find_first_chain(priv->hw_params.valid_tx_ant); + find_first_chain(priv->eeprom_data->valid_tx_ant); data->disconn_array[first_chain] = 0; active_chains |= BIT(first_chain); IWL_DEBUG_CALIB(priv, @@ -864,13 +864,13 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, } } - if (active_chains != priv->hw_params.valid_rx_ant && + if (active_chains != priv->eeprom_data->valid_rx_ant && active_chains != priv->chain_noise_data.active_chains) IWL_DEBUG_CALIB(priv, "Detected that not all antennas are connected! " "Connected: %#x, valid: %#x.\n", active_chains, - priv->hw_params.valid_rx_ant); + priv->eeprom_data->valid_rx_ant); /* Save for use within RXON, TX, SCAN commands, etc. */ data->active_chains = active_chains; @@ -1055,7 +1055,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) priv->cfg->bt_params->advanced_bt_coexist) { /* Disable disconnected antenna algorithm for advanced bt coex, assuming valid antennas are connected */ - data->active_chains = priv->hw_params.valid_rx_ant; + data->active_chains = priv->eeprom_data->valid_rx_ant; for (i = 0; i < NUM_RX_CHAINS; i++) if (!(data->active_chains & (1<disconn_array[i] = 1; @@ -1084,8 +1084,9 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", min_average_noise, min_average_noise_antenna_i); - iwlagn_gain_computation(priv, average_noise, - find_first_chain(priv->hw_params.valid_rx_ant)); + iwlagn_gain_computation( + priv, average_noise, + find_first_chain(priv->eeprom_data->valid_rx_ant)); /* Some power changes may have been made during the calibration. * Update and commit the RXON diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index 2268adddf4c0..8a2d9e643b14 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -303,13 +303,13 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, const u8 *ptr; char *buf; u16 eeprom_ver; - size_t eeprom_len = priv->cfg->base_params->eeprom_size; + size_t eeprom_len = priv->eeprom_blob_size; buf_size = 4 * eeprom_len + 256; if (eeprom_len % 16) return -ENODATA; - ptr = priv->eeprom; + ptr = priv->eeprom_blob; if (!ptr) return -ENOMEM; @@ -318,11 +318,9 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, if (!buf) return -ENOMEM; - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s, " - "version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", eeprom_ver); + eeprom_ver = priv->eeprom_data->eeprom_version; + pos += scnprintf(buf + pos, buf_size - pos, + "NVM version: 0x%x\n", eeprom_ver); for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, @@ -347,9 +345,6 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, char *buf; ssize_t ret; - if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status)) - return -EAGAIN; - buf = kzalloc(bufsz, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -422,8 +417,6 @@ static ssize_t iwl_dbgfs_status_read(struct file *file, test_bit(STATUS_ALIVE, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", test_bit(STATUS_READY, &priv->status)); - pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n", - test_bit(STATUS_GEO_CONFIGURED, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", test_bit(STATUS_EXIT_PENDING, &priv->status)); pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", @@ -1337,17 +1330,17 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { pos += scnprintf(buf + pos, bufsz - pos, "tx power: (1/2 dB step)\n"); - if ((priv->hw_params.valid_tx_ant & ANT_A) && + if ((priv->eeprom_data->valid_tx_ant & ANT_A) && tx->tx_power.ant_a) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna A:", tx->tx_power.ant_a); - if ((priv->hw_params.valid_tx_ant & ANT_B) && + if ((priv->eeprom_data->valid_tx_ant & ANT_B) && tx->tx_power.ant_b) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna B:", tx->tx_power.ant_b); - if ((priv->hw_params.valid_tx_ant & ANT_C) && + if ((priv->eeprom_data->valid_tx_ant & ANT_C) && tx->tx_power.ant_c) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna C:", diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index c29e6378a11d..89f2e1040e7f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -39,6 +39,7 @@ #include #include "iwl-fw.h" +#include "iwl-eeprom-parse.h" #include "iwl-csr.h" #include "iwl-debug.h" #include "iwl-agn-hw.h" @@ -46,7 +47,6 @@ #include "iwl-notif-wait.h" #include "iwl-trans.h" -#include "eeprom.h" #include "led.h" #include "power.h" #include "rs.h" @@ -88,33 +88,6 @@ #define IWL_NUM_SCAN_RATES (2) -/* - * One for each channel, holds all channel setup data - * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant - * with one another! - */ -struct iwl_channel_info { - struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */ - struct iwl_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for - * HT40 channel */ - - u8 channel; /* channel number */ - u8 flags; /* flags copied from EEPROM */ - s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ - s8 min_power; /* always 0 */ - s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ - - u8 group_index; /* 0-4, maps channel to group1/2/3/4/5 */ - u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */ - enum ieee80211_band band; - - /* HT40 channel info */ - s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - u8 ht40_flags; /* flags copied from EEPROM */ - u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ -}; - /* * Minimum number of queues. MAX_NUM is defined in hw specific files. * Set the minimum to accommodate @@ -154,29 +127,6 @@ union iwl_ht_rate_supp { }; }; -#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) -#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) -#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) -#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) -#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K -#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K -#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K - -/* - * Maximal MPDU density for TX aggregation - * 4 - 2us density - * 5 - 4us density - * 6 - 8us density - * 7 - 16us density - */ -#define CFG_HT_MPDU_DENSITY_2USEC (0x4) -#define CFG_HT_MPDU_DENSITY_4USEC (0x5) -#define CFG_HT_MPDU_DENSITY_8USEC (0x6) -#define CFG_HT_MPDU_DENSITY_16USEC (0x7) -#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC -#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC -#define CFG_HT_MPDU_DENSITY_MIN (0x1) - struct iwl_ht_config { bool single_chain_sufficient; enum ieee80211_smps_mode smps; /* current smps mode */ @@ -446,23 +396,6 @@ enum { MEASUREMENT_ACTIVE = (1 << 1), }; -enum iwl_nvm_type { - NVM_DEVICE_TYPE_EEPROM = 0, - NVM_DEVICE_TYPE_OTP, -}; - -/* - * Two types of OTP memory access modes - * IWL_OTP_ACCESS_ABSOLUTE - absolute address mode, - * based on physical memory addressing - * IWL_OTP_ACCESS_RELATIVE - relative address mode, - * based on logical memory addressing - */ -enum iwl_access_mode { - IWL_OTP_ACCESS_ABSOLUTE, - IWL_OTP_ACCESS_RELATIVE, -}; - /* reply_tx_statistics (for _agn devices) */ struct reply_tx_error_statistics { u32 pp_delay; @@ -633,8 +566,6 @@ enum iwl_scan_type { * * @tx_chains_num: Number of TX chains * @rx_chains_num: Number of RX chains - * @valid_tx_ant: usable antennas for TX - * @valid_rx_ant: usable antennas for RX * @sku: sku read from EEPROM * @ct_kill_threshold: temperature threshold - in hw dependent unit * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit @@ -645,8 +576,6 @@ enum iwl_scan_type { struct iwl_hw_params { u8 tx_chains_num; u8 rx_chains_num; - u8 valid_tx_ant; - u8 valid_rx_ant; bool use_rts_for_aggregation; u16 sku; u32 ct_kill_threshold; @@ -663,9 +592,6 @@ struct iwl_lib_ops { /* device specific configuration */ void (*nic_config)(struct iwl_priv *priv); - /* eeprom operations (as defined in eeprom.h) */ - struct iwl_eeprom_ops eeprom_ops; - /* temperature */ void (*temperature)(struct iwl_priv *priv); }; @@ -734,8 +660,6 @@ struct iwl_priv { /* ieee device used by generic ieee processing code */ struct ieee80211_hw *hw; - struct ieee80211_channel *ieee_channels; - struct ieee80211_rate *ieee_rates; struct list_head calib_results; @@ -754,8 +678,6 @@ struct iwl_priv { struct iwl_notif_wait_data notif_wait; - struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; - /* spectrum measurement report caching */ struct iwl_spectrum_notification measure_report; u8 measurement_status; @@ -786,11 +708,6 @@ struct iwl_priv { bool ucode_loaded; bool init_ucode_run; /* Don't run init uCode again */ - /* we allocate array of iwl_channel_info for NIC's valid channels. - * Access via channel # using indirect index array */ - struct iwl_channel_info *channel_info; /* channel info array */ - u8 channel_count; /* # of channels */ - u8 plcp_delta_threshold; /* thermal calibration */ @@ -950,10 +867,8 @@ struct iwl_priv { struct delayed_work scan_check; - /* TX Power */ + /* TX Power settings */ s8 tx_power_user_lmt; - s8 tx_power_device_lmt; - s8 tx_power_lmt_in_half_dbm; /* max tx power in half-dBm format */ s8 tx_power_next; #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -964,9 +879,10 @@ struct iwl_priv { void *wowlan_sram; #endif /* CONFIG_IWLWIFI_DEBUGFS */ - /* eeprom -- this is in the card's little endian byte order */ - u8 *eeprom; - enum iwl_nvm_type nvm_device_type; + struct iwl_eeprom_data *eeprom_data; + /* eeprom blob for debugfs/testmode */ + u8 *eeprom_blob; + size_t eeprom_blob_size; struct work_struct txpower_work; u32 calib_disabled; @@ -1034,36 +950,4 @@ static inline int iwl_is_any_associated(struct iwl_priv *priv) return false; } -static inline int is_channel_valid(const struct iwl_channel_info *ch_info) -{ - if (ch_info == NULL) - return 0; - return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; -} - -static inline int is_channel_radar(const struct iwl_channel_info *ch_info) -{ - return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; -} - -static inline u8 is_channel_a_band(const struct iwl_channel_info *ch_info) -{ - return ch_info->band == IEEE80211_BAND_5GHZ; -} - -static inline u8 is_channel_bg_band(const struct iwl_channel_info *ch_info) -{ - return ch_info->band == IEEE80211_BAND_2GHZ; -} - -static inline int is_channel_passive(const struct iwl_channel_info *ch) -{ - return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; -} - -static inline int is_channel_ibss(const struct iwl_channel_info *ch) -{ - return ((ch->flags & EEPROM_CHANNEL_IBSS)) ? 1 : 0; -} - #endif /* __iwl_dev_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index ddf053c5713d..747e576950f4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -29,11 +29,48 @@ */ #include "iwl-io.h" #include "iwl-prph.h" +#include "iwl-eeprom-parse.h" #include "agn.h" #include "dev.h" #include "commands.h" + +#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 + +static void iwl_rf_config(struct iwl_priv *priv) +{ + u16 radio_cfg = priv->eeprom_data->radio_cfg; + + /* write radio config values to register */ + if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { + u32 reg_val = + EEPROM_RF_CFG_TYPE_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | + EEPROM_RF_CFG_STEP_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | + EEPROM_RF_CFG_DASH_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; + + iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | + CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | + CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val); + + IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", + EEPROM_RF_CFG_TYPE_MSK(radio_cfg), + EEPROM_RF_CFG_STEP_MSK(radio_cfg), + EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + } else { + WARN_ON(1); + } + + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); +} + /* * 1000 series * =========== @@ -180,17 +217,6 @@ static void iwl1000_hw_set_hw_params(struct iwl_priv *priv) struct iwl_lib_ops iwl1000_lib = { .set_hw_params = iwl1000_hw_set_hw_params, .nic_config = iwl1000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - }, .temperature = iwlagn_temperature, }; @@ -251,36 +277,12 @@ static void iwl2000_hw_set_hw_params(struct iwl_priv *priv) struct iwl_lib_ops iwl2000_lib = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - .enhanced_txpower = true, - }, .temperature = iwlagn_temperature, }; struct iwl_lib_ops iwl2030_lib = { .set_hw_params = iwl2000_hw_set_hw_params, .nic_config = iwl2000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REGULATORY_BAND_NO_HT40, - }, - .enhanced_txpower = true, - }, .temperature = iwlagn_temperature, }; @@ -357,11 +359,9 @@ static struct iwl_sensitivity_ranges iwl5150_sensitivity = { static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) { u16 temperature, voltage; - __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(priv, - EEPROM_KELVIN_TEMPERATURE); - temperature = le16_to_cpu(temp_calib[0]); - voltage = le16_to_cpu(temp_calib[1]); + temperature = le16_to_cpu(priv->eeprom_data->kelvin_temperature); + voltage = le16_to_cpu(priv->eeprom_data->kelvin_voltage); /* offset = temp - volt / coeff */ return (s32)(temperature - @@ -478,17 +478,6 @@ struct iwl_lib_ops iwl5000_lib = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .nic_config = iwl5000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - }, .temperature = iwlagn_temperature, }; @@ -496,17 +485,6 @@ struct iwl_lib_ops iwl5150_lib = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, .nic_config = iwl5000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - }, .temperature = iwl5150_temperature, }; @@ -541,13 +519,13 @@ static void iwl6000_nic_config(struct iwl_priv *priv) break; case IWL_DEVICE_FAMILY_6050: /* Indicate calibration version to uCode. */ - if (iwl_eeprom_calib_version(priv) >= 6) + if (priv->eeprom_data->calib_version >= 6) iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); break; case IWL_DEVICE_FAMILY_6150: /* Indicate calibration version to uCode. */ - if (iwl_eeprom_calib_version(priv) >= 6) + if (priv->eeprom_data->calib_version >= 6) iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, @@ -658,18 +636,6 @@ struct iwl_lib_ops iwl6000_lib = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - .enhanced_txpower = true, - }, .temperature = iwlagn_temperature, }; @@ -677,17 +643,5 @@ struct iwl_lib_ops iwl6030_lib = { .set_hw_params = iwl6000_hw_set_hw_params, .set_channel_switch = iwl6000_hw_channel_switch, .nic_config = iwl6000_nic_config, - .eeprom_ops = { - .regulatory_bands = { - EEPROM_REG_BAND_1_CHANNELS, - EEPROM_REG_BAND_2_CHANNELS, - EEPROM_REG_BAND_3_CHANNELS, - EEPROM_REG_BAND_4_CHANNELS, - EEPROM_REG_BAND_5_CHANNELS, - EEPROM_6000_REG_BAND_24_HT40_CHANNELS, - EEPROM_REG_BAND_52_HT40_CHANNELS - }, - .enhanced_txpower = true, - }, .temperature = iwlagn_temperature, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/iwlwifi/dvm/eeprom.c deleted file mode 100644 index b382b16b89d7..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/eeprom.c +++ /dev/null @@ -1,1163 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - - -#include -#include -#include -#include -#include -#include "iwl-io.h" -#include "iwl-prph.h" -#include "iwl-debug.h" -#include "dev.h" -#include "agn.h" -#include "eeprom.h" - -/************************** EEPROM BANDS **************************** - * - * The iwl_eeprom_band definitions below provide the mapping from the - * EEPROM contents to the specific channel number supported for each - * band. - * - * For example, iwl_priv->eeprom.band_3_channels[4] from the band_3 - * definition below maps to physical channel 42 in the 5.2GHz spectrum. - * The specific geography and calibration information for that channel - * is contained in the eeprom map itself. - * - * During init, we copy the eeprom information and channel map - * information into priv->channel_info_24/52 and priv->channel_map_24/52 - * - * channel_map_24/52 provides the index in the channel_info array for a - * given channel. We have to have two separate maps as there is channel - * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and - * band_2 - * - * A value of 0xff stored in the channel_map indicates that the channel - * is not supported by the hardware at all. - * - * A value of 0xfe in the channel_map indicates that the channel is not - * valid for Tx with the current hardware. This means that - * while the system can tune and receive on a given channel, it may not - * be able to associate or transmit any frames on that - * channel. There is no corresponding channel information for that - * entry. - * - *********************************************************************/ - -/* 2.4 GHz */ -const u8 iwl_eeprom_band_1[14] = { - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 -}; - -/* 5.2 GHz bands */ -static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ - 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 -}; - -static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ - 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 -}; - -static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ - 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 -}; - -static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ - 145, 149, 153, 157, 161, 165 -}; - -static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ - 1, 2, 3, 4, 5, 6, 7 -}; - -static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ - 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 -}; - -/****************************************************************************** - * - * generic NVM functions - * -******************************************************************************/ - -/* - * The device's EEPROM semaphore prevents conflicts between driver and uCode - * when accessing the EEPROM; each access is a series of pulses to/from the - * EEPROM chip, not a single event, so even reads could conflict if they - * weren't arbitrated by the semaphore. - */ - -#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ -#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - -static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) -{ - u16 count; - int ret; - - for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { - /* Request semaphore */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - - /* See if we got it */ - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, - EEPROM_SEM_TIMEOUT); - if (ret >= 0) { - IWL_DEBUG_EEPROM(trans, - "Acquired semaphore after %d tries.\n", - count+1); - return ret; - } - } - - return ret; -} - -static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) -{ - iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); - -} - -static int iwl_eeprom_verify_signature(struct iwl_priv *priv) -{ - u32 gp = iwl_read32(priv->trans, CSR_EEPROM_GP) & - CSR_EEPROM_GP_VALID_MSK; - int ret = 0; - - IWL_DEBUG_EEPROM(priv, "EEPROM signature=0x%08x\n", gp); - switch (gp) { - case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: - if (priv->nvm_device_type != NVM_DEVICE_TYPE_OTP) { - IWL_ERR(priv, "EEPROM with bad signature: 0x%08x\n", - gp); - ret = -ENOENT; - } - break; - case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: - case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: - if (priv->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) { - IWL_ERR(priv, "OTP with bad signature: 0x%08x\n", gp); - ret = -ENOENT; - } - break; - case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: - default: - IWL_ERR(priv, "bad EEPROM/OTP signature, type=%s, " - "EEPROM_GP=0x%08x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", gp); - ret = -ENOENT; - break; - } - return ret; -} - -u16 iwl_eeprom_query16(struct iwl_priv *priv, size_t offset) -{ - if (!priv->eeprom) - return 0; - return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); -} - -int iwl_eeprom_check_version(struct iwl_priv *priv) -{ - u16 eeprom_ver; - u16 calib_ver; - - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - calib_ver = iwl_eeprom_calib_version(priv); - - if (eeprom_ver < priv->cfg->eeprom_ver || - calib_ver < priv->cfg->eeprom_calib_ver) - goto err; - - IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", - eeprom_ver, calib_ver); - - return 0; -err: - IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " - "CALIB=0x%x < 0x%x\n", - eeprom_ver, priv->cfg->eeprom_ver, - calib_ver, priv->cfg->eeprom_calib_ver); - return -EINVAL; - -} - -int iwl_eeprom_init_hw_params(struct iwl_priv *priv) -{ - u16 radio_cfg; - - priv->hw_params.sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP); - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE && - !priv->cfg->ht_params) { - IWL_ERR(priv, "Invalid 11n configuration\n"); - return -EINVAL; - } - - if (!priv->hw_params.sku) { - IWL_ERR(priv, "Invalid device sku\n"); - return -EINVAL; - } - - IWL_INFO(priv, "Device SKU: 0x%X\n", priv->hw_params.sku); - - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); - - priv->hw_params.valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); - priv->hw_params.valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); - - /* check overrides (some devices have wrong EEPROM) */ - if (priv->cfg->valid_tx_ant) - priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; - if (priv->cfg->valid_rx_ant) - priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; - - if (!priv->hw_params.valid_tx_ant || !priv->hw_params.valid_rx_ant) { - IWL_ERR(priv, "Invalid chain (0x%X, 0x%X)\n", - priv->hw_params.valid_tx_ant, - priv->hw_params.valid_rx_ant); - return -EINVAL; - } - - priv->hw_params.tx_chains_num = - num_of_ant(priv->hw_params.valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) - priv->hw_params.rx_chains_num = 1; - else - priv->hw_params.rx_chains_num = - num_of_ant(priv->hw_params.valid_rx_ant); - - IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", - priv->hw_params.valid_tx_ant, priv->hw_params.valid_rx_ant); - - return 0; -} - -u16 iwl_eeprom_calib_version(struct iwl_priv *priv) -{ - struct iwl_eeprom_calib_hdr *hdr; - - hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, - EEPROM_CALIB_ALL); - return hdr->version; -} - -static u32 eeprom_indirect_address(struct iwl_priv *priv, u32 address) -{ - u16 offset = 0; - - if ((address & INDIRECT_ADDRESS) == 0) - return address; - - switch (address & INDIRECT_TYPE_MSK) { - case INDIRECT_HOST: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_HOST); - break; - case INDIRECT_GENERAL: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_GENERAL); - break; - case INDIRECT_REGULATORY: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_REGULATORY); - break; - case INDIRECT_TXP_LIMIT: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT); - break; - case INDIRECT_TXP_LIMIT_SIZE: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT_SIZE); - break; - case INDIRECT_CALIBRATION: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_CALIBRATION); - break; - case INDIRECT_PROCESS_ADJST: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_PROCESS_ADJST); - break; - case INDIRECT_OTHERS: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_OTHERS); - break; - default: - IWL_ERR(priv, "illegal indirect type: 0x%X\n", - address & INDIRECT_TYPE_MSK); - break; - } - - /* translate the offset from words to byte */ - return (address & ADDRESS_MSK) + (offset << 1); -} - -const u8 *iwl_eeprom_query_addr(struct iwl_priv *priv, size_t offset) -{ - u32 address = eeprom_indirect_address(priv, offset); - BUG_ON(address >= priv->cfg->base_params->eeprom_size); - return &priv->eeprom[address]; -} - -void iwl_eeprom_get_mac(struct iwl_priv *priv, u8 *mac) -{ - const u8 *addr = iwl_eeprom_query_addr(priv, - EEPROM_MAC_ADDRESS); - memcpy(mac, addr, ETH_ALEN); -} - -/****************************************************************************** - * - * OTP related functions - * -******************************************************************************/ - -static void iwl_set_otp_access(struct iwl_trans *trans, - enum iwl_access_mode mode) -{ - iwl_read32(trans, CSR_OTP_GP_REG); - - if (mode == IWL_OTP_ACCESS_ABSOLUTE) - iwl_clear_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_OTP_ACCESS_MODE); - else - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_OTP_ACCESS_MODE); -} - -static int iwl_get_nvm_type(struct iwl_trans *trans, u32 hw_rev) -{ - u32 otpgp; - int nvm_type; - - /* OTP only valid for CP/PP and after */ - switch (hw_rev & CSR_HW_REV_TYPE_MSK) { - case CSR_HW_REV_TYPE_NONE: - IWL_ERR(trans, "Unknown hardware type\n"); - return -ENOENT; - case CSR_HW_REV_TYPE_5300: - case CSR_HW_REV_TYPE_5350: - case CSR_HW_REV_TYPE_5100: - case CSR_HW_REV_TYPE_5150: - nvm_type = NVM_DEVICE_TYPE_EEPROM; - break; - default: - otpgp = iwl_read32(trans, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) - nvm_type = NVM_DEVICE_TYPE_OTP; - else - nvm_type = NVM_DEVICE_TYPE_EEPROM; - break; - } - return nvm_type; -} - -static int iwl_init_otp_access(struct iwl_trans *trans) -{ - int ret; - - /* Enable 40MHz radio clock */ - iwl_write32(trans, CSR_GP_CNTRL, - iwl_read32(trans, CSR_GP_CNTRL) | - CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - - /* wait for clock to be ready */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - 25000); - if (ret < 0) - IWL_ERR(trans, "Time out access OTP\n"); - else { - iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - udelay(5); - iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - - /* - * CSR auto clock gate disable bit - - * this is only applicable for HW with OTP shadow RAM - */ - if (trans->cfg->base_params->shadow_ram_support) - iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - } - return ret; -} - -static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, - __le16 *eeprom_data) -{ - int ret = 0; - u32 r; - u32 otpgp; - - iwl_write32(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - ret = iwl_poll_bit(trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); - return ret; - } - r = iwl_read32(trans, CSR_EEPROM_REG); - /* check for ECC errors: */ - otpgp = iwl_read32(trans, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { - /* stop in this case */ - /* set the uncorrectable OTP ECC bit for acknowledgement */ - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); - return -EINVAL; - } - if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { - /* continue in this case */ - /* set the correctable OTP ECC bit for acknowledgement */ - iwl_set_bit(trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); - IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); - } - *eeprom_data = cpu_to_le16(r >> 16); - return 0; -} - -/* - * iwl_is_otp_empty: check for empty OTP - */ -static bool iwl_is_otp_empty(struct iwl_trans *trans) -{ - u16 next_link_addr = 0; - __le16 link_value; - bool is_empty = false; - - /* locate the beginning of OTP link list */ - if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { - if (!link_value) { - IWL_ERR(trans, "OTP is empty\n"); - is_empty = true; - } - } else { - IWL_ERR(trans, "Unable to read first block of OTP list.\n"); - is_empty = true; - } - - return is_empty; -} - - -/* - * iwl_find_otp_image: find EEPROM image in OTP - * finding the OTP block that contains the EEPROM image. - * the last valid block on the link list (the block _before_ the last block) - * is the block we should read and used to configure the device. - * If all the available OTP blocks are full, the last block will be the block - * we should read and used to configure the device. - * only perform this operation if shadow RAM is disabled - */ -static int iwl_find_otp_image(struct iwl_trans *trans, - u16 *validblockaddr) -{ - u16 next_link_addr = 0, valid_addr; - __le16 link_value = 0; - int usedblocks = 0; - - /* set addressing mode to absolute to traverse the link list */ - iwl_set_otp_access(trans, IWL_OTP_ACCESS_ABSOLUTE); - - /* checking for empty OTP or error */ - if (iwl_is_otp_empty(trans)) - return -EINVAL; - - /* - * start traverse link list - * until reach the max number of OTP blocks - * different devices have different number of OTP blocks - */ - do { - /* save current valid block address - * check for more block on the link list - */ - valid_addr = next_link_addr; - next_link_addr = le16_to_cpu(link_value) * sizeof(u16); - IWL_DEBUG_EEPROM(trans, "OTP blocks %d addr 0x%x\n", - usedblocks, next_link_addr); - if (iwl_read_otp_word(trans, next_link_addr, &link_value)) - return -EINVAL; - if (!link_value) { - /* - * reach the end of link list, return success and - * set address point to the starting address - * of the image - */ - *validblockaddr = valid_addr; - /* skip first 2 bytes (link list pointer) */ - *validblockaddr += 2; - return 0; - } - /* more in the link list, continue */ - usedblocks++; - } while (usedblocks <= trans->cfg->base_params->max_ll_items); - - /* OTP has no valid blocks */ - IWL_DEBUG_EEPROM(trans, "OTP has no valid blocks\n"); - return -EINVAL; -} - -/****************************************************************************** - * - * Tx Power related functions - * -******************************************************************************/ -/** - * iwl_get_max_txpower_avg - get the highest tx power from all chains. - * find the highest tx power from all chains for the channel - */ -static s8 iwl_get_max_txpower_avg(const struct iwl_cfg *cfg, - struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, - int element, s8 *max_txpower_in_half_dbm) -{ - s8 max_txpower_avg = 0; /* (dBm) */ - - /* Take the highest tx power from any valid chains */ - if ((cfg->valid_tx_ant & ANT_A) && - (enhanced_txpower[element].chain_a_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_a_max; - if ((cfg->valid_tx_ant & ANT_B) && - (enhanced_txpower[element].chain_b_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_b_max; - if ((cfg->valid_tx_ant & ANT_C) && - (enhanced_txpower[element].chain_c_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_c_max; - if (((cfg->valid_tx_ant == ANT_AB) | - (cfg->valid_tx_ant == ANT_BC) | - (cfg->valid_tx_ant == ANT_AC)) && - (enhanced_txpower[element].mimo2_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].mimo2_max; - if ((cfg->valid_tx_ant == ANT_ABC) && - (enhanced_txpower[element].mimo3_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].mimo3_max; - - /* - * max. tx power in EEPROM is in 1/2 dBm format - * convert from 1/2 dBm to dBm (round-up convert) - * but we also do not want to loss 1/2 dBm resolution which - * will impact performance - */ - *max_txpower_in_half_dbm = max_txpower_avg; - return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); -} - -static void -iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv, - struct iwl_eeprom_enhanced_txpwr *txp, - s8 max_txpower_avg) -{ - int ch_idx; - bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ; - enum ieee80211_band band; - - band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - - for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) { - struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx]; - - /* update matching channel or from common data only */ - if (txp->channel != 0 && ch_info->channel != txp->channel) - continue; - - /* update matching band only */ - if (band != ch_info->band) - continue; - - if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) { - ch_info->max_power_avg = max_txpower_avg; - ch_info->curr_txpow = max_txpower_avg; - ch_info->scan_power = max_txpower_avg; - } - - if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg) - ch_info->ht40_max_power_avg = max_txpower_avg; - } -} - -#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) -#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) -#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) - -#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \ - ? # x " " : "") - -static void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) -{ - struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; - int idx, entries; - __le16 *txp_len; - s8 max_txp_avg, max_txp_avg_halfdbm; - - BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); - - /* the length is in 16-bit words, but we want entries */ - txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS); - entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; - - txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS); - - for (idx = 0; idx < entries; idx++) { - txp = &txp_array[idx]; - /* skip invalid entries */ - if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) - continue; - - IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", - (txp->channel && (txp->flags & - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? - "Common " : (txp->channel) ? - "Channel" : "Common", - (txp->channel), - TXP_CHECK_AND_PRINT(VALID), - TXP_CHECK_AND_PRINT(BAND_52G), - TXP_CHECK_AND_PRINT(OFDM), - TXP_CHECK_AND_PRINT(40MHZ), - TXP_CHECK_AND_PRINT(HT_AP), - TXP_CHECK_AND_PRINT(RES1), - TXP_CHECK_AND_PRINT(RES2), - TXP_CHECK_AND_PRINT(COMMON_TYPE), - txp->flags); - IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x " - "chain_B: 0X%02x chain_C: 0X%02x\n", - txp->chain_a_max, txp->chain_b_max, - txp->chain_c_max); - IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x " - "MIMO3: 0x%02x High 20_on_40: 0x%02x " - "Low 20_on_40: 0x%02x\n", - txp->mimo2_max, txp->mimo3_max, - ((txp->delta_20_in_40 & 0xf0) >> 4), - (txp->delta_20_in_40 & 0x0f)); - - max_txp_avg = iwl_get_max_txpower_avg(priv->cfg, txp_array, idx, - &max_txp_avg_halfdbm); - - /* - * Update the user limit values values to the highest - * power supported by any channel - */ - if (max_txp_avg > priv->tx_power_user_lmt) - priv->tx_power_user_lmt = max_txp_avg; - if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm) - priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm; - - iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg); - } -} - -/** - * iwl_eeprom_init - read EEPROM contents - * - * Load the EEPROM contents from adapter into priv->eeprom - * - * NOTE: This routine uses the non-debug IO access functions. - */ -int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) -{ - __le16 *e; - u32 gp = iwl_read32(priv->trans, CSR_EEPROM_GP); - int sz; - int ret; - u16 addr; - u16 validblockaddr = 0; - u16 cache_addr = 0; - - priv->nvm_device_type = iwl_get_nvm_type(priv->trans, hw_rev); - if (priv->nvm_device_type == -ENOENT) - return -ENOENT; - /* allocate eeprom */ - sz = priv->cfg->base_params->eeprom_size; - IWL_DEBUG_EEPROM(priv, "NVM size = %d\n", sz); - priv->eeprom = kzalloc(sz, GFP_KERNEL); - if (!priv->eeprom) { - ret = -ENOMEM; - goto alloc_err; - } - e = (__le16 *)priv->eeprom; - - ret = iwl_eeprom_verify_signature(priv); - if (ret < 0) { - IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); - ret = -ENOENT; - goto err; - } - - /* Make sure driver (instead of uCode) is allowed to read EEPROM */ - ret = iwl_eeprom_acquire_semaphore(priv->trans); - if (ret < 0) { - IWL_ERR(priv, "Failed to acquire EEPROM semaphore.\n"); - ret = -ENOENT; - goto err; - } - - if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { - - ret = iwl_init_otp_access(priv->trans); - if (ret) { - IWL_ERR(priv, "Failed to initialize OTP access.\n"); - ret = -ENOENT; - goto done; - } - iwl_write32(priv->trans, CSR_EEPROM_GP, - iwl_read32(priv->trans, CSR_EEPROM_GP) & - ~CSR_EEPROM_GP_IF_OWNER_MSK); - - iwl_set_bit(priv->trans, CSR_OTP_GP_REG, - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | - CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - /* traversing the linked list if no shadow ram supported */ - if (!priv->cfg->base_params->shadow_ram_support) { - if (iwl_find_otp_image(priv->trans, &validblockaddr)) { - ret = -ENOENT; - goto done; - } - } - for (addr = validblockaddr; addr < validblockaddr + sz; - addr += sizeof(u16)) { - __le16 eeprom_data; - - ret = iwl_read_otp_word(priv->trans, addr, - &eeprom_data); - if (ret) - goto done; - e[cache_addr / 2] = eeprom_data; - cache_addr += sizeof(u16); - } - } else { - /* eeprom is an array of 16bit values */ - for (addr = 0; addr < sz; addr += sizeof(u16)) { - u32 r; - - iwl_write32(priv->trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - - ret = iwl_poll_bit(priv->trans, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(priv, - "Time out reading EEPROM[%d]\n", addr); - goto done; - } - r = iwl_read32(priv->trans, CSR_EEPROM_REG); - e[addr / 2] = cpu_to_le16(r >> 16); - } - } - - IWL_DEBUG_EEPROM(priv, "NVM Type: %s, version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", - iwl_eeprom_query16(priv, EEPROM_VERSION)); - - ret = 0; -done: - iwl_eeprom_release_semaphore(priv->trans); - -err: - if (ret) - iwl_eeprom_free(priv); -alloc_err: - return ret; -} - -void iwl_eeprom_free(struct iwl_priv *priv) -{ - kfree(priv->eeprom); - priv->eeprom = NULL; -} - -static void iwl_init_band_reference(struct iwl_priv *priv, - int eep_band, int *eeprom_ch_count, - const struct iwl_eeprom_channel **eeprom_ch_info, - const u8 **eeprom_ch_index) -{ - u32 offset = priv->lib-> - eeprom_ops.regulatory_bands[eep_band - 1]; - switch (eep_band) { - case 1: /* 2.4GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_1; - break; - case 2: /* 4.9GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_2; - break; - case 3: /* 5.2GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_3; - break; - case 4: /* 5.5GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_4; - break; - case 5: /* 5.7GHz band */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_5; - break; - case 6: /* 2.4GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_6; - break; - case 7: /* 5 GHz ht40 channels */ - *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); - *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); - *eeprom_ch_index = iwl_eeprom_band_7; - break; - default: - BUG(); - return; - } -} - -#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ - ? # x " " : "") -/** - * iwl_mod_ht40_chan_info - Copy ht40 channel info into driver's priv. - * - * Does not set up a command, or touch hardware. - */ -static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, - enum ieee80211_band band, u16 channel, - const struct iwl_eeprom_channel *eeprom_ch, - u8 clear_ht40_extension_channel) -{ - struct iwl_channel_info *ch_info; - - ch_info = (struct iwl_channel_info *) - iwl_get_channel_info(priv, band, channel); - - if (!is_channel_valid(ch_info)) - return -1; - - IWL_DEBUG_EEPROM(priv, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" - " Ad-Hoc %ssupported\n", - ch_info->channel, - is_channel_a_band(ch_info) ? - "5.2" : "2.4", - CHECK_AND_PRINT(IBSS), - CHECK_AND_PRINT(ACTIVE), - CHECK_AND_PRINT(RADAR), - CHECK_AND_PRINT(WIDE), - CHECK_AND_PRINT(DFS), - eeprom_ch->flags, - eeprom_ch->max_power_avg, - ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) - && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? - "" : "not "); - - ch_info->ht40_eeprom = *eeprom_ch; - ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; - ch_info->ht40_flags = eeprom_ch->flags; - if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) - ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; - - return 0; -} - -#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ - ? # x " " : "") - -/** - * iwl_init_channel_map - Set up driver's info for all possible channels - */ -int iwl_init_channel_map(struct iwl_priv *priv) -{ - int eeprom_ch_count = 0; - const u8 *eeprom_ch_index = NULL; - const struct iwl_eeprom_channel *eeprom_ch_info = NULL; - int band, ch; - struct iwl_channel_info *ch_info; - - if (priv->channel_count) { - IWL_DEBUG_EEPROM(priv, "Channel map already initialized.\n"); - return 0; - } - - IWL_DEBUG_EEPROM(priv, "Initializing regulatory info from EEPROM\n"); - - priv->channel_count = - ARRAY_SIZE(iwl_eeprom_band_1) + - ARRAY_SIZE(iwl_eeprom_band_2) + - ARRAY_SIZE(iwl_eeprom_band_3) + - ARRAY_SIZE(iwl_eeprom_band_4) + - ARRAY_SIZE(iwl_eeprom_band_5); - - IWL_DEBUG_EEPROM(priv, "Parsing data for %d channels.\n", - priv->channel_count); - - priv->channel_info = kcalloc(priv->channel_count, - sizeof(struct iwl_channel_info), - GFP_KERNEL); - if (!priv->channel_info) { - IWL_ERR(priv, "Could not allocate channel_info\n"); - priv->channel_count = 0; - return -ENOMEM; - } - - ch_info = priv->channel_info; - - /* Loop through the 5 EEPROM bands adding them in order to the - * channel map we maintain (that contains additional information than - * what just in the EEPROM) */ - for (band = 1; band <= 5; band++) { - - iwl_init_band_reference(priv, band, &eeprom_ch_count, - &eeprom_ch_info, &eeprom_ch_index); - - /* Loop through each band adding each of the channels */ - for (ch = 0; ch < eeprom_ch_count; ch++) { - ch_info->channel = eeprom_ch_index[ch]; - ch_info->band = (band == 1) ? IEEE80211_BAND_2GHZ : - IEEE80211_BAND_5GHZ; - - /* permanently store EEPROM's channel regulatory flags - * and max power in channel info database. */ - ch_info->eeprom = eeprom_ch_info[ch]; - - /* Copy the run-time flags so they are there even on - * invalid channels */ - ch_info->flags = eeprom_ch_info[ch].flags; - /* First write that ht40 is not enabled, and then enable - * one by one */ - ch_info->ht40_extension_channel = - IEEE80211_CHAN_NO_HT40; - - if (!(is_channel_valid(ch_info))) { - IWL_DEBUG_EEPROM(priv, - "Ch. %d Flags %x [%sGHz] - " - "No traffic\n", - ch_info->channel, - ch_info->flags, - is_channel_a_band(ch_info) ? - "5.2" : "2.4"); - ch_info++; - continue; - } - - /* Initialize regulatory-based run-time data */ - ch_info->max_power_avg = ch_info->curr_txpow = - eeprom_ch_info[ch].max_power_avg; - ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; - ch_info->min_power = 0; - - IWL_DEBUG_EEPROM(priv, "Ch. %d [%sGHz] " - "%s%s%s%s%s%s(0x%02x %ddBm):" - " Ad-Hoc %ssupported\n", - ch_info->channel, - is_channel_a_band(ch_info) ? - "5.2" : "2.4", - CHECK_AND_PRINT_I(VALID), - CHECK_AND_PRINT_I(IBSS), - CHECK_AND_PRINT_I(ACTIVE), - CHECK_AND_PRINT_I(RADAR), - CHECK_AND_PRINT_I(WIDE), - CHECK_AND_PRINT_I(DFS), - eeprom_ch_info[ch].flags, - eeprom_ch_info[ch].max_power_avg, - ((eeprom_ch_info[ch]. - flags & EEPROM_CHANNEL_IBSS) - && !(eeprom_ch_info[ch]. - flags & EEPROM_CHANNEL_RADAR)) - ? "" : "not "); - - ch_info++; - } - } - - /* Check if we do have HT40 channels */ - if (priv->lib->eeprom_ops.regulatory_bands[5] == - EEPROM_REGULATORY_BAND_NO_HT40 && - priv->lib->eeprom_ops.regulatory_bands[6] == - EEPROM_REGULATORY_BAND_NO_HT40) - return 0; - - /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ - for (band = 6; band <= 7; band++) { - enum ieee80211_band ieeeband; - - iwl_init_band_reference(priv, band, &eeprom_ch_count, - &eeprom_ch_info, &eeprom_ch_index); - - /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ - ieeeband = - (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; - - /* Loop through each band adding each of the channels */ - for (ch = 0; ch < eeprom_ch_count; ch++) { - /* Set up driver's info for lower half */ - iwl_mod_ht40_chan_info(priv, ieeeband, - eeprom_ch_index[ch], - &eeprom_ch_info[ch], - IEEE80211_CHAN_NO_HT40PLUS); - - /* Set up driver's info for upper half */ - iwl_mod_ht40_chan_info(priv, ieeeband, - eeprom_ch_index[ch] + 4, - &eeprom_ch_info[ch], - IEEE80211_CHAN_NO_HT40MINUS); - } - } - - /* for newer device (6000 series and up) - * EEPROM contain enhanced tx power information - * driver need to process addition information - * to determine the max channel tx power limits - */ - if (priv->lib->eeprom_ops.enhanced_txpower) - iwl_eeprom_enhanced_txpower(priv); - - return 0; -} - -/* - * iwl_free_channel_map - undo allocations in iwl_init_channel_map - */ -void iwl_free_channel_map(struct iwl_priv *priv) -{ - kfree(priv->channel_info); - priv->channel_count = 0; -} - -/** - * iwl_get_channel_info - Find driver's private channel info - * - * Based on band and channel number. - */ -const struct iwl_channel_info *iwl_get_channel_info(const struct iwl_priv *priv, - enum ieee80211_band band, u16 channel) -{ - int i; - - switch (band) { - case IEEE80211_BAND_5GHZ: - for (i = 14; i < priv->channel_count; i++) { - if (priv->channel_info[i].channel == channel) - return &priv->channel_info[i]; - } - break; - case IEEE80211_BAND_2GHZ: - if (channel >= 1 && channel <= 14) - return &priv->channel_info[channel - 1]; - break; - default: - BUG(); - } - - return NULL; -} - -void iwl_rf_config(struct iwl_priv *priv) -{ - u16 radio_cfg; - - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); - - /* write radio config values to register */ - if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { - u32 reg_val = - EEPROM_RF_CFG_TYPE_MSK(radio_cfg) << - CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | - EEPROM_RF_CFG_STEP_MSK(radio_cfg) << - CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | - EEPROM_RF_CFG_DASH_MSK(radio_cfg) << - CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; - - iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | - CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val); - - IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", - EEPROM_RF_CFG_TYPE_MSK(radio_cfg), - EEPROM_RF_CFG_STEP_MSK(radio_cfg), - EEPROM_RF_CFG_DASH_MSK(radio_cfg)); - } else - WARN_ON(1); - - /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); -} diff --git a/drivers/net/wireless/iwlwifi/dvm/eeprom.h b/drivers/net/wireless/iwlwifi/dvm/eeprom.h deleted file mode 100644 index 64bfd947caeb..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/eeprom.h +++ /dev/null @@ -1,269 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -#ifndef __iwl_eeprom_h__ -#define __iwl_eeprom_h__ - -#include - -struct iwl_priv; - -/* - * EEPROM access time values: - * - * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. - * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). - * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. - * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. - */ -#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ - -#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ -#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ - - -/* - * Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags. - * - * IBSS and/or AP operation is allowed *only* on those channels with - * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because - * RADAR detection is not supported by the 4965 driver, but is a - * requirement for establishing a new network for legal operation on channels - * requiring RADAR detection or restricting ACTIVE scanning. - * - * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. - * It only indicates that 20 MHz channel use is supported; HT40 channel - * usage is indicated by a separate set of regulatory flags for each - * HT40 channel pair. - * - * NOTE: Using a channel inappropriately will result in a uCode error! - */ -#define IWL_NUM_TX_CALIB_GROUPS 5 -enum { - EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ - EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ - /* Bit 2 Reserved */ - EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ - EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ - EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ - /* Bit 6 Reserved (was Narrow Channel) */ - EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ -}; - -/* SKU Capabilities */ -#define EEPROM_SKU_CAP_BAND_24GHZ (1 << 4) -#define EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) -#define EEPROM_SKU_CAP_11N_ENABLE (1 << 6) -#define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7) -#define EEPROM_SKU_CAP_IPAN_ENABLE (1 << 8) - -/* *regulatory* channel data format in eeprom, one for each channel. - * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ -struct iwl_eeprom_channel { - u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ - s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ -} __packed; - -enum iwl_eeprom_enhanced_txpwr_flags { - IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), - IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), - IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), - IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), - IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), - IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), - IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), -}; - -/** - * iwl_eeprom_enhanced_txpwr structure - * This structure presents the enhanced regulatory tx power limit layout - * in eeprom image - * Enhanced regulatory tx power portion of eeprom image can be broken down - * into individual structures; each one is 8 bytes in size and contain the - * following information - * @flags: entry flags - * @channel: channel number - * @chain_a_max_pwr: chain a max power in 1/2 dBm - * @chain_b_max_pwr: chain b max power in 1/2 dBm - * @chain_c_max_pwr: chain c max power in 1/2 dBm - * @delta_20_in_40: 20-in-40 deltas (hi/lo) - * @mimo2_max_pwr: mimo2 max power in 1/2 dBm - * @mimo3_max_pwr: mimo3 max power in 1/2 dBm - * - */ -struct iwl_eeprom_enhanced_txpwr { - u8 flags; - u8 channel; - s8 chain_a_max; - s8 chain_b_max; - s8 chain_c_max; - u8 delta_20_in_40; - s8 mimo2_max; - s8 mimo3_max; -} __packed; - -/* calibration */ -struct iwl_eeprom_calib_hdr { - u8 version; - u8 pa_type; - __le16 voltage; -} __packed; - -#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) -#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) - -/* temperature */ -#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) -#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) - - -/* agn links */ -#define EEPROM_LINK_HOST (2*0x64) -#define EEPROM_LINK_GENERAL (2*0x65) -#define EEPROM_LINK_REGULATORY (2*0x66) -#define EEPROM_LINK_CALIBRATION (2*0x67) -#define EEPROM_LINK_PROCESS_ADJST (2*0x68) -#define EEPROM_LINK_OTHERS (2*0x69) -#define EEPROM_LINK_TXP_LIMIT (2*0x6a) -#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) - -/* agn regulatory - indirect access */ -#define EEPROM_REG_BAND_1_CHANNELS ((0x08)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 28 bytes */ -#define EEPROM_REG_BAND_2_CHANNELS ((0x26)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 26 bytes */ -#define EEPROM_REG_BAND_3_CHANNELS ((0x42)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ -#define EEPROM_REG_BAND_4_CHANNELS ((0x5C)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ -#define EEPROM_REG_BAND_5_CHANNELS ((0x74)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 12 bytes */ -#define EEPROM_REG_BAND_24_HT40_CHANNELS ((0x82)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ -#define EEPROM_REG_BAND_52_HT40_CHANNELS ((0x92)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ - -/* 6000 regulatory - indirect access */ -#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS ((0x80)\ - | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ -/* 2.4 GHz */ -extern const u8 iwl_eeprom_band_1[14]; - -#define ADDRESS_MSK 0x0000FFFF -#define INDIRECT_TYPE_MSK 0x000F0000 -#define INDIRECT_HOST 0x00010000 -#define INDIRECT_GENERAL 0x00020000 -#define INDIRECT_REGULATORY 0x00030000 -#define INDIRECT_CALIBRATION 0x00040000 -#define INDIRECT_PROCESS_ADJST 0x00050000 -#define INDIRECT_OTHERS 0x00060000 -#define INDIRECT_TXP_LIMIT 0x00070000 -#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 -#define INDIRECT_ADDRESS 0x00100000 - -/* General */ -#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ -#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ -#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ -#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ -#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ -#define EEPROM_VERSION (2*0x44) /* 2 bytes */ -#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ -#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ -#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ -#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ - -/* The following masks are to be applied on EEPROM_RADIO_CONFIG */ -#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ -#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ -#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ -#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ -#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ -#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ - -#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 - -#define EEPROM_REGULATORY_BAND_NO_HT40 (0) - -struct iwl_eeprom_ops { - const u32 regulatory_bands[7]; - bool enhanced_txpower; -}; - - -int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev); -void iwl_eeprom_free(struct iwl_priv *priv); -int iwl_eeprom_check_version(struct iwl_priv *priv); -int iwl_eeprom_init_hw_params(struct iwl_priv *priv); -u16 iwl_eeprom_calib_version(struct iwl_priv *priv); -const u8 *iwl_eeprom_query_addr(struct iwl_priv *priv, size_t offset); -u16 iwl_eeprom_query16(struct iwl_priv *priv, size_t offset); -void iwl_eeprom_get_mac(struct iwl_priv *priv, u8 *mac); -int iwl_init_channel_map(struct iwl_priv *priv); -void iwl_free_channel_map(struct iwl_priv *priv); -const struct iwl_channel_info *iwl_get_channel_info( - const struct iwl_priv *priv, - enum ieee80211_band band, u16 channel); -void iwl_rf_config(struct iwl_priv *priv); - -#endif /* __iwl_eeprom_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 4ac9af7eb853..cb1ca7a25dd5 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -59,8 +59,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv) /* half dBm need to multiply */ tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); - if (priv->tx_power_lmt_in_half_dbm && - priv->tx_power_lmt_in_half_dbm < tx_power_cmd.global_lmt) { + if (tx_power_cmd.global_lmt > priv->eeprom_data->max_tx_pwr_half_dbm) { /* * For the newer devices which using enhanced/extend tx power * table in EEPROM, the format is in half dBm. driver need to @@ -72,7 +71,8 @@ int iwlagn_send_tx_power(struct iwl_priv *priv) * "tx_power_user_lmt" is higher than EEPROM value (in * half-dBm format), lower the tx power based on EEPROM */ - tx_power_cmd.global_lmt = priv->tx_power_lmt_in_half_dbm; + tx_power_cmd.global_lmt = + priv->eeprom_data->max_tx_pwr_half_dbm; } tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; @@ -824,7 +824,7 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) if (priv->chain_noise_data.active_chains) active_chains = priv->chain_noise_data.active_chains; else - active_chains = priv->hw_params.valid_rx_ant; + active_chains = priv->eeprom_data->valid_rx_ant; if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 32a823d7497f..f60be3993e81 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -48,7 +48,6 @@ #include "iwl-op-mode.h" #include "iwl-modparams.h" -#include "eeprom.h" #include "dev.h" #include "calib.h" #include "agn.h" @@ -238,12 +237,12 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) + if (priv->eeprom_data->bands[IEEE80211_BAND_2GHZ].n_channels) priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &priv->bands[IEEE80211_BAND_2GHZ]; - if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) + &priv->eeprom_data->bands[IEEE80211_BAND_2GHZ]; + if (priv->eeprom_data->bands[IEEE80211_BAND_5GHZ].n_channels) priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &priv->bands[IEEE80211_BAND_5GHZ]; + &priv->eeprom_data->bands[IEEE80211_BAND_5GHZ]; hw->wiphy->hw_version = priv->trans->hw_id; diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 10a2800687bc..ebdc8b93bc04 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -44,13 +44,14 @@ #include +#include "iwl-eeprom-read.h" +#include "iwl-eeprom-parse.h" #include "iwl-io.h" #include "iwl-trans.h" #include "iwl-op-mode.h" #include "iwl-drv.h" #include "iwl-modparams.h" -#include "eeprom.h" #include "dev.h" #include "calib.h" #include "agn.h" @@ -180,7 +181,7 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv) rate = info->control.rates[0].idx; priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - priv->hw_params.valid_tx_ant); + priv->eeprom_data->valid_tx_ant); rate_flags = iwl_ant_idx_to_flags(priv->mgmt_tx_ant); /* In mac80211, rates for 5 GHz start at 0 */ @@ -814,7 +815,7 @@ int iwl_alive_start(struct iwl_priv *priv) ieee80211_wake_queues(priv->hw); /* Configure Tx antenna selection based on H/W config */ - iwlagn_send_tx_ant_config(priv, priv->hw_params.valid_tx_ant); + iwlagn_send_tx_ant_config(priv, priv->eeprom_data->valid_tx_ant); if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { struct iwl_rxon_cmd *active_rxon = @@ -938,8 +939,6 @@ void iwl_down(struct iwl_priv *priv) /* Clear out all status bits but a few that are stable across reset */ priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << STATUS_RF_KILL_HW | - test_bit(STATUS_GEO_CONFIGURED, &priv->status) << - STATUS_GEO_CONFIGURED | test_bit(STATUS_FW_ERROR, &priv->status) << STATUS_FW_ERROR | test_bit(STATUS_EXIT_PENDING, &priv->status) << @@ -1126,227 +1125,14 @@ void iwl_cancel_deferred_work(struct iwl_priv *priv) del_timer_sync(&priv->ucode_trace); } -static void iwl_init_hw_rates(struct ieee80211_rate *rates) -{ - int i; - - for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) { - rates[i].bitrate = iwl_rates[i].ieee * 5; - rates[i].hw_value = i; /* Rate scaling will work on indexes */ - rates[i].hw_value_short = i; - rates[i].flags = 0; - if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) { - /* - * If CCK != 1M then set short preamble rate flag. - */ - rates[i].flags |= - (iwl_rates[i].plcp == IWL_RATE_1M_PLCP) ? - 0 : IEEE80211_RATE_SHORT_PREAMBLE; - } - } -} - -#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ -#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ -static void iwl_init_ht_hw_capab(const struct iwl_priv *priv, - struct ieee80211_sta_ht_cap *ht_info, - enum ieee80211_band band) -{ - u16 max_bit_rate = 0; - u8 rx_chains_num = priv->hw_params.rx_chains_num; - u8 tx_chains_num = priv->hw_params.tx_chains_num; - - ht_info->cap = 0; - ht_info->ht_supported = false; - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - - if (!priv->cfg->ht_params) - return; - - ht_info->ht_supported = true; - - if (priv->cfg->ht_params->ht_greenfield_support) - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (priv->cfg->ht_params->ht40_bands & BIT(band)) { - ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - ht_info->mcs.rx_mask[4] = 0x01; - max_bit_rate = MAX_BIT_RATE_40_MHZ; - } - - if (iwlwifi_mod_params.amsdu_size_8K) - ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; - - ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; - ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; - - ht_info->mcs.rx_mask[0] = 0xFF; - if (rx_chains_num >= 2) - ht_info->mcs.rx_mask[1] = 0xFF; - if (rx_chains_num >= 3) - ht_info->mcs.rx_mask[2] = 0xFF; - - /* Highest supported Rx data rate */ - max_bit_rate *= rx_chains_num; - WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); - ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); - - /* Tx MCS capabilities */ - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - if (tx_chains_num != rx_chains_num) { - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_chains_num - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } -} - -/** - * iwl_init_geos - Initialize mac80211's geo/channel info based from eeprom - */ -static int iwl_init_geos(struct iwl_priv *priv) -{ - struct iwl_channel_info *ch; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channels; - struct ieee80211_channel *geo_ch; - struct ieee80211_rate *rates; - int i = 0; - s8 max_tx_power = IWLAGN_TX_POWER_TARGET_POWER_MIN; - - if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates || - priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) { - IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n"); - set_bit(STATUS_GEO_CONFIGURED, &priv->status); - return 0; - } - - channels = kcalloc(priv->channel_count, - sizeof(struct ieee80211_channel), GFP_KERNEL); - if (!channels) - return -ENOMEM; - - rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate), - GFP_KERNEL); - if (!rates) { - kfree(channels); - return -ENOMEM; - } - - /* 5.2GHz channels start after the 2.4GHz channels */ - sband = &priv->bands[IEEE80211_BAND_5GHZ]; - sband->channels = &channels[ARRAY_SIZE(iwl_eeprom_band_1)]; - /* just OFDM */ - sband->bitrates = &rates[IWL_FIRST_OFDM_RATE]; - sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE; - - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) - iwl_init_ht_hw_capab(priv, &sband->ht_cap, - IEEE80211_BAND_5GHZ); - - sband = &priv->bands[IEEE80211_BAND_2GHZ]; - sband->channels = channels; - /* OFDM & CCK */ - sband->bitrates = rates; - sband->n_bitrates = IWL_RATE_COUNT_LEGACY; - - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) - iwl_init_ht_hw_capab(priv, &sband->ht_cap, - IEEE80211_BAND_2GHZ); - - priv->ieee_channels = channels; - priv->ieee_rates = rates; - - for (i = 0; i < priv->channel_count; i++) { - ch = &priv->channel_info[i]; - - /* FIXME: might be removed if scan is OK */ - if (!is_channel_valid(ch)) - continue; - - sband = &priv->bands[ch->band]; - - geo_ch = &sband->channels[sband->n_channels++]; - - geo_ch->center_freq = - ieee80211_channel_to_frequency(ch->channel, ch->band); - geo_ch->max_power = ch->max_power_avg; - geo_ch->max_antenna_gain = 0xff; - geo_ch->hw_value = ch->channel; - - if (is_channel_valid(ch)) { - if (!(ch->flags & EEPROM_CHANNEL_IBSS)) - geo_ch->flags |= IEEE80211_CHAN_NO_IBSS; - - if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) - geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN; - - if (ch->flags & EEPROM_CHANNEL_RADAR) - geo_ch->flags |= IEEE80211_CHAN_RADAR; - - geo_ch->flags |= ch->ht40_extension_channel; - - if (ch->max_power_avg > max_tx_power) - max_tx_power = ch->max_power_avg; - } else { - geo_ch->flags |= IEEE80211_CHAN_DISABLED; - } - - IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", - ch->channel, geo_ch->center_freq, - is_channel_a_band(ch) ? "5.2" : "2.4", - geo_ch->flags & IEEE80211_CHAN_DISABLED ? - "restricted" : "valid", - geo_ch->flags); - } - - priv->tx_power_device_lmt = max_tx_power; - priv->tx_power_user_lmt = max_tx_power; - priv->tx_power_next = max_tx_power; - - if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && - priv->hw_params.sku & EEPROM_SKU_CAP_BAND_52GHZ) { - IWL_INFO(priv, "Incorrectly detected BG card as ABG. " - "Please send your %s to maintainer.\n", - priv->trans->hw_id_str); - priv->hw_params.sku &= ~EEPROM_SKU_CAP_BAND_52GHZ; - } - - if (iwlwifi_mod_params.disable_5ghz) - priv->bands[IEEE80211_BAND_5GHZ].n_channels = 0; - - IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n", - priv->bands[IEEE80211_BAND_2GHZ].n_channels, - priv->bands[IEEE80211_BAND_5GHZ].n_channels); - - set_bit(STATUS_GEO_CONFIGURED, &priv->status); - - return 0; -} - -/* - * iwl_free_geos - undo allocations in iwl_init_geos - */ -static void iwl_free_geos(struct iwl_priv *priv) -{ - kfree(priv->ieee_channels); - kfree(priv->ieee_rates); - clear_bit(STATUS_GEO_CONFIGURED, &priv->status); -} - static int iwl_init_drv(struct iwl_priv *priv) { - int ret; - spin_lock_init(&priv->sta_lock); mutex_init(&priv->mutex); INIT_LIST_HEAD(&priv->calib_results); - priv->ieee_channels = NULL; - priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; priv->plcp_delta_threshold = @@ -1377,31 +1163,11 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->dynamic_frag_thresh = BT_FRAG_THRESHOLD_DEF; } - ret = iwl_init_channel_map(priv); - if (ret) { - IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); - goto err; - } - - ret = iwl_init_geos(priv); - if (ret) { - IWL_ERR(priv, "initializing geos failed: %d\n", ret); - goto err_free_channel_map; - } - iwl_init_hw_rates(priv->ieee_rates); - return 0; - -err_free_channel_map: - iwl_free_channel_map(priv); -err: - return ret; } static void iwl_uninit_drv(struct iwl_priv *priv) { - iwl_free_geos(priv); - iwl_free_channel_map(priv); kfree(priv->scan_cmd); kfree(priv->beacon_cmd); kfree(rcu_dereference_raw(priv->noa_data)); @@ -1460,6 +1226,42 @@ static void iwl_option_config(struct iwl_priv *priv) #endif } +static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) +{ + u16 radio_cfg; + + priv->hw_params.sku = priv->eeprom_data->sku; + + if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE && + !priv->cfg->ht_params) { + IWL_ERR(priv, "Invalid 11n configuration\n"); + return -EINVAL; + } + + if (!priv->hw_params.sku) { + IWL_ERR(priv, "Invalid device sku\n"); + return -EINVAL; + } + + IWL_INFO(priv, "Device SKU: 0x%X\n", priv->hw_params.sku); + + radio_cfg = priv->eeprom_data->radio_cfg; + + priv->hw_params.tx_chains_num = + num_of_ant(priv->eeprom_data->valid_tx_ant); + if (priv->cfg->rx_with_siso_diversity) + priv->hw_params.rx_chains_num = 1; + else + priv->hw_params.rx_chains_num = + num_of_ant(priv->eeprom_data->valid_rx_ant); + + IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", + priv->eeprom_data->valid_tx_ant, + priv->eeprom_data->valid_rx_ant); + + return 0; +} + static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw) @@ -1605,25 +1407,33 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, goto out_free_hw; /* Read the EEPROM */ - if (iwl_eeprom_init(priv, priv->trans->hw_rev)) { + if (iwl_read_eeprom(priv->trans, &priv->eeprom_blob, + &priv->eeprom_blob_size)) { IWL_ERR(priv, "Unable to init EEPROM\n"); goto out_free_hw; } + /* Reset chip to save power until we load uCode during "up". */ iwl_trans_stop_hw(priv->trans, false); - if (iwl_eeprom_check_version(priv)) + priv->eeprom_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg, + priv->eeprom_blob, + priv->eeprom_blob_size); + if (!priv->eeprom_data) + goto out_free_eeprom_blob; + + if (iwl_eeprom_check_version(priv->eeprom_data, priv->trans)) goto out_free_eeprom; if (iwl_eeprom_init_hw_params(priv)) goto out_free_eeprom; /* extract MAC Address */ - iwl_eeprom_get_mac(priv, priv->addresses[0].addr); + memcpy(priv->addresses[0].addr, priv->eeprom_data->hw_addr, ETH_ALEN); IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); priv->hw->wiphy->addresses = priv->addresses; priv->hw->wiphy->n_addresses = 1; - num_mac = iwl_eeprom_query16(priv, EEPROM_NUM_MAC_ADDRESS); + num_mac = priv->eeprom_data->n_hw_addrs; if (num_mac > 1) { memcpy(priv->addresses[1].addr, priv->addresses[0].addr, ETH_ALEN); @@ -1717,8 +1527,10 @@ out_destroy_workqueue: destroy_workqueue(priv->workqueue); priv->workqueue = NULL; iwl_uninit_drv(priv); +out_free_eeprom_blob: + kfree(priv->eeprom_blob); out_free_eeprom: - iwl_eeprom_free(priv); + iwl_free_eeprom_data(priv->eeprom_data); out_free_hw: ieee80211_free_hw(priv->hw); out: @@ -1743,7 +1555,8 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); - iwl_eeprom_free(priv); + kfree(priv->eeprom_blob); + iwl_free_eeprom_data(priv->eeprom_data); /*netif_stop_queue(dev); */ flush_workqueue(priv->workqueue); diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index 72c940759ac7..518cf3715809 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -36,7 +36,6 @@ #include "iwl-debug.h" #include "iwl-trans.h" #include "iwl-modparams.h" -#include "eeprom.h" #include "dev.h" #include "agn.h" #include "commands.h" diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index af102910e689..6fddd2785e6e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -817,7 +817,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, if (num_of_ant(tbl->ant_type) > 1) tbl->ant_type = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); tbl->is_ht40 = 0; tbl->is_SGI = 0; @@ -1445,7 +1445,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv, u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; int ret = 0; u8 update_search_tbl_counter = 0; @@ -1463,7 +1463,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv, case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: /* avoid antenna B and MIMO */ valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2 && tbl->action != IWL_LEGACY_SWITCH_SISO) tbl->action = IWL_LEGACY_SWITCH_SISO; @@ -1487,7 +1487,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv, else if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) tbl->action = IWL_LEGACY_SWITCH_SISO; valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); } start_action = tbl->action; @@ -1621,7 +1621,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 update_search_tbl_counter = 0; int ret; @@ -1639,7 +1639,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: /* avoid antenna B and MIMO */ valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) tbl->action = IWL_SISO_SWITCH_ANTENNA1; break; @@ -1657,7 +1657,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, /* configure as 1x1 if bt full concurrency */ if (priv->bt_full_concurrent) { valid_tx_ant = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); if (tbl->action >= IWL_LEGACY_SWITCH_ANTENNA2) tbl->action = IWL_SISO_SWITCH_ANTENNA1; } @@ -1793,7 +1793,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; u8 update_search_tbl_counter = 0; int ret; @@ -1963,7 +1963,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 valid_tx_ant = priv->eeprom_data->valid_tx_ant; u8 tx_chains_num = priv->hw_params.tx_chains_num; int ret; u8 update_search_tbl_counter = 0; @@ -2697,7 +2697,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, i = lq_sta->last_txrate_idx; - valid_tx_ant = priv->hw_params.valid_tx_ant; + valid_tx_ant = priv->eeprom_data->valid_tx_ant; if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; @@ -2891,15 +2891,15 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i /* These values will be overridden later */ lq_sta->lq.general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); lq_sta->lq.general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant & - ~first_antenna(priv->hw_params.valid_tx_ant); + priv->eeprom_data->valid_tx_ant & + ~first_antenna(priv->eeprom_data->valid_tx_ant); if (!lq_sta->lq.general_params.dual_stream_ant_msk) { lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { + } else if (num_of_ant(priv->eeprom_data->valid_tx_ant) == 2) { lq_sta->lq.general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant; + priv->eeprom_data->valid_tx_ant; } /* as default allow aggregation for all tids */ @@ -2945,7 +2945,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, if (priv && priv->bt_full_concurrent) { /* 1x1 only */ tbl_type.ant_type = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); } /* How many times should we repeat the initial rate? */ @@ -2977,7 +2977,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, if (priv->bt_full_concurrent) valid_tx_ant = ANT_A; else - valid_tx_ant = priv->hw_params.valid_tx_ant; + valid_tx_ant = priv->eeprom_data->valid_tx_ant; } /* Fill rest of rate table */ @@ -3011,7 +3011,7 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, if (priv && priv->bt_full_concurrent) { /* 1x1 only */ tbl_type.ant_type = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); } /* Indicate to uCode which entries might be MIMO. @@ -3098,7 +3098,7 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, u8 ant_sel_tx; priv = lq_sta->drv; - valid_tx_ant = priv->hw_params.valid_tx_ant; + valid_tx_ant = priv->eeprom_data->valid_tx_ant; if (lq_sta->dbg_fixed_rate) { ant_sel_tx = ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) @@ -3169,9 +3169,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, desc += sprintf(buff+desc, "fixed rate 0x%X\n", lq_sta->dbg_fixed_rate); desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (priv->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", - (priv->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", - (priv->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); + (priv->eeprom_data->valid_tx_ant & ANT_A) ? "ANT_A," : "", + (priv->eeprom_data->valid_tx_ant & ANT_B) ? "ANT_B," : "", + (priv->eeprom_data->valid_tx_ant & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); if (is_Ht(tbl->lq_type)) { diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index d61980248408..0ed90bb8b56a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -33,7 +33,6 @@ #include #include #include "iwl-io.h" -#include "eeprom.h" #include "dev.h" #include "calib.h" #include "agn.h" diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c index 4f55689ac6a7..6ee940f497f9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -420,10 +420,10 @@ static int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) return -EINVAL; } - if (tx_power > priv->tx_power_device_lmt) { + if (tx_power > DIV_ROUND_UP(priv->eeprom_data->max_tx_pwr_half_dbm, 2)) { IWL_WARN(priv, "Requested user TXPOWER %d above upper limit %d.\n", - tx_power, priv->tx_power_device_lmt); + tx_power, priv->eeprom_data->max_tx_pwr_half_dbm); return -EINVAL; } diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index 7ee7f32b0fab..2f271c96ed39 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -30,7 +30,6 @@ #include #include -#include "eeprom.h" #include "dev.h" #include "agn.h" @@ -65,7 +64,6 @@ static int iwl_send_scan_abort(struct iwl_priv *priv) * to receive scan abort command or it does not perform * hardware scan currently */ if (!test_bit(STATUS_READY, &priv->status) || - !test_bit(STATUS_GEO_CONFIGURED, &priv->status) || !test_bit(STATUS_SCAN_HW, &priv->status) || test_bit(STATUS_FW_ERROR, &priv->status)) return -EIO; @@ -648,12 +646,12 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) u16 rx_chain = 0; enum ieee80211_band band; u8 n_probes = 0; - u8 rx_ant = priv->hw_params.valid_rx_ant; + u8 rx_ant = priv->eeprom_data->valid_rx_ant; u8 rate; bool is_active = false; int chan_mod; u8 active_chains; - u8 scan_tx_antennas = priv->hw_params.valid_tx_ant; + u8 scan_tx_antennas = priv->eeprom_data->valid_tx_ant; int ret; int scan_cmd_size = sizeof(struct iwl_scan_cmd) + MAX_SCAN_CHANNEL * sizeof(struct iwl_scan_channel) + @@ -863,7 +861,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) /* MIMO is not used here, but value is required */ rx_chain |= - priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + priv->eeprom_data->valid_rx_ant << RXON_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; @@ -980,7 +978,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) void iwl_init_scan_params(struct iwl_priv *priv) { - u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1; + u8 ant_idx = fls(priv->eeprom_data->valid_tx_ant) - 1; if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index c6b343fdf5f9..286ce4e18068 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -177,17 +177,23 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, if (!ctx->ht.enabled || !ctx->ht.is_40mhz) return false; +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (priv->disable_ht40) + return false; +#endif + /* - * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 - * the bit will not set if it is pure 40MHz case + * Remainder of this function checks ht_cap, but if it's + * NULL then we can do HT40 (special case for RXON) */ - if (ht_cap && !ht_cap->ht_supported) + if (!ht_cap) + return true; + + if (!ht_cap->ht_supported) return false; -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (priv->disable_ht40) + if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) return false; -#endif return true; } @@ -627,23 +633,23 @@ static void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) rate_flags |= RATE_MCS_CCK_MSK; - rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << + rate_flags |= first_antenna(priv->eeprom_data->valid_tx_ant) << RATE_MCS_ANT_POS; rate_n_flags = iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) link_cmd->rs_table[i].rate_n_flags = rate_n_flags; link_cmd->general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); + first_antenna(priv->eeprom_data->valid_tx_ant); link_cmd->general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant & - ~first_antenna(priv->hw_params.valid_tx_ant); + priv->eeprom_data->valid_tx_ant & + ~first_antenna(priv->eeprom_data->valid_tx_ant); if (!link_cmd->general_params.dual_stream_ant_msk) { link_cmd->general_params.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(priv->hw_params.valid_tx_ant) == 2) { + } else if (num_of_ant(priv->eeprom_data->valid_tx_ant) == 2) { link_cmd->general_params.dual_stream_ant_msk = - priv->hw_params.valid_tx_ant; + priv->eeprom_data->valid_tx_ant; } link_cmd->agg_params.agg_dis_start_th = diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c index 59cfe9a8eea5..a7b59590bb53 100644 --- a/drivers/net/wireless/iwlwifi/dvm/testmode.c +++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c @@ -534,9 +534,9 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) break; case IWL_TM_CMD_APP2DEV_GET_EEPROM: - if (priv->eeprom) { + if (priv->eeprom_blob) { skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - priv->cfg->base_params->eeprom_size + 20); + priv->eeprom_blob_size + 20); if (!skb) { IWL_ERR(priv, "Memory allocation fail\n"); return -ENOMEM; @@ -544,15 +544,15 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_EEPROM_RSP) || nla_put(skb, IWL_TM_ATTR_EEPROM, - priv->cfg->base_params->eeprom_size, - priv->eeprom)) + priv->eeprom_blob_size, + priv->eeprom_blob)) goto nla_put_failure; status = cfg80211_testmode_reply(skb); if (status < 0) IWL_ERR(priv, "Error sending msg : %d\n", status); } else - return -EFAULT; + return -ENODATA; break; case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index 55418899bc4e..eb864433e59d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -36,7 +36,6 @@ #include "iwl-modparams.h" #include "iwl-debug.h" #include "agn.h" -#include "eeprom.h" #include "dev.h" #include "commands.h" #include "tt.h" diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 7e3bd6281551..0dfaf649b257 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -186,7 +186,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) - rate_idx = rate_lowest_index(&priv->bands[info->band], + rate_idx = rate_lowest_index( + &priv->eeprom_data->bands[info->band], info->control.sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == IEEE80211_BAND_5GHZ) @@ -206,10 +207,11 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - first_antenna(priv->hw_params.valid_tx_ant)); + first_antenna(priv->eeprom_data->valid_tx_ant)); } else - priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, - priv->hw_params.valid_tx_ant); + priv->mgmt_tx_ant = iwl_toggle_tx_ant( + priv, priv->mgmt_tx_ant, + priv->eeprom_data->valid_tx_ant); rate_flags |= iwl_ant_idx_to_flags(priv->mgmt_tx_ant); /* Set the rate in the TX cmd */ diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 5a2e186c7ded..b3a314ba48c7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -61,8 +61,7 @@ iwl_get_ucode_image(struct iwl_priv *priv, enum iwl_ucode_type ucode_type) static int iwl_set_Xtal_calib(struct iwl_priv *priv) { struct iwl_calib_xtal_freq_cmd cmd; - __le16 *xtal_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_XTAL); + __le16 *xtal_calib = priv->eeprom_data->xtal_calib; iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); @@ -73,12 +72,10 @@ static int iwl_set_Xtal_calib(struct iwl_priv *priv) static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) { struct iwl_calib_temperature_offset_cmd cmd; - __le16 *offset_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - memcpy(&cmd.radio_sensor_offset, offset_calib, sizeof(*offset_calib)); + cmd.radio_sensor_offset = priv->eeprom_data->raw_temperature; if (!(cmd.radio_sensor_offset)) cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; @@ -90,27 +87,17 @@ static int iwl_set_temperature_offset_calib(struct iwl_priv *priv) static int iwl_set_temperature_offset_calib_v2(struct iwl_priv *priv) { struct iwl_calib_temperature_offset_v2_cmd cmd; - __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv, - EEPROM_KELVIN_TEMPERATURE); - __le16 *offset_calib_low = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); - struct iwl_eeprom_calib_hdr *hdr; memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, - EEPROM_CALIB_ALL); - memcpy(&cmd.radio_sensor_offset_high, offset_calib_high, - sizeof(*offset_calib_high)); - memcpy(&cmd.radio_sensor_offset_low, offset_calib_low, - sizeof(*offset_calib_low)); - if (!(cmd.radio_sensor_offset_low)) { + cmd.radio_sensor_offset_high = priv->eeprom_data->kelvin_temperature; + cmd.radio_sensor_offset_low = priv->eeprom_data->raw_temperature; + if (!cmd.radio_sensor_offset_low) { IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; } - memcpy(&cmd.burntVoltageRef, &hdr->voltage, - sizeof(hdr->voltage)); + cmd.burntVoltageRef = priv->eeprom_data->calib_voltage; IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", le16_to_cpu(cmd.radio_sensor_offset_high)); diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index af3a46126f01..e9accfccc050 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -194,6 +194,24 @@ struct iwl_ht_params { u8 ht40_bands; }; +/* + * information on how to parse the EEPROM + */ +#define EEPROM_REG_BAND_1_CHANNELS 0x08 +#define EEPROM_REG_BAND_2_CHANNELS 0x26 +#define EEPROM_REG_BAND_3_CHANNELS 0x42 +#define EEPROM_REG_BAND_4_CHANNELS 0x5C +#define EEPROM_REG_BAND_5_CHANNELS 0x74 +#define EEPROM_REG_BAND_24_HT40_CHANNELS 0x82 +#define EEPROM_REG_BAND_52_HT40_CHANNELS 0x92 +#define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 +#define EEPROM_REGULATORY_BAND_NO_HT40 0 + +struct iwl_eeprom_params { + const u8 regulatory_bands[7]; + bool enhanced_txpower; +}; + /** * struct iwl_cfg * @name: Offical name of the device @@ -246,6 +264,7 @@ struct iwl_cfg { /* params likely to change within a device family */ const struct iwl_ht_params *ht_params; const struct iwl_bt_params *bt_params; + const struct iwl_eeprom_params *eeprom_params; const bool need_temp_offset_calib; /* if used set to true */ const bool no_xtal_calib; enum iwl_led_mode led_mode; diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index f6bf91c8f773..42b20b0e83bc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -45,6 +45,7 @@ void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); /* No matter what is m (priv, bus, trans), this will work */ #define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a) +#define IWL_ERR_DEV(d, f, a...) __iwl_err((d), false, false, f, ## a) #define IWL_WARN(m, f, a...) __iwl_warn((m)->dev, f, ## a) #define IWL_INFO(m, f, a...) __iwl_info((m)->dev, f, ## a) #define IWL_CRIT(m, f, a...) __iwl_crit((m)->dev, f, ## a) @@ -69,6 +70,8 @@ do { \ #define IWL_DEBUG(m, level, fmt, args...) \ __iwl_dbg((m)->dev, level, false, __func__, fmt, ##args) +#define IWL_DEBUG_DEV(dev, level, fmt, args...) \ + __iwl_dbg((dev), level, false, __func__, fmt, ##args) #define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ __iwl_dbg((m)->dev, level, true, __func__, fmt, ##args) @@ -153,7 +156,7 @@ do { \ #define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) #define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) #define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) -#define IWL_DEBUG_EEPROM(p, f, a...) IWL_DEBUG(p, IWL_DL_EEPROM, f, ## a) +#define IWL_DEBUG_EEPROM(d, f, a...) IWL_DEBUG_DEV(d, IWL_DL_EEPROM, f, ## a) #define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) #define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a) #define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a) diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c new file mode 100644 index 000000000000..c87a05cbec12 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -0,0 +1,900 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include +#include +#include "iwl-modparams.h" +#include "iwl-eeprom-parse.h" + +/* EEPROM offset definitions */ + +/* indirect access definitions */ +#define ADDRESS_MSK 0x0000FFFF +#define INDIRECT_TYPE_MSK 0x000F0000 +#define INDIRECT_HOST 0x00010000 +#define INDIRECT_GENERAL 0x00020000 +#define INDIRECT_REGULATORY 0x00030000 +#define INDIRECT_CALIBRATION 0x00040000 +#define INDIRECT_PROCESS_ADJST 0x00050000 +#define INDIRECT_OTHERS 0x00060000 +#define INDIRECT_TXP_LIMIT 0x00070000 +#define INDIRECT_TXP_LIMIT_SIZE 0x00080000 +#define INDIRECT_ADDRESS 0x00100000 + +/* corresponding link offsets in EEPROM */ +#define EEPROM_LINK_HOST (2*0x64) +#define EEPROM_LINK_GENERAL (2*0x65) +#define EEPROM_LINK_REGULATORY (2*0x66) +#define EEPROM_LINK_CALIBRATION (2*0x67) +#define EEPROM_LINK_PROCESS_ADJST (2*0x68) +#define EEPROM_LINK_OTHERS (2*0x69) +#define EEPROM_LINK_TXP_LIMIT (2*0x6a) +#define EEPROM_LINK_TXP_LIMIT_SIZE (2*0x6b) + +/* General */ +#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ +#define EEPROM_SUBSYSTEM_ID (2*0x0A) /* 2 bytes */ +#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ +#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ +#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ +#define EEPROM_VERSION (2*0x44) /* 2 bytes */ +#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ +#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ +#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ +#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ + +/* calibration */ +struct iwl_eeprom_calib_hdr { + u8 version; + u8 pa_type; + __le16 voltage; +} __packed; + +#define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) +#define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) + +/* temperature */ +#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) +#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) + +/* + * EEPROM bands + * These are the channel numbers from each band in the order + * that they are stored in the EEPROM band information. Note + * that EEPROM bands aren't the same as mac80211 bands, and + * there are even special "ht40 bands" in the EEPROM. + */ +static const u8 iwl_eeprom_band_1[14] = { /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +static const u8 iwl_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 iwl_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 iwl_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ + 1, 2, 3, 4, 5, 6, 7 +}; + +static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +#define IWL_NUM_CHANNELS (ARRAY_SIZE(iwl_eeprom_band_1) + \ + ARRAY_SIZE(iwl_eeprom_band_2) + \ + ARRAY_SIZE(iwl_eeprom_band_3) + \ + ARRAY_SIZE(iwl_eeprom_band_4) + \ + ARRAY_SIZE(iwl_eeprom_band_5)) + +/* rate data (static) */ +static struct ieee80211_rate iwl_cfg80211_rates[] = { + { .bitrate = 1 * 10, .hw_value = 0, .hw_value_short = 0, }, + { .bitrate = 2 * 10, .hw_value = 1, .hw_value_short = 1, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 5.5 * 10, .hw_value = 2, .hw_value_short = 2, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 11 * 10, .hw_value = 3, .hw_value_short = 3, + .flags = IEEE80211_RATE_SHORT_PREAMBLE, }, + { .bitrate = 6 * 10, .hw_value = 4, .hw_value_short = 4, }, + { .bitrate = 9 * 10, .hw_value = 5, .hw_value_short = 5, }, + { .bitrate = 12 * 10, .hw_value = 6, .hw_value_short = 6, }, + { .bitrate = 18 * 10, .hw_value = 7, .hw_value_short = 7, }, + { .bitrate = 24 * 10, .hw_value = 8, .hw_value_short = 8, }, + { .bitrate = 36 * 10, .hw_value = 9, .hw_value_short = 9, }, + { .bitrate = 48 * 10, .hw_value = 10, .hw_value_short = 10, }, + { .bitrate = 54 * 10, .hw_value = 11, .hw_value_short = 11, }, +}; +#define RATES_24_OFFS 0 +#define N_RATES_24 ARRAY_SIZE(iwl_cfg80211_rates) +#define RATES_52_OFFS 4 +#define N_RATES_52 (N_RATES_24 - RATES_52_OFFS) + +/* EEPROM reading functions */ + +static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset) +{ + if (WARN_ON(offset + sizeof(u16) > eeprom_size)) + return 0; + return le16_to_cpup((__le16 *)(eeprom + offset)); +} + +static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, + u32 address) +{ + u16 offset = 0; + + if ((address & INDIRECT_ADDRESS) == 0) + return address; + + switch (address & INDIRECT_TYPE_MSK) { + case INDIRECT_HOST: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_HOST); + break; + case INDIRECT_GENERAL: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_GENERAL); + break; + case INDIRECT_REGULATORY: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_REGULATORY); + break; + case INDIRECT_TXP_LIMIT: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_TXP_LIMIT); + break; + case INDIRECT_TXP_LIMIT_SIZE: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_TXP_LIMIT_SIZE); + break; + case INDIRECT_CALIBRATION: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_CALIBRATION); + break; + case INDIRECT_PROCESS_ADJST: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_PROCESS_ADJST); + break; + case INDIRECT_OTHERS: + offset = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_LINK_OTHERS); + break; + default: + WARN_ON(1); + break; + } + + /* translate the offset from words to byte */ + return (address & ADDRESS_MSK) + (offset << 1); +} + +static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, + u32 offset) +{ + u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset); + + if (WARN_ON(address >= eeprom_size)) + return NULL; + + return &eeprom[address]; +} + +static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size, + struct iwl_eeprom_data *data) +{ + struct iwl_eeprom_calib_hdr *hdr; + + hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_CALIB_ALL); + if (!hdr) + return -ENODATA; + data->calib_version = hdr->version; + data->calib_voltage = hdr->voltage; + + return 0; +} + +/** + * enum iwl_eeprom_channel_flags - channel flags in EEPROM + * @EEPROM_CHANNEL_VALID: channel is usable for this SKU/geo + * @EEPROM_CHANNEL_IBSS: usable as an IBSS channel + * @EEPROM_CHANNEL_ACTIVE: active scanning allowed + * @EEPROM_CHANNEL_RADAR: radar detection required + * @EEPROM_CHANNEL_WIDE: 20 MHz channel okay (?) + * @EEPROM_CHANNEL_DFS: dynamic freq selection candidate + */ +enum iwl_eeprom_channel_flags { + EEPROM_CHANNEL_VALID = BIT(0), + EEPROM_CHANNEL_IBSS = BIT(1), + EEPROM_CHANNEL_ACTIVE = BIT(3), + EEPROM_CHANNEL_RADAR = BIT(4), + EEPROM_CHANNEL_WIDE = BIT(5), + EEPROM_CHANNEL_DFS = BIT(7), +}; + +/** + * struct iwl_eeprom_channel - EEPROM channel data + * @flags: %EEPROM_CHANNEL_* flags + * @max_power_avg: max power (in dBm) on this channel, at most 31 dBm + */ +struct iwl_eeprom_channel { + u8 flags; + s8 max_power_avg; +} __packed; + + +enum iwl_eeprom_enhanced_txpwr_flags { + IWL_EEPROM_ENH_TXP_FL_VALID = BIT(0), + IWL_EEPROM_ENH_TXP_FL_BAND_52G = BIT(1), + IWL_EEPROM_ENH_TXP_FL_OFDM = BIT(2), + IWL_EEPROM_ENH_TXP_FL_40MHZ = BIT(3), + IWL_EEPROM_ENH_TXP_FL_HT_AP = BIT(4), + IWL_EEPROM_ENH_TXP_FL_RES1 = BIT(5), + IWL_EEPROM_ENH_TXP_FL_RES2 = BIT(6), + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE = BIT(7), +}; + +/** + * iwl_eeprom_enhanced_txpwr structure + * @flags: entry flags + * @channel: channel number + * @chain_a_max_pwr: chain a max power in 1/2 dBm + * @chain_b_max_pwr: chain b max power in 1/2 dBm + * @chain_c_max_pwr: chain c max power in 1/2 dBm + * @delta_20_in_40: 20-in-40 deltas (hi/lo) + * @mimo2_max_pwr: mimo2 max power in 1/2 dBm + * @mimo3_max_pwr: mimo3 max power in 1/2 dBm + * + * This structure presents the enhanced regulatory tx power limit layout + * in an EEPROM image. + */ +struct iwl_eeprom_enhanced_txpwr { + u8 flags; + u8 channel; + s8 chain_a_max; + s8 chain_b_max; + s8 chain_c_max; + u8 delta_20_in_40; + s8 mimo2_max; + s8 mimo3_max; +} __packed; + +static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_eeprom_data *data, + struct iwl_eeprom_enhanced_txpwr *txp) +{ + s8 result = 0; /* (.5 dBm) */ + + /* Take the highest tx power from any valid chains */ + if (data->valid_tx_ant & ANT_A && txp->chain_a_max > result) + result = txp->chain_a_max; + + if (data->valid_tx_ant & ANT_B && txp->chain_b_max > result) + result = txp->chain_b_max; + + if (data->valid_tx_ant & ANT_C && txp->chain_c_max > result) + result = txp->chain_c_max; + + if ((data->valid_tx_ant == ANT_AB || + data->valid_tx_ant == ANT_BC || + data->valid_tx_ant == ANT_AC) && txp->mimo2_max > result) + result = txp->mimo2_max; + + if (data->valid_tx_ant == ANT_ABC && txp->mimo3_max > result) + result = txp->mimo3_max; + + return result; +} + +#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) +#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) +#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) + +#define TXP_CHECK_AND_PRINT(x) \ + ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) ? # x " " : "") + +static void +iwl_eeprom_enh_txp_read_element(struct iwl_eeprom_data *data, + struct iwl_eeprom_enhanced_txpwr *txp, + int n_channels, s8 max_txpower_avg) +{ + int ch_idx; + enum ieee80211_band band; + + band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + + for (ch_idx = 0; ch_idx < n_channels; ch_idx++) { + struct ieee80211_channel *chan = &data->channels[ch_idx]; + + /* update matching channel or from common data only */ + if (txp->channel != 0 && chan->hw_value != txp->channel) + continue; + + /* update matching band only */ + if (band != chan->band) + continue; + + if (chan->max_power < max_txpower_avg && + !(txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ)) + chan->max_power = max_txpower_avg; + } +} + +static void iwl_eeprom_enhanced_txpower(struct device *dev, + struct iwl_eeprom_data *data, + const u8 *eeprom, size_t eeprom_size, + int n_channels) +{ + struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; + int idx, entries; + __le16 *txp_len; + s8 max_txp_avg_halfdbm; + + BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); + + /* the length is in 16-bit words, but we want entries */ + txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_TXP_SZ_OFFS); + entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; + + txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_TXP_OFFS); + + for (idx = 0; idx < entries; idx++) { + txp = &txp_array[idx]; + /* skip invalid entries */ + if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) + continue; + + IWL_DEBUG_EEPROM(dev, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", + (txp->channel && (txp->flags & + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? + "Common " : (txp->channel) ? + "Channel" : "Common", + (txp->channel), + TXP_CHECK_AND_PRINT(VALID), + TXP_CHECK_AND_PRINT(BAND_52G), + TXP_CHECK_AND_PRINT(OFDM), + TXP_CHECK_AND_PRINT(40MHZ), + TXP_CHECK_AND_PRINT(HT_AP), + TXP_CHECK_AND_PRINT(RES1), + TXP_CHECK_AND_PRINT(RES2), + TXP_CHECK_AND_PRINT(COMMON_TYPE), + txp->flags); + IWL_DEBUG_EEPROM(dev, + "\t\t chain_A: 0x%02x chain_B: 0X%02x chain_C: 0X%02x\n", + txp->chain_a_max, txp->chain_b_max, + txp->chain_c_max); + IWL_DEBUG_EEPROM(dev, + "\t\t MIMO2: 0x%02x MIMO3: 0x%02x High 20_on_40: 0x%02x Low 20_on_40: 0x%02x\n", + txp->mimo2_max, txp->mimo3_max, + ((txp->delta_20_in_40 & 0xf0) >> 4), + (txp->delta_20_in_40 & 0x0f)); + + max_txp_avg_halfdbm = iwl_get_max_txpwr_half_dbm(data, txp); + + iwl_eeprom_enh_txp_read_element(data, txp, n_channels, + DIV_ROUND_UP(max_txp_avg_halfdbm, 2)); + + if (max_txp_avg_halfdbm > data->max_tx_pwr_half_dbm) + data->max_tx_pwr_half_dbm = max_txp_avg_halfdbm; + } +} + +static void iwl_init_band_reference(const struct iwl_cfg *cfg, + const u8 *eeprom, size_t eeprom_size, + int eeprom_band, int *eeprom_ch_count, + const struct iwl_eeprom_channel **ch_info, + const u8 **eeprom_ch_array) +{ + u32 offset = cfg->eeprom_params->regulatory_bands[eeprom_band - 1]; + + offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY; + + *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset); + + switch (eeprom_band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); + *eeprom_ch_array = iwl_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); + *eeprom_ch_array = iwl_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); + *eeprom_ch_array = iwl_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); + *eeprom_ch_array = iwl_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); + *eeprom_ch_array = iwl_eeprom_band_5; + break; + case 6: /* 2.4GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); + *eeprom_ch_array = iwl_eeprom_band_6; + break; + case 7: /* 5 GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); + *eeprom_ch_array = iwl_eeprom_band_7; + break; + default: + *eeprom_ch_count = 0; + *eeprom_ch_array = NULL; + WARN_ON(1); + } +} + +#define CHECK_AND_PRINT(x) \ + ((eeprom_ch->flags & EEPROM_CHANNEL_##x) ? # x " " : "") + +static void iwl_mod_ht40_chan_info(struct device *dev, + struct iwl_eeprom_data *data, int n_channels, + enum ieee80211_band band, u16 channel, + const struct iwl_eeprom_channel *eeprom_ch, + u8 clear_ht40_extension_channel) +{ + struct ieee80211_channel *chan = NULL; + int i; + + for (i = 0; i < n_channels; i++) { + if (data->channels[i].band != band) + continue; + if (data->channels[i].hw_value != channel) + continue; + chan = &data->channels[i]; + break; + } + + if (!chan) + return; + + IWL_DEBUG_EEPROM(dev, + "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + channel, + band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", + CHECK_AND_PRINT(IBSS), + CHECK_AND_PRINT(ACTIVE), + CHECK_AND_PRINT(RADAR), + CHECK_AND_PRINT(WIDE), + CHECK_AND_PRINT(DFS), + eeprom_ch->flags, + eeprom_ch->max_power_avg, + ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) && + !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" + : "not "); + + if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) + chan->flags &= ~clear_ht40_extension_channel; +} + +#define CHECK_AND_PRINT_I(x) \ + ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "") + +static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_eeprom_data *data, + const u8 *eeprom, size_t eeprom_size) +{ + int band, ch_idx; + const struct iwl_eeprom_channel *eeprom_ch_info; + const u8 *eeprom_ch_array; + int eeprom_ch_count; + int n_channels = 0; + + /* + * Loop through the 5 EEPROM bands and add them to the parse list + */ + for (band = 1; band <= 5; band++) { + struct ieee80211_channel *channel; + + iwl_init_band_reference(cfg, eeprom, eeprom_size, band, + &eeprom_ch_count, &eeprom_ch_info, + &eeprom_ch_array); + + /* Loop through each band adding each of the channels */ + for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { + const struct iwl_eeprom_channel *eeprom_ch; + + eeprom_ch = &eeprom_ch_info[ch_idx]; + + if (!(eeprom_ch->flags & EEPROM_CHANNEL_VALID)) { + IWL_DEBUG_EEPROM(dev, + "Ch. %d Flags %x [%sGHz] - No traffic\n", + eeprom_ch_array[ch_idx], + eeprom_ch_info[ch_idx].flags, + (band != 1) ? "5.2" : "2.4"); + continue; + } + + channel = &data->channels[n_channels]; + n_channels++; + + channel->hw_value = eeprom_ch_array[ch_idx]; + channel->band = (band == 1) ? IEEE80211_BAND_2GHZ + : IEEE80211_BAND_5GHZ; + channel->center_freq = + ieee80211_channel_to_frequency( + channel->hw_value, channel->band); + + /* set no-HT40, will enable as appropriate later */ + channel->flags = IEEE80211_CHAN_NO_HT40; + + if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS)) + channel->flags |= IEEE80211_CHAN_NO_IBSS; + + if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE)) + channel->flags |= IEEE80211_CHAN_PASSIVE_SCAN; + + if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR) + channel->flags |= IEEE80211_CHAN_RADAR; + + /* Initialize regulatory-based run-time data */ + channel->max_power = + eeprom_ch_info[ch_idx].max_power_avg; + IWL_DEBUG_EEPROM(dev, + "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n", + channel->hw_value, + (band != 1) ? "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(DFS), + eeprom_ch_info[ch_idx].flags, + eeprom_ch_info[ch_idx].max_power_avg, + ((eeprom_ch_info[ch_idx].flags & + EEPROM_CHANNEL_IBSS) && + !(eeprom_ch_info[ch_idx].flags & + EEPROM_CHANNEL_RADAR)) + ? "" : "not "); + } + } + + if (cfg->eeprom_params->enhanced_txpower) { + /* + * for newer device (6000 series and up) + * EEPROM contain enhanced tx power information + * driver need to process addition information + * to determine the max channel tx power limits + */ + iwl_eeprom_enhanced_txpower(dev, data, eeprom, eeprom_size, + n_channels); + } else { + /* All others use data from channel map */ + int i; + + data->max_tx_pwr_half_dbm = -128; + + for (i = 0; i < n_channels; i++) + data->max_tx_pwr_half_dbm = + max_t(s8, data->max_tx_pwr_half_dbm, + data->channels[i].max_power * 2); + } + + /* Check if we do have HT40 channels */ + if (cfg->eeprom_params->regulatory_bands[5] == + EEPROM_REGULATORY_BAND_NO_HT40 && + cfg->eeprom_params->regulatory_bands[6] == + EEPROM_REGULATORY_BAND_NO_HT40) + return n_channels; + + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ + for (band = 6; band <= 7; band++) { + enum ieee80211_band ieeeband; + + iwl_init_band_reference(cfg, eeprom, eeprom_size, band, + &eeprom_ch_count, &eeprom_ch_info, + &eeprom_ch_array); + + /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ + ieeeband = (band == 6) ? IEEE80211_BAND_2GHZ + : IEEE80211_BAND_5GHZ; + + /* Loop through each band adding each of the channels */ + for (ch_idx = 0; ch_idx < eeprom_ch_count; ch_idx++) { + /* Set up driver's info for lower half */ + iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, + eeprom_ch_array[ch_idx], + &eeprom_ch_info[ch_idx], + IEEE80211_CHAN_NO_HT40PLUS); + + /* Set up driver's info for upper half */ + iwl_mod_ht40_chan_info(dev, data, n_channels, ieeeband, + eeprom_ch_array[ch_idx] + 4, + &eeprom_ch_info[ch_idx], + IEEE80211_CHAN_NO_HT40MINUS); + } + } + + return n_channels; +} + +static int iwl_init_sband_channels(struct iwl_eeprom_data *data, + struct ieee80211_supported_band *sband, + int n_channels, enum ieee80211_band band) +{ + struct ieee80211_channel *chan = &data->channels[0]; + int n = 0, idx = 0; + + while (chan->band != band && idx < n_channels) + chan = &data->channels[++idx]; + + sband->channels = &data->channels[idx]; + + while (chan->band == band && idx < n_channels) { + chan = &data->channels[++idx]; + n++; + } + + sband->n_channels = n; + + return n; +} + +#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ +#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ + +static void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, + struct iwl_eeprom_data *data, + struct ieee80211_sta_ht_cap *ht_info, + enum ieee80211_band band) +{ + int max_bit_rate = 0; + u8 rx_chains; + u8 tx_chains; + + tx_chains = hweight8(data->valid_tx_ant); + if (cfg->rx_with_siso_diversity) + rx_chains = 1; + else + rx_chains = hweight8(data->valid_rx_ant); + + if (!(data->sku & EEPROM_SKU_CAP_11N_ENABLE) || !cfg->ht_params) { + ht_info->ht_supported = false; + return; + } + + ht_info->ht_supported = true; + ht_info->cap = 0; + + if (iwlwifi_mod_params.amsdu_size_8K) + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; + + ht_info->mcs.rx_mask[0] = 0xFF; + if (rx_chains >= 2) + ht_info->mcs.rx_mask[1] = 0xFF; + if (rx_chains >= 3) + ht_info->mcs.rx_mask[2] = 0xFF; + + if (cfg->ht_params->ht_greenfield_support) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + + max_bit_rate = MAX_BIT_RATE_20_MHZ; + + if (cfg->ht_params->ht40_bands & BIT(band)) { + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + ht_info->mcs.rx_mask[4] = 0x01; + max_bit_rate = MAX_BIT_RATE_40_MHZ; + } + + /* Highest supported Rx data rate */ + max_bit_rate *= rx_chains; + WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); + ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); + + /* Tx MCS capabilities */ + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chains != rx_chains) { + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_chains - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } +} + +static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, + struct iwl_eeprom_data *data, + const u8 *eeprom, size_t eeprom_size) +{ + int n_channels = iwl_init_channel_map(dev, cfg, data, + eeprom, eeprom_size); + int n_used = 0; + struct ieee80211_supported_band *sband; + + sband = &data->bands[IEEE80211_BAND_2GHZ]; + sband->band = IEEE80211_BAND_2GHZ; + sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; + sband->n_bitrates = N_RATES_24; + n_used += iwl_init_sband_channels(data, sband, n_channels, + IEEE80211_BAND_2GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_2GHZ); + + sband = &data->bands[IEEE80211_BAND_5GHZ]; + sband->band = IEEE80211_BAND_5GHZ; + sband->bitrates = &iwl_cfg80211_rates[RATES_52_OFFS]; + sband->n_bitrates = N_RATES_52; + n_used += iwl_init_sband_channels(data, sband, n_channels, + IEEE80211_BAND_5GHZ); + iwl_init_ht_hw_capab(cfg, data, &sband->ht_cap, IEEE80211_BAND_5GHZ); + + if (n_channels != n_used) + IWL_ERR_DEV(dev, "EEPROM: used only %d of %d channels\n", + n_used, n_channels); +} + +/* EEPROM data functions */ + +struct iwl_eeprom_data * +iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, + const u8 *eeprom, size_t eeprom_size) +{ + struct iwl_eeprom_data *data; + const void *tmp; + + if (WARN_ON(!cfg || !cfg->eeprom_params)) + return NULL; + + data = kzalloc(sizeof(*data) + + sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, + GFP_KERNEL); + if (!data) + return NULL; + + /* get MAC address(es) */ + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_MAC_ADDRESS); + if (!tmp) + goto err_free; + memcpy(data->hw_addr, tmp, ETH_ALEN); + data->n_hw_addrs = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_NUM_MAC_ADDRESS); + + if (iwl_eeprom_read_calib(eeprom, eeprom_size, data)) + goto err_free; + + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_XTAL); + if (!tmp) + goto err_free; + memcpy(data->xtal_calib, tmp, sizeof(data->xtal_calib)); + + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_RAW_TEMPERATURE); + if (!tmp) + goto err_free; + data->raw_temperature = *(__le16 *)tmp; + + tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, + EEPROM_KELVIN_TEMPERATURE); + if (!tmp) + goto err_free; + data->kelvin_temperature = *(__le16 *)tmp; + data->kelvin_voltage = *((__le16 *)tmp + 1); + + data->radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_RADIO_CONFIG); + data->sku = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_SKU_CAP); + data->eeprom_version = iwl_eeprom_query16(eeprom, eeprom_size, + EEPROM_VERSION); + + data->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(data->radio_cfg); + data->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(data->radio_cfg); + + /* check overrides (some devices have wrong EEPROM) */ + if (cfg->valid_tx_ant) + data->valid_tx_ant = cfg->valid_tx_ant; + if (cfg->valid_rx_ant) + data->valid_rx_ant = cfg->valid_rx_ant; + + if (!data->valid_tx_ant || !data->valid_rx_ant) { + IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n", + data->valid_tx_ant, data->valid_rx_ant); + goto err_free; + } + + iwl_init_sbands(dev, cfg, data, eeprom, eeprom_size); + + return data; + err_free: + kfree(data); + return NULL; +} +EXPORT_SYMBOL_GPL(iwl_parse_eeprom_data); + +/* helper functions */ +int iwl_eeprom_check_version(struct iwl_eeprom_data *data, + struct iwl_trans *trans) +{ + if (data->eeprom_version >= trans->cfg->eeprom_ver || + data->calib_version >= trans->cfg->eeprom_calib_ver) { + IWL_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", + data->eeprom_version, data->calib_version); + return 0; + } + + IWL_ERR(trans, + "Unsupported (too old) EEPROM VER=0x%x < 0x%x CALIB=0x%x < 0x%x\n", + data->eeprom_version, trans->cfg->eeprom_ver, + data->calib_version, trans->cfg->eeprom_calib_ver); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(iwl_eeprom_check_version); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h new file mode 100644 index 000000000000..9c07c670a1ce --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#ifndef __iwl_eeprom_parse_h__ +#define __iwl_eeprom_parse_h__ + +#include +#include +#include "iwl-trans.h" + +/* SKU Capabilities (actual values from EEPROM definition) */ +#define EEPROM_SKU_CAP_BAND_24GHZ (1 << 4) +#define EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) +#define EEPROM_SKU_CAP_11N_ENABLE (1 << 6) +#define EEPROM_SKU_CAP_AMT_ENABLE (1 << 7) +#define EEPROM_SKU_CAP_IPAN_ENABLE (1 << 8) + +/* radio config bits (actual values from EEPROM definition) */ +#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ +#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + +struct iwl_eeprom_data { + int n_hw_addrs; + u8 hw_addr[ETH_ALEN]; + + u16 radio_config; + + u8 calib_version; + __le16 calib_voltage; + + __le16 raw_temperature; + __le16 kelvin_temperature; + __le16 kelvin_voltage; + __le16 xtal_calib[2]; + + u16 sku; + u16 radio_cfg; + u16 eeprom_version; + s8 max_tx_pwr_half_dbm; + + u8 valid_tx_ant, valid_rx_ant; + + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + struct ieee80211_channel channels[]; +}; + +/** + * iwl_parse_eeprom_data - parse EEPROM data and return values + * + * @dev: device pointer we're parsing for, for debug only + * @cfg: device configuration for parsing and overrides + * @eeprom: the EEPROM data + * @eeprom_size: length of the EEPROM data + * + * This function parses all EEPROM values we need and then + * returns a (newly allocated) struct containing all the + * relevant values for driver use. The struct must be freed + * later with iwl_free_eeprom_data(). + */ +struct iwl_eeprom_data * +iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, + const u8 *eeprom, size_t eeprom_size); + +/** + * iwl_free_eeprom_data - free EEPROM data + * @data: the data to free + */ +static inline void iwl_free_eeprom_data(struct iwl_eeprom_data *data) +{ + kfree(data); +} + +int iwl_eeprom_check_version(struct iwl_eeprom_data *data, + struct iwl_trans *trans); + +#endif /* __iwl_eeprom_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c new file mode 100644 index 000000000000..27c7da3c6ed1 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c @@ -0,0 +1,463 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ +#include +#include +#include + +#include "iwl-debug.h" +#include "iwl-eeprom-read.h" +#include "iwl-io.h" +#include "iwl-prph.h" +#include "iwl-csr.h" + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ + +#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ +#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + + +/* + * The device's EEPROM semaphore prevents conflicts between driver and uCode + * when accessing the EEPROM; each access is a series of pulses to/from the + * EEPROM chip, not a single event, so even reads could conflict if they + * weren't arbitrated by the semaphore. + */ + +#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ +#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + +static int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans) +{ + u16 count; + int ret; + + for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + /* Request semaphore */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + + /* See if we got it */ + ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + EEPROM_SEM_TIMEOUT); + if (ret >= 0) { + IWL_DEBUG_EEPROM(trans->dev, + "Acquired semaphore after %d tries.\n", + count+1); + return ret; + } + } + + return ret; +} + +static void iwl_eeprom_release_semaphore(struct iwl_trans *trans) +{ + iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); +} + +static int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp) +{ + u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; + + IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp); + + switch (gp) { + case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: + if (!nvm_is_otp) { + IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n", + gp); + return -ENOENT; + } + return 0; + case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: + case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: + if (nvm_is_otp) { + IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp); + return -ENOENT; + } + return 0; + case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: + default: + IWL_ERR(trans, + "bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n", + nvm_is_otp ? "OTP" : "EEPROM", gp); + return -ENOENT; + } +} + +/****************************************************************************** + * + * OTP related functions + * +******************************************************************************/ + +static void iwl_set_otp_access_absolute(struct iwl_trans *trans) +{ + iwl_read32(trans, CSR_OTP_GP_REG); + + iwl_clear_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); +} + +static int iwl_nvm_is_otp(struct iwl_trans *trans) +{ + u32 otpgp; + + /* OTP only valid for CP/PP and after */ + switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_NONE: + IWL_ERR(trans, "Unknown hardware type\n"); + return -EIO; + case CSR_HW_REV_TYPE_5300: + case CSR_HW_REV_TYPE_5350: + case CSR_HW_REV_TYPE_5100: + case CSR_HW_REV_TYPE_5150: + return 0; + default: + otpgp = iwl_read32(trans, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) + return 1; + return 0; + } +} + +static int iwl_init_otp_access(struct iwl_trans *trans) +{ + int ret; + + /* Enable 40MHz radio clock */ + iwl_write32(trans, CSR_GP_CNTRL, + iwl_read32(trans, CSR_GP_CNTRL) | + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* wait for clock to be ready */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + IWL_ERR(trans, "Time out access OTP\n"); + } else { + iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + udelay(5); + iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + + /* + * CSR auto clock gate disable bit - + * this is only applicable for HW with OTP shadow RAM + */ + if (trans->cfg->base_params->shadow_ram_support) + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + } + return ret; +} + +static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, + __le16 *eeprom_data) +{ + int ret = 0; + u32 r; + u32 otpgp; + + iwl_write32(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + ret = iwl_poll_bit(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(trans, "Time out reading OTP[%d]\n", addr); + return ret; + } + r = iwl_read32(trans, CSR_EEPROM_REG); + /* check for ECC errors: */ + otpgp = iwl_read32(trans, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { + /* stop in this case */ + /* set the uncorrectable OTP ECC bit for acknowledgement */ + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n"); + return -EINVAL; + } + if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { + /* continue in this case */ + /* set the correctable OTP ECC bit for acknowledgement */ + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); + IWL_ERR(trans, "Correctable OTP ECC error, continue read\n"); + } + *eeprom_data = cpu_to_le16(r >> 16); + return 0; +} + +/* + * iwl_is_otp_empty: check for empty OTP + */ +static bool iwl_is_otp_empty(struct iwl_trans *trans) +{ + u16 next_link_addr = 0; + __le16 link_value; + bool is_empty = false; + + /* locate the beginning of OTP link list */ + if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) { + if (!link_value) { + IWL_ERR(trans, "OTP is empty\n"); + is_empty = true; + } + } else { + IWL_ERR(trans, "Unable to read first block of OTP list.\n"); + is_empty = true; + } + + return is_empty; +} + + +/* + * iwl_find_otp_image: find EEPROM image in OTP + * finding the OTP block that contains the EEPROM image. + * the last valid block on the link list (the block _before_ the last block) + * is the block we should read and used to configure the device. + * If all the available OTP blocks are full, the last block will be the block + * we should read and used to configure the device. + * only perform this operation if shadow RAM is disabled + */ +static int iwl_find_otp_image(struct iwl_trans *trans, + u16 *validblockaddr) +{ + u16 next_link_addr = 0, valid_addr; + __le16 link_value = 0; + int usedblocks = 0; + + /* set addressing mode to absolute to traverse the link list */ + iwl_set_otp_access_absolute(trans); + + /* checking for empty OTP or error */ + if (iwl_is_otp_empty(trans)) + return -EINVAL; + + /* + * start traverse link list + * until reach the max number of OTP blocks + * different devices have different number of OTP blocks + */ + do { + /* save current valid block address + * check for more block on the link list + */ + valid_addr = next_link_addr; + next_link_addr = le16_to_cpu(link_value) * sizeof(u16); + IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n", + usedblocks, next_link_addr); + if (iwl_read_otp_word(trans, next_link_addr, &link_value)) + return -EINVAL; + if (!link_value) { + /* + * reach the end of link list, return success and + * set address point to the starting address + * of the image + */ + *validblockaddr = valid_addr; + /* skip first 2 bytes (link list pointer) */ + *validblockaddr += 2; + return 0; + } + /* more in the link list, continue */ + usedblocks++; + } while (usedblocks <= trans->cfg->base_params->max_ll_items); + + /* OTP has no valid blocks */ + IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n"); + return -EINVAL; +} + +/** + * iwl_read_eeprom - read EEPROM contents + * + * Load the EEPROM contents from adapter and return it + * and its size. + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) +{ + __le16 *e; + u32 gp = iwl_read32(trans, CSR_EEPROM_GP); + int sz; + int ret; + u16 addr; + u16 validblockaddr = 0; + u16 cache_addr = 0; + int nvm_is_otp; + + if (!eeprom || !eeprom_size) + return -EINVAL; + + nvm_is_otp = iwl_nvm_is_otp(trans); + if (nvm_is_otp < 0) + return nvm_is_otp; + + sz = trans->cfg->base_params->eeprom_size; + IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz); + + e = kmalloc(sz, GFP_KERNEL); + if (!e) + return -ENOMEM; + + ret = iwl_eeprom_verify_signature(trans, nvm_is_otp); + if (ret < 0) { + IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); + goto err_free; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + ret = iwl_eeprom_acquire_semaphore(trans); + if (ret < 0) { + IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n"); + goto err_free; + } + + if (nvm_is_otp) { + ret = iwl_init_otp_access(trans); + if (ret) { + IWL_ERR(trans, "Failed to initialize OTP access.\n"); + goto err_unlock; + } + + iwl_write32(trans, CSR_EEPROM_GP, + iwl_read32(trans, CSR_EEPROM_GP) & + ~CSR_EEPROM_GP_IF_OWNER_MSK); + + iwl_set_bit(trans, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + /* traversing the linked list if no shadow ram supported */ + if (!trans->cfg->base_params->shadow_ram_support) { + ret = iwl_find_otp_image(trans, &validblockaddr); + if (ret) + goto err_unlock; + } + for (addr = validblockaddr; addr < validblockaddr + sz; + addr += sizeof(u16)) { + __le16 eeprom_data; + + ret = iwl_read_otp_word(trans, addr, &eeprom_data); + if (ret) + goto err_unlock; + e[cache_addr / 2] = eeprom_data; + cache_addr += sizeof(u16); + } + } else { + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + u32 r; + + iwl_write32(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + + ret = iwl_poll_bit(trans, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(trans, + "Time out reading EEPROM[%d]\n", addr); + goto err_unlock; + } + r = iwl_read32(trans, CSR_EEPROM_REG); + e[addr / 2] = cpu_to_le16(r >> 16); + } + } + + IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n", + nvm_is_otp ? "OTP" : "EEPROM"); + + iwl_eeprom_release_semaphore(trans); + + *eeprom_size = sz; + *eeprom = (u8 *)e; + return 0; + + err_unlock: + iwl_eeprom_release_semaphore(trans); + err_free: + kfree(e); + + return ret; +} +EXPORT_SYMBOL_GPL(iwl_read_eeprom); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h new file mode 100644 index 000000000000..1337c9d36fee --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h @@ -0,0 +1,70 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_eeprom_h__ +#define __iwl_eeprom_h__ + +#include "iwl-trans.h" + +int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size); + +#endif /* __iwl_eeprom_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c index 174cf5e38962..0269ea6a5c35 100644 --- a/drivers/net/wireless/iwlwifi/pcie/1000.c +++ b/drivers/net/wireless/iwlwifi/pcie/1000.c @@ -74,6 +74,18 @@ static const struct iwl_ht_params iwl1000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ), }; +static const struct iwl_eeprom_params iwl1000_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + } +}; + #define IWL_DEVICE_1000 \ .fw_name_pre = IWL1000_FW_PRE, \ .ucode_api_max = IWL1000_UCODE_API_MAX, \ @@ -85,6 +97,7 @@ static const struct iwl_ht_params iwl1000_ht_params = { .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ .base_params = &iwl1000_base_params, \ + .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_BLINK const struct iwl_cfg iwl1000_bgn_cfg = { @@ -109,6 +122,7 @@ const struct iwl_cfg iwl1000_bg_cfg = { .eeprom_ver = EEPROM_1000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ .base_params = &iwl1000_base_params, \ + .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true diff --git a/drivers/net/wireless/iwlwifi/pcie/2000.c b/drivers/net/wireless/iwlwifi/pcie/2000.c index 8d4d7295aa46..fd4e78f56fa6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/2000.c +++ b/drivers/net/wireless/iwlwifi/pcie/2000.c @@ -117,6 +117,19 @@ static const struct iwl_bt_params iwl2030_bt_params = { .bt_session_2 = true, }; +static const struct iwl_eeprom_params iwl20x0_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REGULATORY_BAND_NO_HT40, + }, + .enhanced_txpower = true, +}; + #define IWL_DEVICE_2000 \ .fw_name_pre = IWL2000_FW_PRE, \ .ucode_api_max = IWL2000_UCODE_API_MAX, \ @@ -128,6 +141,7 @@ static const struct iwl_bt_params iwl2030_bt_params = { .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE @@ -156,6 +170,7 @@ const struct iwl_cfg iwl2000_2bgn_d_cfg = { .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ .bt_params = &iwl2030_bt_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ @@ -178,6 +193,7 @@ const struct iwl_cfg iwl2030_2bgn_cfg = { .eeprom_ver = EEPROM_2000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2000_base_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ @@ -208,6 +224,7 @@ const struct iwl_cfg iwl105_bgn_d_cfg = { .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .base_params = &iwl2030_base_params, \ .bt_params = &iwl2030_bt_params, \ + .eeprom_params = &iwl20x0_eeprom_params, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ diff --git a/drivers/net/wireless/iwlwifi/pcie/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c index 75d90a9c184a..745a358ab8e8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/5000.c +++ b/drivers/net/wireless/iwlwifi/pcie/5000.c @@ -72,6 +72,18 @@ static const struct iwl_ht_params iwl5000_ht_params = { .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; +static const struct iwl_eeprom_params iwl5000_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, +}; + #define IWL_DEVICE_5000 \ .fw_name_pre = IWL5000_FW_PRE, \ .ucode_api_max = IWL5000_UCODE_API_MAX, \ @@ -83,6 +95,7 @@ static const struct iwl_ht_params iwl5000_ht_params = { .eeprom_ver = EEPROM_5000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ + .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK const struct iwl_cfg iwl5300_agn_cfg = { @@ -129,6 +142,7 @@ const struct iwl_cfg iwl5350_agn_cfg = { .eeprom_ver = EEPROM_5050_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, .base_params = &iwl5000_base_params, + .eeprom_params = &iwl5000_eeprom_params, .ht_params = &iwl5000_ht_params, .led_mode = IWL_LED_BLINK, .internal_wimax_coex = true, @@ -145,6 +159,7 @@ const struct iwl_cfg iwl5350_agn_cfg = { .eeprom_ver = EEPROM_5050_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .base_params = &iwl5000_base_params, \ + .eeprom_params = &iwl5000_eeprom_params, \ .no_xtal_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c index 7b9c02cc67e5..8dd8a6fe61e8 100644 --- a/drivers/net/wireless/iwlwifi/pcie/6000.c +++ b/drivers/net/wireless/iwlwifi/pcie/6000.c @@ -136,6 +136,19 @@ static const struct iwl_bt_params iwl6000_bt_params = { .bt_sco_disable = true, }; +static const struct iwl_eeprom_params iwl6000_eeprom_params = { + .regulatory_bands = { + EEPROM_REG_BAND_1_CHANNELS, + EEPROM_REG_BAND_2_CHANNELS, + EEPROM_REG_BAND_3_CHANNELS, + EEPROM_REG_BAND_4_CHANNELS, + EEPROM_REG_BAND_5_CHANNELS, + EEPROM_6000_REG_BAND_24_HT40_CHANNELS, + EEPROM_REG_BAND_52_HT40_CHANNELS + }, + .enhanced_txpower = true, +}; + #define IWL_DEVICE_6005 \ .fw_name_pre = IWL6005_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ @@ -147,6 +160,7 @@ static const struct iwl_bt_params iwl6000_bt_params = { .eeprom_ver = EEPROM_6005_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE @@ -202,6 +216,7 @@ const struct iwl_cfg iwl6005_2agn_mow2_cfg = { .eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .bt_params = &iwl6000_bt_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true \ @@ -274,6 +289,7 @@ const struct iwl_cfg iwl130_bg_cfg = { .eeprom_ver = EEPROM_6000_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ .base_params = &iwl6000_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK const struct iwl_cfg iwl6000i_2agn_cfg = { @@ -304,6 +320,7 @@ const struct iwl_cfg iwl6000i_2bg_cfg = { .eeprom_ver = EEPROM_6050_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ .base_params = &iwl6050_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true @@ -328,6 +345,7 @@ const struct iwl_cfg iwl6050_2abg_cfg = { .eeprom_ver = EEPROM_6150_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ .base_params = &iwl6050_base_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true @@ -354,6 +372,7 @@ const struct iwl_cfg iwl6000_3agn_cfg = { .eeprom_ver = EEPROM_6000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, .base_params = &iwl6000_base_params, + .eeprom_params = &iwl6000_eeprom_params, .ht_params = &iwl6000_ht_params, .led_mode = IWL_LED_BLINK, }; -- cgit v1.2.3 From 6de4902ee8e07252d335b16810cb73432cf0d514 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 30 May 2012 09:14:41 +0200 Subject: iwlwifi: fix typo 'IWL_WATCHHDOG_DISABLED' Commit 7c5ba4a830cbb730770129b0004e2a06e47dbac5 ("iwlwifi: move queue watchdog into transport") introduced the named constant 'IWL_WATCHHDOG_DISABLED'. Rename it to 'IWL_WATCHDOG_DISABLED'. Signed-off-by: Paul Bolle Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 2 +- drivers/net/wireless/iwlwifi/iwl-config.h | 2 +- drivers/net/wireless/iwlwifi/pcie/1000.c | 2 +- drivers/net/wireless/iwlwifi/pcie/5000.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index ebdc8b93bc04..c91b66c77972 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1347,7 +1347,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.queue_watchdog_timeout = priv->cfg->base_params->wd_timeout; else - trans_cfg.queue_watchdog_timeout = IWL_WATCHHDOG_DISABLED; + trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_dvm_cmd_strings; ucode_flags = fw->ucode_capa.flags; diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index e9accfccc050..10e47938b635 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -113,7 +113,7 @@ enum iwl_led_mode { #define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE 0 /* TX queue watchdog timeouts in mSecs */ -#define IWL_WATCHHDOG_DISABLED 0 +#define IWL_WATCHDOG_DISABLED 0 #define IWL_DEF_WD_TIMEOUT 2000 #define IWL_LONG_WD_TIMEOUT 10000 #define IWL_MAX_WD_TIMEOUT 120000 diff --git a/drivers/net/wireless/iwlwifi/pcie/1000.c b/drivers/net/wireless/iwlwifi/pcie/1000.c index 0269ea6a5c35..81b83f484f08 100644 --- a/drivers/net/wireless/iwlwifi/pcie/1000.c +++ b/drivers/net/wireless/iwlwifi/pcie/1000.c @@ -64,7 +64,7 @@ static const struct iwl_base_params iwl1000_base_params = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, - .wd_timeout = IWL_WATCHHDOG_DISABLED, + .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 128, }; diff --git a/drivers/net/wireless/iwlwifi/pcie/5000.c b/drivers/net/wireless/iwlwifi/pcie/5000.c index 745a358ab8e8..d1665fa6d15a 100644 --- a/drivers/net/wireless/iwlwifi/pcie/5000.c +++ b/drivers/net/wireless/iwlwifi/pcie/5000.c @@ -62,7 +62,7 @@ static const struct iwl_base_params iwl5000_base_params = { .led_compensation = 51, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, - .wd_timeout = IWL_WATCHHDOG_DISABLED, + .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 512, .no_idle_support = true, }; -- cgit v1.2.3 From 12b78246aa49b72f665601fdc7a44eb75fcf593b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 May 2012 12:31:23 +0200 Subject: iwlwifi: configure PHY version for 1000 series We should also configure the PHY version in the CSR_HW_IF_CONFIG_REG register for 1000 series devices, not just for the other devices. Reviewed-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/devices.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 747e576950f4..d12f0631b67a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -96,10 +96,7 @@ static void iwl1000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 1000 series */ static void iwl1000_nic_config(struct iwl_priv *priv) { - /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); + iwl_rf_config(priv); /* Setting digital SVR for 1000 card to 1.32V */ /* locking is acquired in iwl_set_bits_mask_prph() function */ -- cgit v1.2.3 From 1280d428ad0f134d1e370a0a0b70793ebcf56742 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 30 May 2012 12:36:23 +0200 Subject: iwlwifi: move RF config into NIC config Since the RF config is done for all devices, there's no need to keep a separate function that is called for all devices, move it into the general NIC config function. Reviewed-by: Emmanuel Grumbach Reviewed-by: Wey-Yi W Guy Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/devices.c | 43 ------------------------------ drivers/net/wireless/iwlwifi/dvm/main.c | 31 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index d12f0631b67a..0521a6be09d2 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -36,41 +36,6 @@ #include "commands.h" -#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 - -static void iwl_rf_config(struct iwl_priv *priv) -{ - u16 radio_cfg = priv->eeprom_data->radio_cfg; - - /* write radio config values to register */ - if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { - u32 reg_val = - EEPROM_RF_CFG_TYPE_MSK(radio_cfg) << - CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | - EEPROM_RF_CFG_STEP_MSK(radio_cfg) << - CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | - EEPROM_RF_CFG_DASH_MSK(radio_cfg) << - CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; - - iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | - CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | - CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val); - - IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", - EEPROM_RF_CFG_TYPE_MSK(radio_cfg), - EEPROM_RF_CFG_STEP_MSK(radio_cfg), - EEPROM_RF_CFG_DASH_MSK(radio_cfg)); - } else { - WARN_ON(1); - } - - /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | - CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); -} - /* * 1000 series * =========== @@ -96,8 +61,6 @@ static void iwl1000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 1000 series */ static void iwl1000_nic_config(struct iwl_priv *priv) { - iwl_rf_config(priv); - /* Setting digital SVR for 1000 card to 1.32V */ /* locking is acquired in iwl_set_bits_mask_prph() function */ iwl_set_bits_mask_prph(priv->trans, APMG_DIGITAL_SVR_REG, @@ -233,8 +196,6 @@ static void iwl2000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 2000 series */ static void iwl2000_nic_config(struct iwl_priv *priv) { - iwl_rf_config(priv); - iwl_set_bit(priv->trans, CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); } @@ -291,8 +252,6 @@ struct iwl_lib_ops iwl2030_lib = { /* NIC configuration for 5000 series */ static void iwl5000_nic_config(struct iwl_priv *priv) { - iwl_rf_config(priv); - /* W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), * causing ME FW to lose ownership and not being able to obtain it back. @@ -502,8 +461,6 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 6000 series */ static void iwl6000_nic_config(struct iwl_priv *priv) { - iwl_rf_config(priv); - switch (priv->cfg->device_family) { case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6030: diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index c91b66c77972..ec0b77b862a8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -2027,9 +2027,12 @@ static void iwl_cmd_queue_full(struct iwl_op_mode *op_mode) } } +#define EEPROM_RF_CONFIG_TYPE_MAX 0x3 + static void iwl_nic_config(struct iwl_op_mode *op_mode) { struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + u16 radio_cfg = priv->eeprom_data->radio_cfg; /* SKU Control */ iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, @@ -2040,6 +2043,34 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode) (CSR_HW_REV_DASH(priv->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_DASH)); + /* write radio config values to register */ + if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { + u32 reg_val = + EEPROM_RF_CFG_TYPE_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE | + EEPROM_RF_CFG_STEP_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_STEP | + EEPROM_RF_CFG_DASH_MSK(radio_cfg) << + CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; + + iwl_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | + CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | + CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH, reg_val); + + IWL_INFO(priv, "Radio type=0x%x-0x%x-0x%x\n", + EEPROM_RF_CFG_TYPE_MSK(radio_cfg), + EEPROM_RF_CFG_STEP_MSK(radio_cfg), + EEPROM_RF_CFG_DASH_MSK(radio_cfg)); + } else { + WARN_ON(1); + } + + /* set CSR_HW_CONFIG_REG for uCode use */ + iwl_set_bit(priv->trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | + CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); + priv->lib->nic_config(priv); } -- cgit v1.2.3 From b1abedada3fd0aa100723aa9b60b7e31c17945cb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 31 May 2012 13:54:56 +0200 Subject: iwlwifi: remove extern opmode ops declarations There's no need to declare the opmode ops as extern since they're now dynamically registered. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 4 +++- drivers/net/wireless/iwlwifi/iwl-op-mode.h | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index ec0b77b862a8..1c2d0233a405 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -81,6 +81,8 @@ MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); +static const struct iwl_op_mode_ops iwl_dvm_ops; + void iwl_update_chain_flags(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; @@ -2163,7 +2165,7 @@ static void iwl_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); } -const struct iwl_op_mode_ops iwl_dvm_ops = { +static const struct iwl_op_mode_ops iwl_dvm_ops = { .start = iwl_op_mode_dvm_start, .stop = iwl_op_mode_dvm_stop, .rx = iwl_rx_dispatch, diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index cec133c87ad8..cd9ef114d3a3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -221,9 +221,4 @@ static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode) op_mode->ops->wimax_active(op_mode); } -/***************************************************** -* Op mode layers implementations -******************************************************/ -extern const struct iwl_op_mode_ops iwl_dvm_ops; - #endif /* __iwl_op_mode_h__ */ -- cgit v1.2.3 From 4db703ead4535792ea54dba7275fdd1527848e74 Mon Sep 17 00:00:00 2001 From: Austin Hendrix Date: Mon, 4 Jun 2012 15:27:51 -0700 Subject: HID: multitouch: add support for Novatek touchscreen Add support for a Novatek touchscreen panel as a generic HID multitouch panel. Signed-off-by: Austin Hendrix Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-multitouch.c | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9373f535dfe9..734a2b986502 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -564,6 +564,9 @@ #define USB_VENDOR_ID_NINTENDO 0x057e #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 +#define USB_VENDOR_ID_NOVATEK 0x0603 +#define USB_DEVICE_ID_NOVATEK_PCT 0x0600 + #define USB_VENDOR_ID_NTRIG 0x1b96 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1 0x0003 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 6e3332a99976..fae08dfd18bf 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -951,6 +951,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, USB_DEVICE_ID_PANABOARD_UBT880) }, + /* Novatek Panel */ + { .driver_data = MT_CLS_DEFAULT, + HID_USB_DEVICE(USB_VENDOR_ID_NOVATEK, + USB_DEVICE_ID_NOVATEK_PCT) }, + /* PenMount panels */ { .driver_data = MT_CLS_CONFIDENCE, MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, -- cgit v1.2.3 From 4380d8198845da88915c93a4b3f9cb2fa0f917be Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 6 Jun 2012 16:28:33 +0200 Subject: HID: multitouch: fix entry for Novatek Touchscreen Since Henrik's autoloading changes, the proper macro for device entry is MT_USB_DEVICE(). Reported-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index fae08dfd18bf..61cc4cbe0f3e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -953,7 +953,7 @@ static const struct hid_device_id mt_devices[] = { /* Novatek Panel */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_NOVATEK, + MT_USB_DEVICE(USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_PCT) }, /* PenMount panels */ -- cgit v1.2.3 From 1d1e9a467c7a478d24a1af39e467a414a8363661 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:17 +0000 Subject: be2net: don't call vid_config() when there's no vlan config be_vid_config() is called from be_setup() to replay config cmds after a card reset. Skip calling it when no vlans are configured. Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 08efd308d78a..f1b092692aa5 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2762,7 +2762,8 @@ static int be_setup(struct be_adapter *adapter) be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL); - be_vid_config(adapter, false, 0); + if (adapter->vlans_added) + be_vid_config(adapter, false, 0); be_set_rx_mode(adapter->netdev); -- cgit v1.2.3 From 10329df8f9b90e27ef63d89f9d58fed8d466f943 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:18 +0000 Subject: be2net: cleanup be_vid_config() - get rid of 2 unused arguments to the routine and some unused code - don't use the term "vlan_tag" in place of "vid" as they are different Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index f1b092692aa5..cfbec8b495cb 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -785,19 +785,12 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu) * A max of 64 (BE_NUM_VLANS_SUPPORTED) vlans can be configured in BE. * If the user configures more, place BE in vlan promiscuous mode. */ -static int be_vid_config(struct be_adapter *adapter, bool vf, u32 vf_num) +static int be_vid_config(struct be_adapter *adapter) { - struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf_num]; - u16 vtag[BE_NUM_VLANS_SUPPORTED]; - u16 ntags = 0, i; + u16 vids[BE_NUM_VLANS_SUPPORTED]; + u16 num = 0, i; int status = 0; - if (vf) { - vtag[0] = cpu_to_le16(vf_cfg->vlan_tag); - status = be_cmd_vlan_config(adapter, vf_cfg->if_handle, vtag, - 1, 1, 0); - } - /* No need to further configure vids if in promiscuous mode */ if (adapter->promiscuous) return 0; @@ -808,10 +801,10 @@ static int be_vid_config(struct be_adapter *adapter, bool vf, u32 vf_num) /* Construct VLAN Table to give to HW */ for (i = 0; i < VLAN_N_VID; i++) if (adapter->vlan_tag[i]) - vtag[ntags++] = cpu_to_le16(i); + vids[num++] = cpu_to_le16(i); status = be_cmd_vlan_config(adapter, adapter->if_handle, - vtag, ntags, 1, 0); + vids, num, 1, 0); /* Set to VLAN promisc mode as setting VLAN filter failed */ if (status) { @@ -840,7 +833,7 @@ static int be_vlan_add_vid(struct net_device *netdev, u16 vid) adapter->vlan_tag[vid] = 1; if (adapter->vlans_added <= (adapter->max_vlans + 1)) - status = be_vid_config(adapter, false, 0); + status = be_vid_config(adapter); if (!status) adapter->vlans_added++; @@ -862,7 +855,7 @@ static int be_vlan_rem_vid(struct net_device *netdev, u16 vid) adapter->vlan_tag[vid] = 0; if (adapter->vlans_added <= adapter->max_vlans) - status = be_vid_config(adapter, false, 0); + status = be_vid_config(adapter); if (!status) adapter->vlans_added--; @@ -889,7 +882,7 @@ static void be_set_rx_mode(struct net_device *netdev) be_cmd_rx_filter(adapter, IFF_PROMISC, OFF); if (adapter->vlans_added) - be_vid_config(adapter, false, 0); + be_vid_config(adapter); } /* Enable multicast promisc if num configured exceeds what we support */ @@ -2763,7 +2756,7 @@ static int be_setup(struct be_adapter *adapter) be_cmd_get_fw_ver(adapter, adapter->fw_ver, NULL); if (adapter->vlans_added) - be_vid_config(adapter, false, 0); + be_vid_config(adapter); be_set_rx_mode(adapter->netdev); -- cgit v1.2.3 From 0513ac3d2212072c6694430b1003ad4246a35466 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:19 +0000 Subject: be2net: do not modify PCI MaxReadReq size Setting the PCI MRRS to a value of 4096 (overriding the system decided value) had provided perf improvement in TX. But, IBM has provided feedback that on POWER platforms, this value is set by the system firmware, and drivers modifying this value can cause unpredictable results (like EEH errors.) So, backing off this change. On POWER7 platforms most slots, it seems, do get a MRRS of 4096. This patch reverts the following commit: "be2net: Modified PCI MaxReadReq size to 4096 bytes" commit 5a56eb10babbcd7b3796dc3c28c271260aa3608d. Suggested-by: Brian King Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index cfbec8b495cb..695c3cf76e8d 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2766,8 +2766,6 @@ static int be_setup(struct be_adapter *adapter) be_cmd_set_flow_control(adapter, adapter->tx_fc, adapter->rx_fc); - pcie_set_readrq(adapter->pdev, 4096); - if (be_physfn(adapter) && num_vfs) { if (adapter->dev_num_vfs) be_vf_setup(adapter); -- cgit v1.2.3 From 7f6400626631454f5b535eb92c09a35390777a52 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:20 +0000 Subject: be2net: fix reporting number of actual rx queues Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 695c3cf76e8d..3aa478f50358 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1890,6 +1890,12 @@ static int be_rx_cqs_create(struct be_adapter *adapter) */ adapter->num_rx_qs = (num_irqs(adapter) > 1) ? num_irqs(adapter) + 1 : 1; + if (adapter->num_rx_qs != MAX_RX_QS) { + rtnl_lock(); + netif_set_real_num_rx_queues(adapter->netdev, + adapter->num_rx_qs); + rtnl_unlock(); + } adapter->big_page_size = (1 << get_order(rx_frag_size)) * PAGE_SIZE; for_all_rx_queues(adapter, rxo, i) { @@ -3740,7 +3746,7 @@ static int __devinit be_probe(struct pci_dev *pdev, goto disable_dev; pci_set_master(pdev); - netdev = alloc_etherdev_mq(sizeof(struct be_adapter), MAX_TX_QS); + netdev = alloc_etherdev_mqs(sizeof(*adapter), MAX_TX_QS, MAX_RX_QS); if (netdev == NULL) { status = -ENOMEM; goto rel_reg; -- cgit v1.2.3 From 421737bdd51912d136ca3e554f54731039d95847 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:21 +0000 Subject: be2net: remove unnecessary usage of unlikely() Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 3aa478f50358..42ee75b794b6 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -719,8 +719,8 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, * 60 bytes long. * As a workaround disable TX vlan offloading in such cases. */ - if (unlikely(vlan_tx_tag_present(skb) && - (skb->ip_summed != CHECKSUM_PARTIAL || skb->len <= 60))) { + if (vlan_tx_tag_present(skb) && + (skb->ip_summed != CHECKSUM_PARTIAL || skb->len <= 60)) { skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) goto tx_drop; -- cgit v1.2.3 From d79c0a207aa3be002e1d9473ea98cc4f838e5a8b Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:22 +0000 Subject: be2net: do not use SCRATCHPAD register The CUST_SCRATCHPAD_CSR register is used for marking if FW cleanup is needed. This is used in a crash kernel scenario. Do no use this register as it is not available for some functions. Instead, always issue an FLR when a function is probed *except* when VFs are preset (enabled in the previous PF load). Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_hw.h | 2 -- drivers/net/ethernet/emulex/benet/be_main.c | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index d9fb0c501fa1..7c8a710eac2f 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -58,8 +58,6 @@ #define SLI_PORT_CONTROL_IP_MASK 0x08000000 -#define PCICFG_CUST_SCRATCHPAD_CSR 0x1EC - /********* Memory BAR register ************/ #define PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET 0xfc /* Host Interrupt Enable, if set interrupts are enabled although "PCI Interrupt diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 42ee75b794b6..f29827f657eb 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1049,6 +1049,8 @@ static int be_find_vfs(struct be_adapter *adapter, int vf_state) u16 offset, stride; pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return 0; pci_read_config_word(pdev, pos + PCI_SRIOV_VF_OFFSET, &offset); pci_read_config_word(pdev, pos + PCI_SRIOV_VF_STRIDE, &stride); @@ -2542,7 +2544,6 @@ static int be_clear(struct be_adapter *adapter) be_cmd_fw_clean(adapter); be_msix_disable(adapter); - pci_write_config_dword(adapter->pdev, PCICFG_CUST_SCRATCHPAD_CSR, 0); return 0; } @@ -2785,8 +2786,6 @@ static int be_setup(struct be_adapter *adapter) schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000)); adapter->flags |= BE_FLAGS_WORKER_SCHEDULED; - - pci_write_config_dword(adapter->pdev, PCICFG_CUST_SCRATCHPAD_CSR, 1); return 0; err: be_clear(adapter); @@ -3724,10 +3723,7 @@ reschedule: static bool be_reset_required(struct be_adapter *adapter) { - u32 reg; - - pci_read_config_dword(adapter->pdev, PCICFG_CUST_SCRATCHPAD_CSR, ®); - return reg; + return be_find_vfs(adapter, ENABLED) > 0 ? false : true; } static int __devinit be_probe(struct pci_dev *pdev, -- cgit v1.2.3 From 20d5ec435c5fba8142eb7df692a5f54fbb26e892 Mon Sep 17 00:00:00 2001 From: Sathya Perla Date: Tue, 5 Jun 2012 19:37:23 +0000 Subject: be2net: update driver version Signed-off-by: Sathya Perla Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index c5c4c0e83bd1..7b5cc2b212e5 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -34,7 +34,7 @@ #include "be_hw.h" #include "be_roce.h" -#define DRV_VER "4.2.220u" +#define DRV_VER "4.2.248.0u" #define DRV_NAME "be2net" #define BE_NAME "ServerEngines BladeEngine2 10Gbps NIC" #define BE3_NAME "ServerEngines BladeEngine3 10Gbps NIC" -- cgit v1.2.3 From 2c0133a437905591cdaa39cf65a3f7188d20a094 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 18 May 2012 07:46:36 +0300 Subject: wlcore/wl12xx/wl18xx: introduce quirk to remove TKIP header space 18xx chips do not require extra space in the TKIP header. Introduce a new HW quirk to allow us to make this feature arch-specific. 12xx chip will now have this quirk. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 9 ++++++--- drivers/net/wireless/ti/wl18xx/tx.c | 3 ++- drivers/net/wireless/ti/wlcore/cmd.c | 5 +++-- drivers/net/wireless/ti/wlcore/main.c | 8 +++++--- drivers/net/wireless/ti/wlcore/tx.c | 9 ++++++--- drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 6 files changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 77e0f318aa67..d33117efec79 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -625,7 +625,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", wl->chip.id); - wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; + wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | + WLCORE_QUIRK_TKIP_HEADER_SPACE; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, @@ -640,7 +641,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl->chip.id); - wl->quirks |= WLCORE_QUIRK_LEGACY_NVS; + wl->quirks |= WLCORE_QUIRK_LEGACY_NVS | + WLCORE_QUIRK_TKIP_HEADER_SPACE; wl->plt_fw_name = WL127X_PLT_FW_NAME; wl->sr_fw_name = WL127X_FW_NAME_SINGLE; wl->mr_fw_name = WL127X_FW_NAME_MULTI; @@ -660,7 +662,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl) wl->mr_fw_name = WL128X_FW_NAME_MULTI; /* wl128x requires TX blocksize alignment */ - wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; + wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | + WLCORE_QUIRK_TKIP_HEADER_SPACE; break; case CHIP_ID_1283_PG10: diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c index 4de00b901505..5b1fb10d9fd7 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.c +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -75,7 +75,8 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); /* remove TKIP header space if present */ - if (info->control.hw_key && + if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index df8d672b1f06..885364ca4344 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1046,7 +1046,7 @@ out: int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - int ret, extra; + int ret, extra = 0; u16 fc; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; @@ -1085,7 +1085,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) /* encryption space */ switch (wlvif->encryption_type) { case KEY_TKIP: - extra = WL1271_EXTRA_SPACE_TKIP; + if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) + extra = WL1271_EXTRA_SPACE_TKIP; break; case KEY_AES: extra = WL1271_EXTRA_SPACE_AES; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 414fde41072c..5ac062831666 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4975,9 +4975,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) WL1271_CIPHER_SUITE_GEM, }; - /* The tx descriptor buffer and the TKIP space. */ - wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP + - sizeof(struct wl1271_tx_hw_descr); + /* The tx descriptor buffer */ + wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr); + + if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) + wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP; /* unit us */ /* FIXME: find a proper value */ diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index e8a2998408b6..6b68e29a1e92 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -358,7 +358,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, /* TODO: handle dummy packets on multi-vifs */ is_dummy = wl12xx_is_dummy_packet(wl, skb); - if (info->control.hw_key && + if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) extra = WL1271_EXTRA_SPACE_TKIP; @@ -852,7 +853,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); /* remove TKIP header space if present */ - if (info->control.hw_key && + if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, @@ -1001,7 +1003,8 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) */ info = IEEE80211_SKB_CB(skb); skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); - if (info->control.hw_key && + if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) && + info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 37a80f4bf5bb..16c28bbd1b20 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -411,6 +411,9 @@ int wlcore_free_hw(struct wl1271 *wl); /* Some firmwares may not support ELP */ #define WLCORE_QUIRK_NO_ELP BIT(6) +/* extra header space is required for TKIP */ +#define WLCORE_QUIRK_TKIP_HEADER_SPACE BIT(8) + /* TODO: move to the lower drivers when all usages are abstracted */ #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) -- cgit v1.2.3 From 32bb2c03f990d015c0fec67e9134ea8625aaf784 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 18 May 2012 07:46:37 +0300 Subject: wlcore/wl12xx/wl18xx: handle spare blocks spacial cases per arch Add a HW op for getting spare blocks. 12xx cards require 2 spare blocks for GEM encrypted SKBs, regardless of VIFs or keys programmed into the FW. 18xx cards require 2 spare blocks when there are any connected TKIP or GEM VIFs. For now always return 2 spare blocks, as this works with all networks. The special case TKIP/GEM functionality is added at a later patch. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 11 +++++++++-- drivers/net/wireless/ti/wl18xx/main.c | 11 ++++++++--- drivers/net/wireless/ti/wl18xx/tx.h | 3 ++- drivers/net/wireless/ti/wlcore/debugfs.c | 1 - drivers/net/wireless/ti/wlcore/hw_ops.h | 9 +++++++++ drivers/net/wireless/ti/wlcore/main.c | 11 ----------- drivers/net/wireless/ti/wlcore/tx.c | 20 ++++++++++---------- drivers/net/wireless/ti/wlcore/wlcore.h | 5 +---- drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 --- 9 files changed, 39 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index d33117efec79..03ff1ce56bb4 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1361,6 +1361,14 @@ out: return ret; } +static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) +{ + if (is_gem) + return WL12XX_TX_HW_BLOCK_GEM_SPARE; + + return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; +} + static struct wlcore_ops wl12xx_ops = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, @@ -1384,6 +1392,7 @@ static struct wlcore_ops wl12xx_ops = { .set_rx_csum = NULL, .ap_get_mimo_wide_rate_mask = NULL, .debugfs_init = wl12xx_debugfs_add_files, + .get_spare_blocks = wl12xx_get_spare_blocks, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { @@ -1419,8 +1428,6 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) wl->rtable = wl12xx_rtable; wl->num_tx_desc = 16; wl->num_rx_desc = 8; - wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; - wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE; wl->band_rate_to_idx = wl12xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index d67be3eae3d9..c651f872d7d5 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -874,7 +874,7 @@ static int wl18xx_hw_init(struct wl1271 *wl) ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap, sdio_align_size, - WL18XX_TX_HW_BLOCK_SPARE, + WL18XX_TX_HW_EXTRA_BLOCK_SPARE, WL18XX_HOST_IF_LEN_SIZE_FIELD); if (ret < 0) return ret; @@ -1034,6 +1034,12 @@ static int wl18xx_handle_static_data(struct wl1271 *wl, return 0; } +static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) +{ + /* TODO: dynamically change to extra only when we have GEM or TKIP */ + return WL18XX_TX_HW_EXTRA_BLOCK_SPARE; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -1056,6 +1062,7 @@ static struct wlcore_ops wl18xx_ops = { .get_mac = wl18xx_get_mac, .debugfs_init = wl18xx_debugfs_add_files, .handle_static_data = wl18xx_handle_static_data, + .get_spare_blocks = wl18xx_get_spare_blocks, }; /* HT cap appropriate for wide channels */ @@ -1129,8 +1136,6 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) wl->rtable = wl18xx_rtable; wl->num_tx_desc = 32; wl->num_rx_desc = 16; - wl->normal_tx_spare = WL18XX_TX_HW_BLOCK_SPARE; - wl->gem_tx_spare = WL18XX_TX_HW_GEM_BLOCK_SPARE; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; diff --git a/drivers/net/wireless/ti/wl18xx/tx.h b/drivers/net/wireless/ti/wl18xx/tx.h index 2417262de207..8aecaf09da9c 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.h +++ b/drivers/net/wireless/ti/wl18xx/tx.h @@ -25,7 +25,8 @@ #include "../wlcore/wlcore.h" #define WL18XX_TX_HW_BLOCK_SPARE 1 -#define WL18XX_TX_HW_GEM_BLOCK_SPARE 2 +/* for special cases - namely, TKIP and GEM */ +#define WL18XX_TX_HW_EXTRA_BLOCK_SPARE 2 #define WL18XX_TX_HW_BLOCK_SIZE 268 #define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index fc44262e4cf0..fcd60636e9d1 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -507,7 +507,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(last_rssi_event); VIF_STATE_PRINT_INT(ba_support); VIF_STATE_PRINT_INT(ba_allowed); - VIF_STATE_PRINT_INT(is_gem); VIF_STATE_PRINT_LLHEX(tx_security_seq); VIF_STATE_PRINT_INT(tx_security_last_seq_lsb); } diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index c590b6f529d1..2cb35218ba47 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -167,4 +167,13 @@ wlcore_handle_static_data(struct wl1271 *wl, void *static_data) return 0; } +static inline int +wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem) +{ + if (!wl->ops->get_spare_blocks) + BUG_ON(1); + + return wl->ops->get_spare_blocks(wl, is_gem); +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 5ac062831666..0f25d4eea037 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2799,17 +2799,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, int ret; bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); - /* - * A role set to GEM cipher requires different Tx settings (namely - * spare blocks). Note when we are in this mode so the HW can adjust. - */ - if (key_type == KEY_GEM) { - if (action == KEY_ADD_OR_REPLACE) - wlvif->is_gem = true; - else if (action == KEY_REMOVE) - wlvif->is_gem = false; - } - if (is_ap) { struct wl1271_station *wl_sta; u8 hlid; diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 6b68e29a1e92..0949ab1f5972 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -187,28 +187,24 @@ EXPORT_SYMBOL(wlcore_calc_packet_alignment); static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb, u32 extra, u32 buf_offset, - u8 hlid) + u8 hlid, bool is_gem) { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; u32 total_blocks; int id, ret = -EBUSY, ac; - u32 spare_blocks = wl->normal_tx_spare; - bool is_dummy = false; + u32 spare_blocks; if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE) return -EAGAIN; + spare_blocks = wlcore_hw_get_spare_blocks(wl, is_gem); + /* allocate free identifier for the packet */ id = wl1271_alloc_tx_id(wl, skb); if (id < 0) return id; - if (unlikely(wl12xx_is_dummy_packet(wl, skb))) - is_dummy = true; - else if (wlvif->is_gem) - spare_blocks = wl->gem_tx_spare; - total_blocks = wlcore_hw_calc_tx_blocks(wl, total_len, spare_blocks); if (total_blocks <= wl->tx_blocks_available) { @@ -230,7 +226,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); wl->tx_allocated_pkts[ac]++; - if (!is_dummy && wlvif && + if (!wl12xx_is_dummy_packet(wl, skb) && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS && test_bit(hlid, wlvif->ap.sta_hlid_map)) wl->links[hlid].allocated_pkts++; @@ -349,6 +345,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, u32 total_len; u8 hlid; bool is_dummy; + bool is_gem = false; if (!skb) return -EINVAL; @@ -377,6 +374,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; wlvif->default_key = idx; } + + is_gem = (cipher == WL1271_CIPHER_SUITE_GEM); } hlid = wl12xx_tx_get_hlid(wl, wlvif, skb); if (hlid == WL12XX_INVALID_LINK_ID) { @@ -384,7 +383,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, return -EINVAL; } - ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid); + ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid, + is_gem); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 16c28bbd1b20..5274ace6c8e4 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -75,6 +75,7 @@ struct wlcore_ops { int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir); int (*handle_static_data)(struct wl1271 *wl, struct wl1271_static_data *static_data); + int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); }; enum wlcore_partitions { @@ -354,10 +355,6 @@ struct wl1271 { /* number of RX descriptors the HW supports. */ u32 num_rx_desc; - /* spare Tx blocks for normal/GEM operating modes */ - u32 normal_tx_spare; - u32 gem_tx_spare; - /* translate HW Tx rates to standard rate-indices */ const u8 **band_rate_to_idx; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 83c9869105c3..8260b1e9288a 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -417,9 +417,6 @@ struct wl12xx_vif { struct work_struct rx_streaming_disable_work; struct timer_list rx_streaming_timer; - /* does the current role use GEM for encryption (AP or STA) */ - bool is_gem; - /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) -- cgit v1.2.3 From 6639611467f34038aa63c5cb9f8d9e48171d6022 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 18 May 2012 07:46:38 +0300 Subject: wlcore: add stop reason bitmap for waking/starting queues Allow the driver to wake/stop the queues for multiple reasons. A queue is started when no stop-reasons exist. Convert all wake/stop queue calls to use the new API. Before, a stopped queue was almost synonymous a high-watermark on Tx. Remove a bit of code in wl12xx_tx_reset() that relied on it. Internal packets arriving from mac80211 are also discarded when a queue is stopped. A notable exception to this is the watermark reason, which is a "soft"-stop reason. We allow traffic to gradually come to a halt, but we don't mind spurious packets here and there. This is merely a flow regulation mechanism. Based on a similar patch by Eliad Peller . Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 21 ++++-- drivers/net/wireless/ti/wlcore/tx.c | 109 ++++++++++++++++++++++++++++---- drivers/net/wireless/ti/wlcore/tx.h | 21 +++++- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- 4 files changed, 132 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0f25d4eea037..bbab19a1ce8a 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -865,7 +865,7 @@ static void wl1271_recovery_work(struct work_struct *work) } /* Prevent spurious TX during FW restart */ - ieee80211_stop_queues(wl->hw); + wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); if (wl->sched_scanning) { ieee80211_sched_scan_stopped(wl->hw); @@ -890,7 +890,7 @@ static void wl1271_recovery_work(struct work_struct *work) * Its safe to enable TX now - the queues are stopped after a request * to restart the HW. */ - ieee80211_wake_queues(wl->hw); + wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); return; out_unlock: mutex_unlock(&wl->mutex); @@ -1107,9 +1107,16 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) spin_lock_irqsave(&wl->wl_lock, flags); - /* queue the packet */ + /* + * drop the packet if the link is invalid or the queue is stopped + * for any reason but watermark. Watermark is a "soft"-stop so we + * allow these packets through. + */ if (hlid == WL12XX_INVALID_LINK_ID || - (wlvif && !test_bit(hlid, wlvif->links_map))) { + (wlvif && !test_bit(hlid, wlvif->links_map)) || + (wlcore_is_queue_stopped(wl, q) && + !wlcore_is_queue_stopped_by_reason(wl, q, + WLCORE_QUEUE_STOP_REASON_WATERMARK))) { wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q); ieee80211_free_txskb(hw, skb); goto out; @@ -1127,8 +1134,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) */ if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q); - ieee80211_stop_queue(wl->hw, mapping); - set_bit(q, &wl->stopped_queues_map); + wlcore_stop_queue_locked(wl, q, + WLCORE_QUEUE_STOP_REASON_WATERMARK); } /* @@ -1711,7 +1718,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) cancel_delayed_work_sync(&wl->connection_loss_work); /* let's notify MAC80211 about the remaining pending TX frames */ - wl12xx_tx_reset(wl, true); + wl12xx_tx_reset(wl); mutex_lock(&wl->mutex); wl1271_power_off(wl); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 0949ab1f5972..f68567b1524c 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -443,18 +443,15 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, void wl1271_handle_tx_low_watermark(struct wl1271 *wl) { - unsigned long flags; int i; for (i = 0; i < NUM_TX_QUEUES; i++) { - if (test_bit(i, &wl->stopped_queues_map) && + if (wlcore_is_queue_stopped_by_reason(wl, i, + WLCORE_QUEUE_STOP_REASON_WATERMARK) && wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) { /* firmware buffer has space, restart queues */ - spin_lock_irqsave(&wl->wl_lock, flags); - ieee80211_wake_queue(wl->hw, - wl1271_tx_get_mac80211_queue(i)); - clear_bit(i, &wl->stopped_queues_map); - spin_unlock_irqrestore(&wl->wl_lock, flags); + wlcore_wake_queue(wl, i, + WLCORE_QUEUE_STOP_REASON_WATERMARK); } } } @@ -963,7 +960,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) } /* caller must hold wl->mutex and TX must be stopped */ -void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) +void wl12xx_tx_reset(struct wl1271 *wl) { int i; struct sk_buff *skb; @@ -978,15 +975,12 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) wl->tx_queue_count[i] = 0; } - wl->stopped_queues_map = 0; - /* * Make sure the driver is at a consistent state, in case this * function is called from a context other than interface removal. * This call will always wake the TX queues. */ - if (reset_tx_queues) - wl1271_handle_tx_low_watermark(wl); + wl1271_handle_tx_low_watermark(wl); for (i = 0; i < wl->num_tx_desc; i++) { if (wl->tx_frames[i] == NULL) @@ -1060,3 +1054,94 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) return BIT(__ffs(rate_set)); } + +void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason) +{ + bool stopped = !!wl->queue_stop_reasons[queue]; + + /* queue should not be stopped for this reason */ + WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue])); + + if (stopped) + return; + + ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); +} + +void wlcore_stop_queue(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason) +{ + unsigned long flags; + + spin_lock_irqsave(&wl->wl_lock, flags); + wlcore_stop_queue_locked(wl, queue, reason); + spin_unlock_irqrestore(&wl->wl_lock, flags); +} + +void wlcore_wake_queue(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason) +{ + unsigned long flags; + + spin_lock_irqsave(&wl->wl_lock, flags); + + /* queue should not be clear for this reason */ + WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue])); + + if (wl->queue_stop_reasons[queue]) + goto out; + + ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue)); + +out: + spin_unlock_irqrestore(&wl->wl_lock, flags); +} + +void wlcore_stop_queues(struct wl1271 *wl, + enum wlcore_queue_stop_reason reason) +{ + int i; + + for (i = 0; i < NUM_TX_QUEUES; i++) + wlcore_stop_queue(wl, i, reason); +} + +void wlcore_wake_queues(struct wl1271 *wl, + enum wlcore_queue_stop_reason reason) +{ + int i; + + for (i = 0; i < NUM_TX_QUEUES; i++) + wlcore_wake_queue(wl, i, reason); +} + +void wlcore_reset_stopped_queues(struct wl1271 *wl) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&wl->wl_lock, flags); + + for (i = 0; i < NUM_TX_QUEUES; i++) { + if (!wl->queue_stop_reasons[i]) + continue; + + wl->queue_stop_reasons[i] = 0; + ieee80211_wake_queue(wl->hw, + wl1271_tx_get_mac80211_queue(i)); + } + + spin_unlock_irqrestore(&wl->wl_lock, flags); +} + +bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason) +{ + return test_bit(reason, &wl->queue_stop_reasons[queue]); +} + +bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue) +{ + return !!wl->queue_stop_reasons[queue]; +} diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index e24c436bf65f..6bf695681762 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -184,6 +184,11 @@ struct wl1271_tx_hw_res_if { struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; } __packed; +enum wlcore_queue_stop_reason { + WLCORE_QUEUE_STOP_REASON_WATERMARK, + WLCORE_QUEUE_STOP_REASON_FW_RESTART, +}; + static inline int wl1271_tx_get_queue(int queue) { switch (queue) { @@ -230,7 +235,7 @@ void wl1271_tx_work(struct work_struct *work); void wl1271_tx_work_locked(struct wl1271 *wl); void wl1271_tx_complete(struct wl1271 *wl); void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues); +void wl12xx_tx_reset(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl); u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, @@ -247,6 +252,20 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids); unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length); void wl1271_free_tx_id(struct wl1271 *wl, int id); +void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason); +void wlcore_stop_queue(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason); +void wlcore_wake_queue(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason); +void wlcore_stop_queues(struct wl1271 *wl, + enum wlcore_queue_stop_reason reason); +void wlcore_wake_queues(struct wl1271 *wl, + enum wlcore_queue_stop_reason reason); +void wlcore_reset_stopped_queues(struct wl1271 *wl); +bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue, + enum wlcore_queue_stop_reason reason); +bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue); /* from main.c */ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 5274ace6c8e4..681054331fd2 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -209,7 +209,7 @@ struct wl1271 { /* Frames scheduled for transmission, not handled yet */ int tx_queue_count[NUM_TX_QUEUES]; - long stopped_queues_map; + unsigned long queue_stop_reasons[NUM_TX_QUEUES]; /* Frames received, not handled yet by mac80211 */ struct sk_buff_head deferred_rx_queue; -- cgit v1.2.3 From 2c38849f4a247673c8203a569444042e32d82410 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 18 May 2012 07:46:39 +0300 Subject: wlcore: stop queues on Tx flush Stop network queues during Tx flush, and also drop other internal mac80211 packets (mgmt) that may arrive when the queues are stopped. When flush is done all driver queues are clear, forcefully if needed. Protect the Tx flush operation with a new mutex, to prevent concurrency that can mess us queue state. Based on a patch by Eliad Peller Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 1 + drivers/net/wireless/ti/wlcore/tx.c | 11 ++++++++++- drivers/net/wireless/ti/wlcore/tx.h | 1 + drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++ 4 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index bbab19a1ce8a..d81c86cbbf71 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5148,6 +5148,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) wl->state = WL1271_STATE_OFF; wl->fw_type = WL12XX_FW_TYPE_NONE; mutex_init(&wl->mutex); + mutex_init(&wl->flush_mutex); order = get_order(WL1271_AGGR_BUFFER_SIZE); wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index f68567b1524c..78bf1b9208a9 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1024,6 +1024,11 @@ void wl1271_tx_flush(struct wl1271 *wl) int i; timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT); + /* only one flush should be in progress, for consistent queue state */ + mutex_lock(&wl->flush_mutex); + + wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH); + while (!time_after(jiffies, timeout)) { mutex_lock(&wl->mutex); wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d", @@ -1032,7 +1037,7 @@ void wl1271_tx_flush(struct wl1271 *wl) if ((wl->tx_frames_cnt == 0) && (wl1271_tx_total_queue_count(wl) == 0)) { mutex_unlock(&wl->mutex); - return; + goto out; } mutex_unlock(&wl->mutex); msleep(1); @@ -1045,6 +1050,10 @@ void wl1271_tx_flush(struct wl1271 *wl) for (i = 0; i < WL12XX_MAX_LINKS; i++) wl1271_tx_reset_link_queues(wl, i); mutex_unlock(&wl->mutex); + +out: + wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH); + mutex_unlock(&wl->flush_mutex); } u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 6bf695681762..e058a55f533d 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -187,6 +187,7 @@ struct wl1271_tx_hw_res_if { enum wlcore_queue_stop_reason { WLCORE_QUEUE_STOP_REASON_WATERMARK, WLCORE_QUEUE_STOP_REASON_FW_RESTART, + WLCORE_QUEUE_STOP_REASON_FLUSH, }; static inline int wl1271_tx_get_queue(int queue) diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 681054331fd2..99a061950a3a 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -378,6 +378,9 @@ struct wl1271 { /* the current channel type */ enum nl80211_channel_type channel_type; + + /* mutex for protecting the tx_flush function */ + struct mutex flush_mutex; }; int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); -- cgit v1.2.3 From a1c597f2b22cdc228de3c58784b00e80b9b53e03 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 18 May 2012 07:46:40 +0300 Subject: wlcore/wl12xx/wl18xx: implement op_set_key per HW arch The 12xx set_key just calls the common wlcore_set_key function, in order to program the keys into the FW. The 18xx variant changes the spare block count when a GEM or TKIP key is set. Also modify the get_spare_blocks HW op for 18xx to return the correct numbers of spare blocks, according to what is currently set in FW. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 9 ++++ drivers/net/wireless/ti/wl18xx/acx.c | 4 ++ drivers/net/wireless/ti/wl18xx/main.c | 96 ++++++++++++++++++++++++++++----- drivers/net/wireless/ti/wl18xx/wl18xx.h | 3 ++ drivers/net/wireless/ti/wlcore/hw_ops.h | 12 +++++ drivers/net/wireless/ti/wlcore/main.c | 14 ++++- drivers/net/wireless/ti/wlcore/tx.c | 3 ++ drivers/net/wireless/ti/wlcore/tx.h | 1 + drivers/net/wireless/ti/wlcore/wlcore.h | 8 +++ 9 files changed, 136 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 03ff1ce56bb4..2f6a39bfcf18 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1369,6 +1369,14 @@ static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT; } +static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ + return wlcore_set_key(wl, cmd, vif, sta, key_conf); +} + static struct wlcore_ops wl12xx_ops = { .identify_chip = wl12xx_identify_chip, .identify_fw = wl12xx_identify_fw, @@ -1393,6 +1401,7 @@ static struct wlcore_ops wl12xx_ops = { .ap_get_mimo_wide_rate_mask = NULL, .debugfs_init = wl12xx_debugfs_add_files, .get_spare_blocks = wl12xx_get_spare_blocks, + .set_key = wl12xx_set_key, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index 3379db23a165..01ba40321435 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -32,6 +32,10 @@ int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, struct wl18xx_acx_host_config_bitmap *bitmap_conf; int ret; + wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d", + host_cfg_bitmap, sdio_blk_size, extra_mem_blks, + len_field_size); + bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); if (!bitmap_conf) { ret = -ENOMEM; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c651f872d7d5..9e5ce569f8da 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -848,17 +848,12 @@ static void wl18xx_tx_immediate_completion(struct wl1271 *wl) wl18xx_tx_immediate_complete(wl); } -static int wl18xx_hw_init(struct wl1271 *wl) +static int wl18xx_set_host_cfg_bitmap(struct wl1271 *wl, u32 extra_mem_blk) { int ret; - struct wl18xx_priv *priv = wl->priv; - u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE | - HOST_IF_CFG_ADD_RX_ALIGNMENT; - u32 sdio_align_size = 0; - - /* (re)init private structures. Relevant on recovery as well. */ - priv->last_fw_rls_idx = 0; + u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE | + HOST_IF_CFG_ADD_RX_ALIGNMENT; /* Enable Tx SDIO padding */ if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) { @@ -873,12 +868,28 @@ static int wl18xx_hw_init(struct wl1271 *wl) } ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap, - sdio_align_size, - WL18XX_TX_HW_EXTRA_BLOCK_SPARE, + sdio_align_size, extra_mem_blk, WL18XX_HOST_IF_LEN_SIZE_FIELD); if (ret < 0) return ret; + return 0; +} + +static int wl18xx_hw_init(struct wl1271 *wl) +{ + int ret; + struct wl18xx_priv *priv = wl->priv; + + /* (re)init private structures. Relevant on recovery as well. */ + priv->last_fw_rls_idx = 0; + priv->extra_spare_vif_count = 0; + + /* set the default amount of spare blocks in the bitmap */ + ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE); + if (ret < 0) + return ret; + if (checksum_param) { ret = wl18xx_acx_set_checksum_state(wl); if (ret != 0) @@ -1036,8 +1047,68 @@ static int wl18xx_handle_static_data(struct wl1271 *wl, static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem) { - /* TODO: dynamically change to extra only when we have GEM or TKIP */ - return WL18XX_TX_HW_EXTRA_BLOCK_SPARE; + struct wl18xx_priv *priv = wl->priv; + + /* If we have VIFs requiring extra spare, indulge them */ + if (priv->extra_spare_vif_count) + return WL18XX_TX_HW_EXTRA_BLOCK_SPARE; + + return WL18XX_TX_HW_BLOCK_SPARE; +} + +static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ + struct wl18xx_priv *priv = wl->priv; + bool change_spare = false; + int ret; + + /* + * when adding the first or removing the last GEM/TKIP interface, + * we have to adjust the number of spare blocks. + */ + change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM || + key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) && + ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) || + (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY)); + + /* no need to change spare - just regular set_key */ + if (!change_spare) + return wlcore_set_key(wl, cmd, vif, sta, key_conf); + + /* + * stop the queues and flush to ensure the next packets are + * in sync with FW spare block accounting + */ + wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK); + wl1271_tx_flush(wl); + + ret = wlcore_set_key(wl, cmd, vif, sta, key_conf); + if (ret < 0) + goto out; + + /* key is now set, change the spare blocks */ + if (cmd == SET_KEY) { + ret = wl18xx_set_host_cfg_bitmap(wl, + WL18XX_TX_HW_EXTRA_BLOCK_SPARE); + if (ret < 0) + goto out; + + priv->extra_spare_vif_count++; + } else { + ret = wl18xx_set_host_cfg_bitmap(wl, + WL18XX_TX_HW_BLOCK_SPARE); + if (ret < 0) + goto out; + + priv->extra_spare_vif_count--; + } + +out: + wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK); + return ret; } static struct wlcore_ops wl18xx_ops = { @@ -1063,6 +1134,7 @@ static struct wlcore_ops wl18xx_ops = { .debugfs_init = wl18xx_debugfs_add_files, .handle_static_data = wl18xx_handle_static_data, .get_spare_blocks = wl18xx_get_spare_blocks, + .set_key = wl18xx_set_key, }; /* HT cap appropriate for wide channels */ diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 34e202bc2bb3..b9c43097d2f6 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -36,6 +36,9 @@ struct wl18xx_priv { u8 last_fw_rls_idx; u8 board_type; + + /* number of VIFs requiring extra spare mem-blocks */ + int extra_spare_vif_count; }; #define WL18XX_FW_MAX_TX_STATUS_DESC 33 diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 2cb35218ba47..34e0498727fc 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -176,4 +176,16 @@ wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem) return wl->ops->get_spare_blocks(wl, is_gem); } +static inline int +wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ + if (!wl->ops->set_key) + BUG_ON(1); + + return wl->ops->set_key(wl, cmd, vif, sta, key_conf); +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d81c86cbbf71..7c4f78136bb1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2883,12 +2883,21 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif, return 0; } -static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, +static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wl1271 *wl = hw->priv; + + return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf); +} + +int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; u32 tx_seq_32 = 0; @@ -2999,6 +3008,7 @@ out_unlock: return ret; } +EXPORT_SYMBOL_GPL(wlcore_set_key); static int wl1271_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -4643,7 +4653,7 @@ static const struct ieee80211_ops wl1271_ops = { .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, - .set_key = wl1271_op_set_key, + .set_key = wlcore_op_set_key, .hw_scan = wl1271_op_hw_scan, .cancel_hw_scan = wl1271_op_cancel_hw_scan, .sched_scan_start = wl1271_op_sched_scan_start, diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 78bf1b9208a9..da9a07d2cf4b 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -1055,6 +1055,7 @@ out: wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH); mutex_unlock(&wl->flush_mutex); } +EXPORT_SYMBOL_GPL(wl1271_tx_flush); u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) { @@ -1115,6 +1116,7 @@ void wlcore_stop_queues(struct wl1271 *wl, for (i = 0; i < NUM_TX_QUEUES; i++) wlcore_stop_queue(wl, i, reason); } +EXPORT_SYMBOL_GPL(wlcore_stop_queues); void wlcore_wake_queues(struct wl1271 *wl, enum wlcore_queue_stop_reason reason) @@ -1124,6 +1126,7 @@ void wlcore_wake_queues(struct wl1271 *wl, for (i = 0; i < NUM_TX_QUEUES; i++) wlcore_wake_queue(wl, i, reason); } +EXPORT_SYMBOL_GPL(wlcore_wake_queues); void wlcore_reset_stopped_queues(struct wl1271 *wl) { diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index e058a55f533d..49e441f34839 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -188,6 +188,7 @@ enum wlcore_queue_stop_reason { WLCORE_QUEUE_STOP_REASON_WATERMARK, WLCORE_QUEUE_STOP_REASON_FW_RESTART, WLCORE_QUEUE_STOP_REASON_FLUSH, + WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */ }; static inline int wl1271_tx_get_queue(int queue) diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 99a061950a3a..4ca968fac0eb 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -76,6 +76,10 @@ struct wlcore_ops { int (*handle_static_data)(struct wl1271 *wl, struct wl1271_static_data *static_data); int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem); + int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf); }; enum wlcore_partitions { @@ -387,6 +391,10 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); int __devexit wlcore_remove(struct platform_device *pdev); struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size); int wlcore_free_hw(struct wl1271 *wl); +int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf); /* Firmware image load chunk size */ #define CHUNK_SIZE 16384 -- cgit v1.2.3 From afbe37185c0ecad3442791be666b6851eba52318 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Wed, 16 May 2012 11:34:18 +0300 Subject: wlcore: do not send stop fwlog cmd if fw is hanged If the driver received a watchdog interrupt then the assumption is that the fw is hanged. Avoid sending the stop fwlog command in case of a watchdog recovey to avoid waiting for the 2 seconds timeout of the command. Signed-off-by: Yoni Divinsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 9 +++++++-- drivers/net/wireless/ti/wlcore/wlcore.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 7c4f78136bb1..54da16501e42 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -544,6 +544,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { wl1271_error("watchdog interrupt received! " "starting recovery."); + wl->watchdog_recovery = true; wl12xx_queue_recovery_work(wl); /* restarting the chip. ignore any other interrupt. */ @@ -782,10 +783,12 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) /* * Make sure the chip is awake and the logger isn't active. - * This might fail if the firmware hanged. + * Do not send a stop fwlog command if the fw is hanged. */ - if (!wl1271_ps_elp_wakeup(wl)) + if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery) wl12xx_cmd_stop_fwlog(wl); + else + goto out; /* Read the first memory block address */ wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2); @@ -879,6 +882,7 @@ static void wl1271_recovery_work(struct work_struct *work) vif = wl12xx_wlvif_to_vif(wlvif); __wl1271_op_remove_interface(wl, vif, false); } + wl->watchdog_recovery = false; mutex_unlock(&wl->mutex); wl1271_op_stop(wl->hw); @@ -893,6 +897,7 @@ static void wl1271_recovery_work(struct work_struct *work) wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART); return; out_unlock: + wl->watchdog_recovery = false; mutex_unlock(&wl->mutex); } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 4ca968fac0eb..e63450072f4d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -252,6 +252,7 @@ struct wl1271 { /* Hardware recovery work */ struct work_struct recovery_work; + bool watchdog_recovery; /* Pointer that holds DMA-friendly block for the mailbox */ struct event_mailbox *mbox; -- cgit v1.2.3 From 9f5b424d6c44db1e02744ddc10c569dc7eb1c100 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 14:53:17 +0300 Subject: wl12xx: send beacon loss events to userspace Send beacon loss events to userspace, so it will be able to initiate roaming before disconnection Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/event.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 4ed835799178..c976f0409865 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -156,6 +156,15 @@ static int wl1271_event_process(struct wl1271 *wl) */ ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work, msecs_to_jiffies(delay)); + + wl12xx_for_each_wlvif_sta(wl, wlvif) { + vif = wl12xx_wlvif_to_vif(wlvif); + + ieee80211_cqm_rssi_notify( + vif, + NL80211_CQM_RSSI_BEACON_LOSS_EVENT, + GFP_KERNEL); + } } if (vector & REGAINED_BSS_EVENT_ID) { -- cgit v1.2.3 From 186b5a7c938b13b9f6ee2532a13596556c224df0 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 16:35:20 +0300 Subject: wl12xx/wl18xx: add erp protection IE to the beacon filter We have to reconfigure the fw when erp protection should be enabled/disabled. Pass beacons containing changes in the ERP protection IE, so we could analyze them. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 6 +++++- drivers/net/wireless/ti/wl18xx/main.c | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 2f6a39bfcf18..41017baaf253 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -216,7 +216,7 @@ static struct wlcore_conf wl12xx_conf = { .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, .suspend_listen_interval = 3, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, - .bcn_filt_ie_count = 2, + .bcn_filt_ie_count = 3, .bcn_filt_ie = { [0] = { .ie = WLAN_EID_CHANNEL_SWITCH, @@ -226,6 +226,10 @@ static struct wlcore_conf wl12xx_conf = { .ie = WLAN_EID_HT_OPERATION, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, + [2] = { + .ie = WLAN_EID_ERP_INFO, + .rule = CONF_BCN_RULE_PASS_ON_CHANGE, + }, }, .synch_fail_thold = 12, .bss_lose_timeout = 400, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 9e5ce569f8da..c8d45f011c63 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -339,7 +339,7 @@ static struct wlcore_conf wl18xx_conf = { .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, .suspend_listen_interval = 3, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, - .bcn_filt_ie_count = 2, + .bcn_filt_ie_count = 3, .bcn_filt_ie = { [0] = { .ie = WLAN_EID_CHANNEL_SWITCH, @@ -349,6 +349,10 @@ static struct wlcore_conf wl18xx_conf = { .ie = WLAN_EID_HT_OPERATION, .rule = CONF_BCN_RULE_PASS_ON_CHANGE, }, + [2] = { + .ie = WLAN_EID_ERP_INFO, + .rule = CONF_BCN_RULE_PASS_ON_CHANGE, + }, }, .synch_fail_thold = 12, .bss_lose_timeout = 400, -- cgit v1.2.3 From 6469933605a3ecdfa66b98160cde98ecd256cb3f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 4 Jun 2012 12:44:16 +0000 Subject: ethernet: Remove casts to same type Adding casts of objects to the same type is unnecessary and confusing for a human reader. For example, this cast: int y; int *p = (int *)&y; I used the coccinelle script below to find and remove these unnecessary casts. I manually removed the conversions this script produces of casts with __force, __iomem and __user. @@ type T; T *p; @@ - (T *)p + p A function in atl1e_main.c was passed a const pointer when it actually modified elements of the structure. Change the argument to a non-const pointer. A function in stmmac needed a __force to avoid a sparse warning. Added it. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/ethernet/8390/apne.c | 2 +- drivers/net/ethernet/aeroflex/greth.c | 8 ++--- drivers/net/ethernet/amd/declance.c | 4 +-- drivers/net/ethernet/apple/macmace.c | 2 +- drivers/net/ethernet/atheros/atl1c/atl1c_hw.c | 6 ++-- drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 9 +++--- drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c | 2 +- drivers/net/ethernet/atheros/atl1e/atl1e_main.c | 35 ++++++++++------------ drivers/net/ethernet/atheros/atlx/atl1.c | 2 +- drivers/net/ethernet/broadcom/bnx2.c | 3 +- drivers/net/ethernet/broadcom/cnic.c | 12 ++++---- drivers/net/ethernet/brocade/bna/cna_fwimg.c | 4 +-- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 2 +- drivers/net/ethernet/chelsio/cxgb4/sge.c | 2 +- .../net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 2 +- drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 5 ++-- drivers/net/ethernet/dec/tulip/de4x5.c | 2 +- drivers/net/ethernet/freescale/ucc_geth.c | 2 +- drivers/net/ethernet/hp/hp100.c | 6 ++-- drivers/net/ethernet/i825xx/lp486e.c | 8 ++--- drivers/net/ethernet/i825xx/sun3_82586.c | 4 +-- drivers/net/ethernet/marvell/mv643xx_eth.c | 4 +-- drivers/net/ethernet/marvell/pxa168_eth.c | 4 +-- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 4 +-- drivers/net/ethernet/neterion/s2io.c | 14 ++++----- drivers/net/ethernet/neterion/vxge/vxge-config.c | 8 ++--- drivers/net/ethernet/neterion/vxge/vxge-config.h | 2 +- drivers/net/ethernet/neterion/vxge/vxge-main.c | 8 ++--- drivers/net/ethernet/neterion/vxge/vxge-traffic.c | 5 ++-- drivers/net/ethernet/sgi/ioc3-eth.c | 4 +-- drivers/net/ethernet/smsc/smsc9420.c | 3 +- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 +- drivers/net/ethernet/sun/sunqe.c | 2 +- drivers/net/ethernet/via/via-velocity.c | 2 +- 34 files changed, 87 insertions(+), 97 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/8390/apne.c b/drivers/net/ethernet/8390/apne.c index 923959275a82..912ed7a5f33a 100644 --- a/drivers/net/ethernet/8390/apne.c +++ b/drivers/net/ethernet/8390/apne.c @@ -454,7 +454,7 @@ apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int rin buf[count-1] = inb(NE_BASE + NE_DATAPORT); } } else { - ptrc = (char*)buf; + ptrc = buf; for (cnt = 0; cnt < count; cnt++) *ptrc++ = inb(NE_BASE + NE_DATAPORT); } diff --git a/drivers/net/ethernet/aeroflex/greth.c b/drivers/net/ethernet/aeroflex/greth.c index 348501178089..9c77c736f171 100644 --- a/drivers/net/ethernet/aeroflex/greth.c +++ b/drivers/net/ethernet/aeroflex/greth.c @@ -1014,7 +1014,7 @@ static int greth_set_mac_add(struct net_device *dev, void *p) struct greth_regs *regs; greth = netdev_priv(dev); - regs = (struct greth_regs *) greth->regs; + regs = greth->regs; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; @@ -1036,7 +1036,7 @@ static void greth_set_hash_filter(struct net_device *dev) { struct netdev_hw_addr *ha; struct greth_private *greth = netdev_priv(dev); - struct greth_regs *regs = (struct greth_regs *) greth->regs; + struct greth_regs *regs = greth->regs; u32 mc_filter[2]; unsigned int bitnr; @@ -1055,7 +1055,7 @@ static void greth_set_multicast_list(struct net_device *dev) { int cfg; struct greth_private *greth = netdev_priv(dev); - struct greth_regs *regs = (struct greth_regs *) greth->regs; + struct greth_regs *regs = greth->regs; cfg = GRETH_REGLOAD(regs->control); if (dev->flags & IFF_PROMISC) @@ -1414,7 +1414,7 @@ static int __devinit greth_of_probe(struct platform_device *ofdev) goto error1; } - regs = (struct greth_regs *) greth->regs; + regs = greth->regs; greth->irq = ofdev->archdata.irqs[0]; dev_set_drvdata(greth->dev, dev); diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c index 75299f500ee5..7203b522f234 100644 --- a/drivers/net/ethernet/amd/declance.c +++ b/drivers/net/ethernet/amd/declance.c @@ -623,7 +623,7 @@ static int lance_rx(struct net_device *dev) skb_put(skb, len); /* make room */ cp_from_buf(lp->type, skb->data, - (char *)lp->rx_buf_ptr_cpu[entry], len); + lp->rx_buf_ptr_cpu[entry], len); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); @@ -919,7 +919,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev) *lib_ptr(ib, btx_ring[entry].length, lp->type) = (-len); *lib_ptr(ib, btx_ring[entry].misc, lp->type) = 0; - cp_to_buf(lp->type, (char *)lp->tx_buf_ptr_cpu[entry], skb->data, len); + cp_to_buf(lp->type, lp->tx_buf_ptr_cpu[entry], skb->data, len); /* Now, give the packet to the lance */ *lib_ptr(ib, btx_ring[entry].tmd1, lp->type) = diff --git a/drivers/net/ethernet/apple/macmace.c b/drivers/net/ethernet/apple/macmace.c index ab7ff8645ab1..a92ddee7f665 100644 --- a/drivers/net/ethernet/apple/macmace.c +++ b/drivers/net/ethernet/apple/macmace.c @@ -228,7 +228,7 @@ static int __devinit mace_probe(struct platform_device *pdev) * bits are reversed. */ - addr = (void *)MACE_PROM; + addr = MACE_PROM; for (j = 0; j < 6; ++j) { u8 v = bitrev8(addr[j<<4]); diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c index ff9c73859d45..801f0126512d 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c @@ -602,7 +602,7 @@ int atl1c_phy_reset(struct atl1c_hw *hw) int atl1c_phy_init(struct atl1c_hw *hw) { - struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; int ret_val; u16 mii_bmcr_data = BMCR_RESET; @@ -696,7 +696,7 @@ int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) /* select one link mode to get lower power consumption */ int atl1c_phy_to_ps_link(struct atl1c_hw *hw) { - struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; int ret = 0; u16 autoneg_advertised = ADVERTISED_10baseT_Half; @@ -768,7 +768,7 @@ int atl1c_restart_autoneg(struct atl1c_hw *hw) int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc) { - struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; u32 master_ctrl, mac_ctrl, phy_ctrl; u32 wol_ctrl, speed; diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index 9cc15701101b..85717cb306d1 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -989,12 +989,12 @@ static int atl1c_setup_ring_resources(struct atl1c_adapter *adapter) } for (i = 0; i < AT_MAX_TRANSMIT_QUEUE; i++) { tpd_ring[i].buffer_info = - (struct atl1c_buffer *) (tpd_ring->buffer_info + count); + (tpd_ring->buffer_info + count); count += tpd_ring[i].count; } rfd_ring->buffer_info = - (struct atl1c_buffer *) (tpd_ring->buffer_info + count); + (tpd_ring->buffer_info + count); count += rfd_ring->count; rx_desc_count += rfd_ring->count; @@ -1227,7 +1227,7 @@ static void atl1c_start_mac(struct atl1c_adapter *adapter) */ static int atl1c_reset_mac(struct atl1c_hw *hw) { - struct atl1c_adapter *adapter = (struct atl1c_adapter *)hw->adapter; + struct atl1c_adapter *adapter = hw->adapter; struct pci_dev *pdev = adapter->pdev; u32 ctrl_data = 0; @@ -1531,8 +1531,7 @@ static inline void atl1c_clear_phy_int(struct atl1c_adapter *adapter) static bool atl1c_clean_tx_irq(struct atl1c_adapter *adapter, enum atl1c_trans_queue type) { - struct atl1c_tpd_ring *tpd_ring = (struct atl1c_tpd_ring *) - &adapter->tpd_ring[type]; + struct atl1c_tpd_ring *tpd_ring = &adapter->tpd_ring[type]; struct atl1c_buffer *buffer_info; struct pci_dev *pdev = adapter->pdev; u16 next_to_clean = atomic_read(&tpd_ring->next_to_clean); diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c index 6e61f9f9ebb5..82b23861bf55 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_ethtool.c @@ -268,7 +268,7 @@ static int atl1e_set_eeprom(struct net_device *netdev, if (eeprom_buff == NULL) return -ENOMEM; - ptr = (u32 *)eeprom_buff; + ptr = eeprom_buff; if (eeprom->offset & 3) { /* need read/modify/write of first changed EEPROM word */ diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 1220e511ced6..0aed82e1bb3e 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -641,8 +641,7 @@ static int __devinit atl1e_sw_init(struct atl1e_adapter *adapter) */ static void atl1e_clean_tx_ring(struct atl1e_adapter *adapter) { - struct atl1e_tx_ring *tx_ring = (struct atl1e_tx_ring *) - &adapter->tx_ring; + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; struct atl1e_tx_buffer *tx_buffer = NULL; struct pci_dev *pdev = adapter->pdev; u16 index, ring_count; @@ -686,7 +685,7 @@ static void atl1e_clean_tx_ring(struct atl1e_adapter *adapter) static void atl1e_clean_rx_ring(struct atl1e_adapter *adapter) { struct atl1e_rx_ring *rx_ring = - (struct atl1e_rx_ring *)&adapter->rx_ring; + &adapter->rx_ring; struct atl1e_rx_page_desc *rx_page_desc = rx_ring->rx_page_desc; u16 i, j; @@ -884,14 +883,12 @@ failed: return err; } -static inline void atl1e_configure_des_ring(const struct atl1e_adapter *adapter) +static inline void atl1e_configure_des_ring(struct atl1e_adapter *adapter) { - struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw; - struct atl1e_rx_ring *rx_ring = - (struct atl1e_rx_ring *)&adapter->rx_ring; - struct atl1e_tx_ring *tx_ring = - (struct atl1e_tx_ring *)&adapter->tx_ring; + struct atl1e_hw *hw = &adapter->hw; + struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; struct atl1e_rx_page_desc *rx_page_desc = NULL; int i, j; @@ -932,7 +929,7 @@ static inline void atl1e_configure_des_ring(const struct atl1e_adapter *adapter) static inline void atl1e_configure_tx(struct atl1e_adapter *adapter) { - struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw; + struct atl1e_hw *hw = &adapter->hw; u32 dev_ctrl_data = 0; u32 max_pay_load = 0; u32 jumbo_thresh = 0; @@ -975,7 +972,7 @@ static inline void atl1e_configure_tx(struct atl1e_adapter *adapter) static inline void atl1e_configure_rx(struct atl1e_adapter *adapter) { - struct atl1e_hw *hw = (struct atl1e_hw *)&adapter->hw; + struct atl1e_hw *hw = &adapter->hw; u32 rxf_len = 0; u32 rxf_low = 0; u32 rxf_high = 0; @@ -1224,8 +1221,7 @@ static inline void atl1e_clear_phy_int(struct atl1e_adapter *adapter) static bool atl1e_clean_tx_irq(struct atl1e_adapter *adapter) { - struct atl1e_tx_ring *tx_ring = (struct atl1e_tx_ring *) - &adapter->tx_ring; + struct atl1e_tx_ring *tx_ring = &adapter->tx_ring; struct atl1e_tx_buffer *tx_buffer = NULL; u16 hw_next_to_clean = AT_READ_REGW(&adapter->hw, REG_TPD_CONS_IDX); u16 next_to_clean = atomic_read(&tx_ring->next_to_clean); @@ -1384,15 +1380,14 @@ static struct atl1e_rx_page *atl1e_get_rx_page(struct atl1e_adapter *adapter, (struct atl1e_rx_page_desc *) adapter->rx_ring.rx_page_desc; u8 rx_using = rx_page_desc[que].rx_using; - return (struct atl1e_rx_page *)&(rx_page_desc[que].rx_page[rx_using]); + return &(rx_page_desc[que].rx_page[rx_using]); } static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, int *work_done, int work_to_do) { struct net_device *netdev = adapter->netdev; - struct atl1e_rx_ring *rx_ring = (struct atl1e_rx_ring *) - &adapter->rx_ring; + struct atl1e_rx_ring *rx_ring = &adapter->rx_ring; struct atl1e_rx_page_desc *rx_page_desc = (struct atl1e_rx_page_desc *) rx_ring->rx_page_desc; struct sk_buff *skb = NULL; @@ -1576,7 +1571,7 @@ static struct atl1e_tpd_desc *atl1e_get_tpd(struct atl1e_adapter *adapter) tx_ring->next_to_use = 0; memset(&tx_ring->desc[next_to_use], 0, sizeof(struct atl1e_tpd_desc)); - return (struct atl1e_tpd_desc *)&tx_ring->desc[next_to_use]; + return &tx_ring->desc[next_to_use]; } static struct atl1e_tx_buffer * @@ -2061,8 +2056,8 @@ static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state) if (wufc) { /* get link status */ - atl1e_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); - atl1e_read_phy_reg(hw, MII_BMSR, (u16 *)&mii_bmsr_data); + atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); + atl1e_read_phy_reg(hw, MII_BMSR, &mii_bmsr_data); mii_advertise_data = ADVERTISE_10HALF; @@ -2086,7 +2081,7 @@ static int atl1e_suspend(struct pci_dev *pdev, pm_message_t state) for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { msleep(100); atl1e_read_phy_reg(hw, MII_BMSR, - (u16 *)&mii_bmsr_data); + &mii_bmsr_data); if (mii_bmsr_data & BMSR_LSTATUS) break; } diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 5d10884e5080..149a294d54e9 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -1061,7 +1061,7 @@ static s32 atl1_setup_ring_resources(struct atl1_adapter *adapter) goto err_nomem; } rfd_ring->buffer_info = - (struct atl1_buffer *)(tpd_ring->buffer_info + tpd_ring->count); + (tpd_ring->buffer_info + tpd_ring->count); /* * real ring DMA buffer diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index ac7b74488531..ff5d3c1f1217 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -872,8 +872,7 @@ bnx2_alloc_mem(struct bnx2 *bp) bnapi = &bp->bnx2_napi[i]; - sblk = (void *) (status_blk + - BNX2_SBLK_MSIX_ALIGN_SIZE * i); + sblk = (status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); bnapi->status_blk.msix = sblk; bnapi->hw_tx_cons_ptr = &sblk->status_tx_quick_consumer_index; diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index c95e7b5e2b85..65e66caea50d 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -2585,7 +2585,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe) return; } - cqes[0] = (struct kcqe *) &kcqe; + cqes[0] = &kcqe; cnic_reply_bnx2x_kcqes(dev, ulp_type, cqes, 1); } @@ -4665,9 +4665,9 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) cp->kcq1.sw_prod_idx = 0; cp->kcq1.hw_prod_idx_ptr = - (u16 *) &sblk->status_completion_producer_index; + &sblk->status_completion_producer_index; - cp->kcq1.status_idx_ptr = (u16 *) &sblk->status_idx; + cp->kcq1.status_idx_ptr = &sblk->status_idx; /* Initialize the kernel complete queue context. */ val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE | @@ -4693,9 +4693,9 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) u32 sb = BNX2_L2CTX_L5_STATUSB_NUM(sb_id); cp->kcq1.hw_prod_idx_ptr = - (u16 *) &msblk->status_completion_producer_index; - cp->kcq1.status_idx_ptr = (u16 *) &msblk->status_idx; - cp->kwq_con_idx_ptr = (u16 *) &msblk->status_cmd_consumer_index; + &msblk->status_completion_producer_index; + cp->kcq1.status_idx_ptr = &msblk->status_idx; + cp->kwq_con_idx_ptr = &msblk->status_cmd_consumer_index; cp->int_num = sb_id << BNX2_PCICFG_INT_ACK_CMD_INT_NUM_SHIFT; cnic_ctx_wr(dev, kwq_cid_addr, L5_KRNLQ_HOST_QIDX, sb); cnic_ctx_wr(dev, kcq_cid_addr, L5_KRNLQ_HOST_QIDX, sb); diff --git a/drivers/net/ethernet/brocade/bna/cna_fwimg.c b/drivers/net/ethernet/brocade/bna/cna_fwimg.c index cfc22a64157e..6a68e8d93309 100644 --- a/drivers/net/ethernet/brocade/bna/cna_fwimg.c +++ b/drivers/net/ethernet/brocade/bna/cna_fwimg.c @@ -67,10 +67,10 @@ bfa_cb_image_get_chunk(enum bfi_asic_gen asic_gen, u32 off) { switch (asic_gen) { case BFI_ASIC_GEN_CT: - return (u32 *)(bfi_image_ct_cna + off); + return (bfi_image_ct_cna + off); break; case BFI_ASIC_GEN_CT2: - return (u32 *)(bfi_image_ct2_cna + off); + return (bfi_image_ct2_cna + off); break; default: return NULL; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 65e4b280619a..55cf72af69ce 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -575,7 +575,7 @@ static void t3_process_tid_release_list(struct work_struct *work) if (!skb) { spin_lock_bh(&td->tid_release_lock); p->ctx = (void *)td->tid_release_list; - td->tid_release_list = (struct t3c_tid_entry *)p; + td->tid_release_list = p; break; } mk_tid_release(skb, p - td->tid_maps.tid_tab); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index e111d974afd8..8596acaa402b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -753,7 +753,7 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q, end = (void *)q->desc + part1; } if ((uintptr_t)end & 8) /* 0-pad to multiple of 16 */ - *(u64 *)end = 0; + *end = 0; } /** diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 25e3308fc9d8..9dad56101e23 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -418,7 +418,7 @@ static int fwevtq_handler(struct sge_rspq *rspq, const __be64 *rsp, * restart a TX Ethernet Queue which was stopped for lack of * free TX Queue Descriptors ... */ - const struct cpl_sge_egr_update *p = (void *)cpl; + const struct cpl_sge_egr_update *p = cpl; unsigned int qid = EGR_QID(be32_to_cpu(p->opcode_qid)); struct sge *s = &adapter->sge; struct sge_txq *tq; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index 0bd585bba39d..f2d1ecdcaf98 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -934,7 +934,7 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *tq, end = (void *)tq->desc + part1; } if ((uintptr_t)end & 8) /* 0-pad to multiple of 16 */ - *(u64 *)end = 0; + *end = 0; } /** @@ -1323,8 +1323,7 @@ int t4vf_eth_xmit(struct sk_buff *skb, struct net_device *dev) */ if (unlikely((void *)sgl == (void *)tq->stat)) { sgl = (void *)tq->desc; - end = (void *)((void *)tq->desc + - ((void *)end - (void *)tq->stat)); + end = ((void *)tq->desc + ((void *)end - (void *)tq->stat)); } write_sgl(skb, tq, sgl, end, 0, addr); diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index d3cd489d11a2..f879e9224846 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -3973,7 +3973,7 @@ DevicePresent(struct net_device *dev, u_long aprom_addr) tmp = srom_rd(aprom_addr, i); *p++ = cpu_to_le16(tmp); } - de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + de4x5_dbg_srom(&lp->srom); } } diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 9ac14f804851..21c6574c5f15 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -185,7 +185,7 @@ static void mem_disp(u8 *addr, int size) for (; (u32) i < (u32) addr + size4Aling; i += 4) printk("%08x ", *((u32 *) (i))); for (; (u32) i < (u32) addr + size; i++) - printk("%02x", *((u8 *) (i))); + printk("%02x", *((i))); if (notAlign == 1) printk("\r\n"); } diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index d496673f0908..3f4391bede81 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -1217,7 +1217,7 @@ static int hp100_init_rxpdl(struct net_device *dev, ringptr->pdl = pdlptr + 1; ringptr->pdl_paddr = virt_to_whatever(dev, pdlptr + 1); - ringptr->skb = (void *) NULL; + ringptr->skb = NULL; /* * Write address and length of first PDL Fragment (which is used for @@ -1243,7 +1243,7 @@ static int hp100_init_txpdl(struct net_device *dev, ringptr->pdl = pdlptr; /* +1; */ ringptr->pdl_paddr = virt_to_whatever(dev, pdlptr); /* +1 */ - ringptr->skb = (void *) NULL; + ringptr->skb = NULL; return roundup(MAX_TX_FRAG * 2 + 2, 4); } @@ -1628,7 +1628,7 @@ static void hp100_clean_txring(struct net_device *dev) /* Conversion to new PCI API : NOP */ pci_unmap_single(lp->pci_dev, (dma_addr_t) lp->txrhead->pdl[1], lp->txrhead->pdl[2], PCI_DMA_TODEVICE); dev_kfree_skb_any(lp->txrhead->skb); - lp->txrhead->skb = (void *) NULL; + lp->txrhead->skb = NULL; lp->txrhead = lp->txrhead->next; lp->txrcommit--; } diff --git a/drivers/net/ethernet/i825xx/lp486e.c b/drivers/net/ethernet/i825xx/lp486e.c index 6c2952c8ea15..3735bfa53600 100644 --- a/drivers/net/ethernet/i825xx/lp486e.c +++ b/drivers/net/ethernet/i825xx/lp486e.c @@ -629,10 +629,10 @@ init_i596(struct net_device *dev) { memcpy ((void *)lp->eth_addr, dev->dev_addr, 6); lp->set_add.command = CmdIASetup; - i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add); + i596_add_cmd(dev, &lp->set_add); lp->tdr.command = CmdTDR; - i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr); + i596_add_cmd(dev, &lp->tdr); if (lp->scb.command && i596_timeout(dev, "i82596 init", 200)) return 1; @@ -737,7 +737,7 @@ i596_cleanup_cmd(struct net_device *dev) { lp = netdev_priv(dev); while (lp->cmd_head) { - cmd = (struct i596_cmd *)lp->cmd_head; + cmd = lp->cmd_head; lp->cmd_head = pa_to_va(lp->cmd_head->pa_next); lp->cmd_backlog--; @@ -1281,7 +1281,7 @@ static void set_multicast_list(struct net_device *dev) { lp->i596_config[8] |= 0x01; } - i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf); + i596_add_cmd(dev, &lp->set_conf); } } diff --git a/drivers/net/ethernet/i825xx/sun3_82586.c b/drivers/net/ethernet/i825xx/sun3_82586.c index cae17f4bc93e..353f57f675d0 100644 --- a/drivers/net/ethernet/i825xx/sun3_82586.c +++ b/drivers/net/ethernet/i825xx/sun3_82586.c @@ -571,7 +571,7 @@ static int init586(struct net_device *dev) } #endif - ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */ + ptr = alloc_rfa(dev,ptr); /* init receive-frame-area */ /* * alloc xmit-buffs / init xmit_cmds @@ -584,7 +584,7 @@ static int init586(struct net_device *dev) ptr = (char *) ptr + XMIT_BUFF_SIZE; p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */ ptr = (char *) ptr + sizeof(struct tbd_struct); - if((void *)ptr > (void *)dev->mem_end) + if(ptr > (void *)dev->mem_end) { printk("%s: not enough shared-mem for your configuration!\n",dev->name); return 1; diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 04d901d0ff63..ec85bf1bffd1 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1894,7 +1894,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) goto out_free; } - rx_desc = (struct rx_desc *)rxq->rx_desc_area; + rx_desc = rxq->rx_desc_area; for (i = 0; i < rxq->rx_ring_size; i++) { int nexti; @@ -1999,7 +1999,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) txq->tx_desc_area_size = size; - tx_desc = (struct tx_desc *)txq->tx_desc_area; + tx_desc = txq->tx_desc_area; for (i = 0; i < txq->tx_ring_size; i++) { struct tx_desc *txd = tx_desc + i; int nexti; diff --git a/drivers/net/ethernet/marvell/pxa168_eth.c b/drivers/net/ethernet/marvell/pxa168_eth.c index 1db023b075a1..59489722e898 100644 --- a/drivers/net/ethernet/marvell/pxa168_eth.c +++ b/drivers/net/ethernet/marvell/pxa168_eth.c @@ -1032,7 +1032,7 @@ static int rxq_init(struct net_device *dev) } memset((void *)pep->p_rx_desc_area, 0, size); /* initialize the next_desc_ptr links in the Rx descriptors ring */ - p_rx_desc = (struct rx_desc *)pep->p_rx_desc_area; + p_rx_desc = pep->p_rx_desc_area; for (i = 0; i < rx_desc_num; i++) { p_rx_desc[i].next_desc_ptr = pep->rx_desc_dma + ((i + 1) % rx_desc_num) * sizeof(struct rx_desc); @@ -1095,7 +1095,7 @@ static int txq_init(struct net_device *dev) } memset((void *)pep->p_tx_desc_area, 0, pep->tx_desc_area_size); /* Initialize the next_desc_ptr links in the Tx descriptors ring */ - p_tx_desc = (struct tx_desc *)pep->p_tx_desc_area; + p_tx_desc = pep->p_tx_desc_area; for (i = 0; i < tx_desc_num; i++) { p_tx_desc[i].next_desc_ptr = pep->tx_desc_dma + ((i + 1) % tx_desc_num) * sizeof(struct tx_desc); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index b45d0e7f6ab0..766b8c5a235e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -779,7 +779,7 @@ static int qp_res_start_move_to(struct mlx4_dev *dev, int slave, int qpn, r->com.to_state = state; r->com.state = RES_QP_BUSY; if (qp) - *qp = (struct res_qp *)r; + *qp = r; } } @@ -832,7 +832,7 @@ static int mr_res_start_move_to(struct mlx4_dev *dev, int slave, int index, r->com.to_state = state; r->com.state = RES_MPT_BUSY; if (mpt) - *mpt = (struct res_mpt *)r; + *mpt = r; } } diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index bb367582c1e8..e7cd587d8ae7 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -6946,9 +6946,9 @@ static int rxd_owner_bit_reset(struct s2io_nic *sp) if (sp->rxd_mode == RXD_MODE_3B) ba = &ring->ba[j][k]; if (set_rxd_buffer_pointer(sp, rxdp, ba, &skb, - (u64 *)&temp0_64, - (u64 *)&temp1_64, - (u64 *)&temp2_64, + &temp0_64, + &temp1_64, + &temp2_64, size) == -ENOMEM) { return 0; } @@ -7149,7 +7149,7 @@ static int s2io_card_up(struct s2io_nic *sp) int i, ret = 0; struct config_param *config; struct mac_info *mac_control; - struct net_device *dev = (struct net_device *)sp->dev; + struct net_device *dev = sp->dev; u16 interruptible; /* Initialize the H/W I/O registers */ @@ -7325,7 +7325,7 @@ static void s2io_tx_watchdog(struct net_device *dev) static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) { struct s2io_nic *sp = ring_data->nic; - struct net_device *dev = (struct net_device *)ring_data->dev; + struct net_device *dev = ring_data->dev; struct sk_buff *skb = (struct sk_buff *) ((unsigned long)rxdp->Host_Control); int ring_no = ring_data->ring_no; @@ -7508,7 +7508,7 @@ aggregate: static void s2io_link(struct s2io_nic *sp, int link) { - struct net_device *dev = (struct net_device *)sp->dev; + struct net_device *dev = sp->dev; struct swStat *swstats = &sp->mac_control.stats_info->sw_stat; if (link != sp->last_link_state) { @@ -8280,7 +8280,7 @@ static int check_L2_lro_capable(u8 *buffer, struct iphdr **ip, return -1; } - *ip = (struct iphdr *)((u8 *)buffer + ip_off); + *ip = (struct iphdr *)(buffer + ip_off); ip_len = (u8)((*ip)->ihl); ip_len <<= 2; *tcp = (struct tcphdr *)((unsigned long)*ip + ip_len); diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c index 98e2c10ae08b..32d06824fe3e 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-config.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c @@ -2346,7 +2346,7 @@ void __vxge_hw_blockpool_blocks_add(struct __vxge_hw_blockpool *blockpool) for (i = 0; i < nreq; i++) vxge_os_dma_malloc_async( - ((struct __vxge_hw_device *)blockpool->hldev)->pdev, + (blockpool->hldev)->pdev, blockpool->hldev, VXGE_HW_BLOCK_SIZE); } @@ -2428,13 +2428,13 @@ __vxge_hw_blockpool_blocks_remove(struct __vxge_hw_blockpool *blockpool) break; pci_unmap_single( - ((struct __vxge_hw_device *)blockpool->hldev)->pdev, + (blockpool->hldev)->pdev, ((struct __vxge_hw_blockpool_entry *)p)->dma_addr, ((struct __vxge_hw_blockpool_entry *)p)->length, PCI_DMA_BIDIRECTIONAL); vxge_os_dma_free( - ((struct __vxge_hw_device *)blockpool->hldev)->pdev, + (blockpool->hldev)->pdev, ((struct __vxge_hw_blockpool_entry *)p)->memblock, &((struct __vxge_hw_blockpool_entry *)p)->acc_handle); @@ -4059,7 +4059,7 @@ __vxge_hw_vpath_sw_reset(struct __vxge_hw_device *hldev, u32 vp_id) enum vxge_hw_status status = VXGE_HW_OK; struct __vxge_hw_virtualpath *vpath; - vpath = (struct __vxge_hw_virtualpath *)&hldev->virtual_paths[vp_id]; + vpath = &hldev->virtual_paths[vp_id]; if (vpath->ringh) { status = __vxge_hw_ring_reset(vpath->ringh); diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.h b/drivers/net/ethernet/neterion/vxge/vxge-config.h index 5046a64f0fe8..9e0c1eed5dc5 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-config.h +++ b/drivers/net/ethernet/neterion/vxge/vxge-config.h @@ -1922,7 +1922,7 @@ realloc: /* misaligned, free current one and try allocating * size + VXGE_CACHE_LINE_SIZE memory */ - kfree((void *) vaddr); + kfree(vaddr); size += VXGE_CACHE_LINE_SIZE; realloc_flag = 1; goto realloc; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index 51387c31914b..2578eb1f025d 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -1134,7 +1134,7 @@ static void vxge_set_multicast(struct net_device *dev) "%s:%d", __func__, __LINE__); vdev = netdev_priv(dev); - hldev = (struct __vxge_hw_device *)vdev->devh; + hldev = vdev->devh; if (unlikely(!is_vxge_card_up(vdev))) return; @@ -3989,16 +3989,16 @@ static void __devinit vxge_print_parm(struct vxgedev *vdev, u64 vpath_mask) continue; vxge_debug_ll_config(VXGE_TRACE, "%s: MTU size - %d", vdev->ndev->name, - ((struct __vxge_hw_device *)(vdev->devh))-> + ((vdev->devh))-> config.vp_config[i].mtu); vxge_debug_init(VXGE_TRACE, "%s: VLAN tag stripping %s", vdev->ndev->name, - ((struct __vxge_hw_device *)(vdev->devh))-> + ((vdev->devh))-> config.vp_config[i].rpa_strip_vlan_tag ? "Enabled" : "Disabled"); vxge_debug_ll_config(VXGE_TRACE, "%s: Max frags : %d", vdev->ndev->name, - ((struct __vxge_hw_device *)(vdev->devh))-> + ((vdev->devh))-> config.vp_config[i].fifo.max_frags); break; } diff --git a/drivers/net/ethernet/neterion/vxge/vxge-traffic.c b/drivers/net/ethernet/neterion/vxge/vxge-traffic.c index 5954fa264da1..99749bd07d72 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-traffic.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-traffic.c @@ -533,8 +533,7 @@ __vxge_hw_device_handle_error(struct __vxge_hw_device *hldev, u32 vp_id, /* notify driver */ if (hldev->uld_callbacks->crit_err) - hldev->uld_callbacks->crit_err( - (struct __vxge_hw_device *)hldev, + hldev->uld_callbacks->crit_err(hldev, type, vp_id); out: @@ -1322,7 +1321,7 @@ enum vxge_hw_status vxge_hw_ring_rxd_next_completed( /* check whether it is not the end */ if (!own || *t_code == VXGE_HW_RING_T_CODE_FRM_DROP) { - vxge_assert(((struct vxge_hw_ring_rxd_1 *)rxdp)->host_control != + vxge_assert((rxdp)->host_control != 0); ++ring->cmpl_cnt; diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index ac149d99f78f..b5ba3084c7fc 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -583,7 +583,7 @@ static inline void ioc3_rx(struct net_device *dev) unsigned long *rxr; u32 w0, err; - rxr = (unsigned long *) ip->rxr; /* Ring base */ + rxr = ip->rxr; /* Ring base */ rx_entry = ip->rx_ci; /* RX consume index */ n_entry = ip->rx_pi; @@ -903,7 +903,7 @@ static void ioc3_alloc_rings(struct net_device *dev) if (ip->rxr == NULL) { /* Allocate and initialize rx ring. 4kb = 512 entries */ ip->rxr = (unsigned long *) get_zeroed_page(GFP_ATOMIC); - rxr = (unsigned long *) ip->rxr; + rxr = ip->rxr; if (!rxr) printk("ioc3_alloc_rings(): get_zeroed_page() failed!\n"); diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index fd33b21f6c96..1fcd914ec39b 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -1640,8 +1640,7 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_free_io_4; /* descriptors are aligned due to the nature of pci_alloc_consistent */ - pd->tx_ring = (struct smsc9420_dma_desc *) - (pd->rx_ring + RX_RING_SIZE); + pd->tx_ring = (pd->rx_ring + RX_RING_SIZE); pd->tx_dma_addr = pd->rx_dma_addr + sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 3dd8f0803808..df4774d5f6a7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -190,7 +190,7 @@ static int stmmac_pltfr_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - iounmap((void *)priv->ioaddr); + iounmap((void __force __iomem *)priv->ioaddr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index 7d4a040d84a2..aeded7ff1c8f 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -441,7 +441,7 @@ static void qe_rx(struct sunqe *qep) } else { skb_reserve(skb, 2); skb_put(skb, len); - skb_copy_to_linear_data(skb, (unsigned char *) this_qbuf, + skb_copy_to_linear_data(skb, this_qbuf, len); skb->protocol = eth_type_trans(skb, qep->dev); netif_rx(skb); diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index ea3e0a21ba74..a46c19859683 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -486,7 +486,7 @@ static void __devinit velocity_get_options(struct velocity_opt *opts, int index, velocity_set_bool_opt(&opts->flags, IP_byte_align[index], IP_ALIG_DEF, VELOCITY_FLAGS_IP_ALIGN, "IP_byte_align", devname); velocity_set_bool_opt(&opts->flags, ValPktLen[index], VAL_PKT_LEN_DEF, VELOCITY_FLAGS_VAL_PKT_LEN, "ValPktLen", devname); velocity_set_int_opt((int *) &opts->spd_dpx, speed_duplex[index], MED_LNK_MIN, MED_LNK_MAX, MED_LNK_DEF, "Media link mode", devname); - velocity_set_int_opt((int *) &opts->wol_opts, wol_opts[index], WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, "Wake On Lan options", devname); + velocity_set_int_opt(&opts->wol_opts, wol_opts[index], WOL_OPT_MIN, WOL_OPT_MAX, WOL_OPT_DEF, "Wake On Lan options", devname); opts->numrx = (opts->numrx & ~3); } -- cgit v1.2.3 From 2c208890c6d4e16076c6664137703ec813e8fa6c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 4 Jun 2012 12:44:17 +0000 Subject: wireless: Remove casts to same type Adding casts of objects to the same type is unnecessary and confusing for a human reader. For example, this cast: int y; int *p = (int *)&y; I used the coccinelle script below to find and remove these unnecessary casts. I manually removed the conversions this script produces of casts with __force, __iomem and __user. @@ type T; T *p; @@ - (T *)p + p Neatened the mwifiex_deauthenticate_infra function which was doing odd things with array pointers and not using is_zero_ether_addr. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/wireless/adm8211.c | 3 +- drivers/net/wireless/airo.c | 4 +- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 4 +- drivers/net/wireless/ath/ath9k/eeprom_4k.c | 3 +- drivers/net/wireless/ath/ath9k/eeprom_def.c | 3 +- drivers/net/wireless/ath/carl9170/cmd.c | 2 +- drivers/net/wireless/ath/carl9170/rx.c | 4 +- drivers/net/wireless/atmel.c | 4 +- drivers/net/wireless/b43legacy/dma.c | 2 +- drivers/net/wireless/b43legacy/xmit.c | 6 +-- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4 +- drivers/net/wireless/brcm80211/brcmsmac/dma.c | 2 +- drivers/net/wireless/brcm80211/brcmsmac/main.c | 2 +- drivers/net/wireless/hostap/hostap_proc.c | 3 +- drivers/net/wireless/ipw2x00/ipw2200.c | 4 +- drivers/net/wireless/iwlwifi/iwl-testmode.c | 2 +- drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 2 +- drivers/net/wireless/libertas/debugfs.c | 4 +- drivers/net/wireless/libertas/if_usb.c | 2 +- drivers/net/wireless/libertas_tf/if_usb.c | 2 +- drivers/net/wireless/mwifiex/11n.c | 14 +++---- drivers/net/wireless/mwifiex/11n.h | 3 +- drivers/net/wireless/mwifiex/11n_rxreorder.c | 18 +++------ drivers/net/wireless/mwifiex/cfg80211.c | 2 +- drivers/net/wireless/mwifiex/join.c | 20 ++++------ drivers/net/wireless/mwifiex/scan.c | 24 +++++------ drivers/net/wireless/mwifiex/sta_cmd.c | 16 +++----- drivers/net/wireless/mwifiex/sta_cmdresp.c | 24 +++++------ drivers/net/wireless/mwifiex/sta_event.c | 2 +- drivers/net/wireless/p54/eeprom.c | 2 +- drivers/net/wireless/p54/fwio.c | 2 +- drivers/net/wireless/prism54/islpci_eth.c | 2 +- drivers/net/wireless/ray_cs.c | 2 +- drivers/net/wireless/rtlwifi/base.c | 2 +- drivers/net/wireless/rtlwifi/cam.c | 2 +- drivers/net/wireless/rtlwifi/core.c | 14 +++---- drivers/net/wireless/rtlwifi/efuse.c | 4 +- drivers/net/wireless/rtlwifi/pci.c | 14 +++---- drivers/net/wireless/rtlwifi/ps.c | 10 ++--- drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c | 5 +-- drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c | 4 +- drivers/net/wireless/rtlwifi/rtl8192ce/hw.c | 43 ++++++++++---------- drivers/net/wireless/rtlwifi/rtl8192ce/trx.c | 4 +- drivers/net/wireless/rtlwifi/rtl8192cu/hw.c | 43 ++++++++++---------- drivers/net/wireless/rtlwifi/rtl8192cu/trx.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192de/dm.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192de/fw.c | 6 +-- drivers/net/wireless/rtlwifi/rtl8192de/hw.c | 34 ++++++++-------- drivers/net/wireless/rtlwifi/rtl8192de/trx.c | 6 +-- drivers/net/wireless/rtlwifi/rtl8192se/dm.c | 2 +- drivers/net/wireless/rtlwifi/rtl8192se/hw.c | 46 +++++++++++----------- drivers/net/wireless/rtlwifi/rtl8192se/trx.c | 2 +- drivers/net/wireless/ti/wlcore/tx.c | 3 +- drivers/net/wireless/zd1211rw/zd_chip.h | 2 +- drivers/net/wireless/zd1211rw/zd_usb.h | 2 +- 55 files changed, 198 insertions(+), 247 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 0ac09a2bd144..97afcec24759 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1738,8 +1738,7 @@ static int adm8211_alloc_rings(struct ieee80211_hw *dev) return -ENOMEM; } - priv->tx_ring = (struct adm8211_desc *)(priv->rx_ring + - priv->rx_ring_size); + priv->tx_ring = priv->rx_ring + priv->rx_ring_size; priv->tx_ring_dma = priv->rx_ring_dma + sizeof(struct adm8211_desc) * priv->rx_ring_size; diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 520a4b2eb9cc..252c2c2d76c1 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1997,7 +1997,7 @@ static int mpi_send_packet (struct net_device *dev) * ------------------------------------------------ */ - memcpy((char *)ai->txfids[0].virtual_host_addr, + memcpy(ai->txfids[0].virtual_host_addr, (char *)&wifictlhdr8023, sizeof(wifictlhdr8023)); payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr + @@ -4212,7 +4212,7 @@ static int PC4500_writerid(struct airo_info *ai, u16 rid, airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); rc = -1; } else { - memcpy((char *)ai->config_desc.virtual_host_addr, + memcpy(ai->config_desc.virtual_host_addr, pBuf, len); rc = issuecommand(ai, &cmd, &rsp); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index dfb0441f406c..ca881558da78 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3178,7 +3178,7 @@ static int ar9300_compress_decision(struct ath_hw *ah, mdata_size, length); return -1; } - memcpy(mptr, (u8 *) (word + COMP_HDR_LEN), length); + memcpy(mptr, word + COMP_HDR_LEN, length); ath_dbg(common, EEPROM, "restored eeprom %d: uncompressed, length %d\n", it, length); @@ -3199,7 +3199,7 @@ static int ar9300_compress_decision(struct ath_hw *ah, "restore eeprom %d: block, reference %d, length %d\n", it, reference, length); ar9300_uncompress_block(ah, mptr, mdata_size, - (u8 *) (word + COMP_HDR_LEN), length); + (word + COMP_HDR_LEN), length); break; default: ath_dbg(common, EEPROM, "unknown compression code %d\n", code); diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 4322ac80c203..92543d166fe9 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -188,8 +188,7 @@ static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) { #define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) struct ath_common *common = ath9k_hw_common(ah); - struct ar5416_eeprom_4k *eep = - (struct ar5416_eeprom_4k *) &ah->eeprom.map4k; + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; u16 *eepdata, temp, magic, magic2; u32 sum = 0, el; bool need_swap = false; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index b5fba8b18b8b..a9f071bc643a 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -264,8 +264,7 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) { - struct ar5416_eeprom_def *eep = - (struct ar5416_eeprom_def *) &ah->eeprom.def; + struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct ath_common *common = ath9k_hw_common(ah); u16 *eepdata, temp, magic, magic2; u32 sum = 0, el; diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c index 195dc6538110..39a63874b275 100644 --- a/drivers/net/wireless/ath/carl9170/cmd.c +++ b/drivers/net/wireless/ath/carl9170/cmd.c @@ -138,7 +138,7 @@ int carl9170_reboot(struct ar9170 *ar) if (!cmd) return -ENOMEM; - err = __carl9170_exec_cmd(ar, (struct carl9170_cmd *)cmd, true); + err = __carl9170_exec_cmd(ar, cmd, true); return err; } diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index 84b22eec7abd..7a8e90eaad83 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -161,7 +161,7 @@ static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) { - struct carl9170_rsp *cmd = (void *) buf; + struct carl9170_rsp *cmd = buf; struct ieee80211_vif *vif; if (carl9170_check_sequence(ar, cmd->hdr.seq)) @@ -520,7 +520,7 @@ static u8 *carl9170_find_ie(u8 *data, unsigned int len, u8 ie) */ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) { - struct ieee80211_hdr *hdr = (void *) data; + struct ieee80211_hdr *hdr = data; struct ieee80211_tim_ie *tim_ie; u8 *tim; u8 tim_len; diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index d07c0301da6a..4a4e98f71807 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -2952,10 +2952,10 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) /* current AP address - only in reassoc frame */ if (is_reassoc) { memcpy(body.ap, priv->CurrentBSSID, 6); - ssid_el_p = (u8 *)&body.ssid_el_id; + ssid_el_p = &body.ssid_el_id; bodysize = 18 + priv->SSID_size; } else { - ssid_el_p = (u8 *)&body.ap[0]; + ssid_el_p = &body.ap[0]; bodysize = 12 + priv->SSID_size; } diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index f1f8bd09bd87..ff50cb4290e4 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -52,7 +52,7 @@ struct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring, desc = ring->descbase; desc = &(desc[slot]); - return (struct b43legacy_dmadesc32 *)desc; + return desc; } static void op32_fill_descriptor(struct b43legacy_dmaring *ring, diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index a8012f2749ee..b8ffea6f5c64 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -269,8 +269,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) (&txhdr->plcp), plcp_fragment_len, rate); - b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) - (&txhdr->plcp_fb), plcp_fragment_len, + b43legacy_generate_plcp_hdr(&txhdr->plcp_fb, plcp_fragment_len, rate_fb->hw_value); /* PHY TX Control word */ @@ -340,8 +339,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) (&txhdr->rts_plcp), len, rts_rate); - b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) - (&txhdr->rts_plcp_fb), + b43legacy_generate_plcp_hdr(&txhdr->rts_plcp_fb, len, rts_rate_fb); hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame); txhdr->rts_dur_fb = hdr->duration_id; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1dbf2be478c8..4deae28fc211 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2502,7 +2502,7 @@ clkwait: int ret, i; ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, (u8 *) bus->ctrl_frame_buf, + SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, (u32) bus->ctrl_frame_len); if (ret < 0) { @@ -3327,7 +3327,7 @@ static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) len = brcmf_sdbrcm_get_image(memblock, MEMBLOCK, bus); if (len > 0 && len < MEMBLOCK) { - bufp = (char *)memblock; + bufp = memblock; bufp[len] = 0; len = brcmf_process_nvram_vars(bufp, len); bufp += len; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c index 11054ae9d4f6..7516639412ec 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -1433,7 +1433,7 @@ void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc) struct ieee80211_tx_info *tx_info; while (i != end) { - skb = (struct sk_buff *)di->txp[i]; + skb = di->txp[i]; if (skb != NULL) { tx_info = (struct ieee80211_tx_info *)skb->cb; (callback_fnc)(tx_info, arg_a); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 19db4052c44c..e67556780a31 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -8318,7 +8318,7 @@ brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, struct brcms_pub *pub; /* allocate struct brcms_c_info state and its substructures */ - wlc = (struct brcms_c_info *) brcms_c_attach_malloc(unit, &err, 0); + wlc = brcms_c_attach_malloc(unit, &err, 0); if (wlc == NULL) goto fail; wlc->wiphy = wl->wiphy; diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c index 75ef8f04aabe..dc447c1b5abe 100644 --- a/drivers/net/wireless/hostap/hostap_proc.c +++ b/drivers/net/wireless/hostap/hostap_proc.c @@ -58,8 +58,7 @@ static int prism2_stats_proc_read(char *page, char **start, off_t off, { char *p = page; local_info_t *local = (local_info_t *) data; - struct comm_tallies_sums *sums = (struct comm_tallies_sums *) - &local->comm_tallies; + struct comm_tallies_sums *sums = &local->comm_tallies; if (off != 0) { *eof = 1; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 0036737fe8e3..afdec4a0d9a0 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -7069,9 +7069,7 @@ static int ipw_qos_activate(struct ipw_priv *priv, } IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n"); - err = ipw_send_qos_params_command(priv, - (struct libipw_qos_parameters *) - &(qos_parameters[0])); + err = ipw_send_qos_params_command(priv, &qos_parameters[0]); if (err) IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n"); diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.c b/drivers/net/wireless/iwlwifi/iwl-testmode.c index 060aac3e22f1..492e64fb9f3d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-testmode.c +++ b/drivers/net/wireless/iwlwifi/iwl-testmode.c @@ -170,7 +170,7 @@ static void iwl_testmode_ucode_rx_pkt(struct iwl_priv *priv, void *data; int length; - data = (void *)rxb_addr(rxb); + data = rxb_addr(rxb); length = get_event_length(rxb); if (!data || length == 0) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index ec6fb395b84d..b849528d48dc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -1354,7 +1354,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, DMA_BIDIRECTIONAL); trace_iwlwifi_dev_tx(trans->dev, - &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], + &txq->tfds[txq->q.write_ptr], sizeof(struct iwl_tfd), &dev_cmd->hdr, firstlen, skb->data + hdr_len, secondlen); diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index a06cc283e23d..668dd27616a0 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -483,7 +483,7 @@ static ssize_t lbs_rdmac_write(struct file *file, res = -EFAULT; goto out_unlock; } - priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); + priv->mac_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); @@ -565,7 +565,7 @@ static ssize_t lbs_rdbbp_write(struct file *file, res = -EFAULT; goto out_unlock; } - priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); + priv->bbp_offset = simple_strtoul(buf, NULL, 16); res = count; out_unlock: free_page(addr); diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index cd3b0d400618..64b7dc5de126 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -302,7 +302,7 @@ error: static void if_usb_disconnect(struct usb_interface *intf) { struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbs_private *priv = (struct lbs_private *) cardp->priv; + struct lbs_private *priv = cardp->priv; lbs_deb_enter(LBS_DEB_MAIN); diff --git a/drivers/net/wireless/libertas_tf/if_usb.c b/drivers/net/wireless/libertas_tf/if_usb.c index 19a5a92dd779..d576dd6665d3 100644 --- a/drivers/net/wireless/libertas_tf/if_usb.c +++ b/drivers/net/wireless/libertas_tf/if_usb.c @@ -253,7 +253,7 @@ lbtf_deb_leave(LBTF_DEB_MAIN); static void if_usb_disconnect(struct usb_interface *intf) { struct if_usb_card *cardp = usb_get_intfdata(intf); - struct lbtf_private *priv = (struct lbtf_private *) cardp->priv; + struct lbtf_private *priv = cardp->priv; lbtf_deb_enter(LBTF_DEB_MAIN); diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index fe8ebfebcc0e..e535c937628b 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -101,8 +101,7 @@ int mwifiex_ret_11n_delba(struct mwifiex_private *priv, { int tid; struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; - struct host_cmd_ds_11n_delba *del_ba = - (struct host_cmd_ds_11n_delba *) &resp->params.del_ba; + struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); tid = del_ba_param_set >> DELBA_TID_POS; @@ -147,8 +146,7 @@ int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { int tid; - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = - (struct host_cmd_ds_11n_addba_rsp *) &resp->params.add_ba_rsp; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) @@ -412,7 +410,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, memcpy((u8 *) bss_co_2040 + sizeof(struct mwifiex_ie_types_header), - (u8 *) bss_desc->bcn_bss_co_2040 + + bss_desc->bcn_bss_co_2040 + sizeof(struct ieee_types_header), le16_to_cpu(bss_co_2040->header.len)); @@ -426,10 +424,8 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap)); - memcpy((u8 *) ext_cap + - sizeof(struct mwifiex_ie_types_header), - (u8 *) bss_desc->bcn_ext_cap + - sizeof(struct ieee_types_header), + memcpy((u8 *)ext_cap + sizeof(struct mwifiex_ie_types_header), + bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), le16_to_cpu(ext_cap->header.len)); *buffer += sizeof(struct mwifiex_ie_types_extcap); diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 77646d777dce..28366e9211fb 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -105,8 +105,7 @@ static inline u8 mwifiex_space_avail_for_new_ba_stream( priv = adapter->priv[i]; if (priv) ba_stream_num += mwifiex_wmm_list_len( - (struct list_head *) - &priv->tx_ba_stream_tbl_ptr); + &priv->tx_ba_stream_tbl_ptr); } return ((ba_stream_num < diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 9c44088054dd..89f7c570cd2e 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -296,9 +296,7 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, */ int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) { - struct host_cmd_ds_11n_addba_req *add_ba_req = - (struct host_cmd_ds_11n_addba_req *) - &cmd->params.add_ba_req; + struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); @@ -320,9 +318,7 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, struct host_cmd_ds_11n_addba_req *cmd_addba_req) { - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = - (struct host_cmd_ds_11n_addba_rsp *) - &cmd->params.add_ba_rsp; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; u8 tid; int win_size; uint16_t block_ack_param_set; @@ -367,8 +363,7 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, */ int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) { - struct host_cmd_ds_11n_delba *del_ba = (struct host_cmd_ds_11n_delba *) - &cmd->params.del_ba; + struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); @@ -398,8 +393,7 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, int start_win, end_win, win_size; u16 pkt_index; - tbl = mwifiex_11n_get_rx_reorder_tbl((struct mwifiex_private *) priv, - tid, ta); + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); if (!tbl) { if (pkt_type != PKT_TYPE_BAR) mwifiex_process_rx_packet(priv->adapter, payload); @@ -520,9 +514,7 @@ mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = - (struct host_cmd_ds_11n_addba_rsp *) - &resp->params.add_ba_rsp; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; int tid, win_size; struct mwifiex_rx_reorder_tbl *tbl; uint16_t block_ack_param_set; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 87671446e24b..42c1af943111 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1703,7 +1703,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wdev_priv = wiphy_priv(wiphy); *(unsigned long *)wdev_priv = (unsigned long)adapter; - set_wiphy_dev(wiphy, (struct device *)priv->adapter->dev); + set_wiphy_dev(wiphy, priv->adapter->dev); ret = wiphy_register(wiphy); if (ret < 0) { diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index d6b4fb04011f..82e63cee1e97 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -1349,22 +1349,16 @@ static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) { u8 mac_address[ETH_ALEN]; int ret; - u8 zero_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - if (mac) { - if (!memcmp(mac, zero_mac, sizeof(zero_mac))) - memcpy((u8 *) &mac_address, - (u8 *) &priv->curr_bss_params.bss_descriptor. - mac_address, ETH_ALEN); - else - memcpy((u8 *) &mac_address, (u8 *) mac, ETH_ALEN); - } else { - memcpy((u8 *) &mac_address, (u8 *) &priv->curr_bss_params. - bss_descriptor.mac_address, ETH_ALEN); - } + if (!mac || is_zero_ether_addr(mac)) + memcpy(mac_address, + priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + else + memcpy(mac_address, mac, ETH_ALEN); ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, - HostCmd_ACT_GEN_SET, 0, &mac_address); + HostCmd_ACT_GEN_SET, 0, mac_address); return ret; } diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 74f045715723..8fa763fa629a 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1014,14 +1014,12 @@ mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, case TLV_TYPE_TSFTIMESTAMP: dev_dbg(adapter->dev, "info: SCAN_RESP: TSF " "timestamp TLV, len = %d\n", tlv_len); - *tlv_data = (struct mwifiex_ie_types_data *) - current_tlv; + *tlv_data = current_tlv; break; case TLV_TYPE_CHANNELBANDLIST: dev_dbg(adapter->dev, "info: SCAN_RESP: channel" " band list TLV, len = %d\n", tlv_len); - *tlv_data = (struct mwifiex_ie_types_data *) - current_tlv; + *tlv_data = current_tlv; break; default: dev_err(adapter->dev, @@ -1226,15 +1224,15 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, bss_entry->beacon_buf); break; case WLAN_EID_BSS_COEX_2040: - bss_entry->bcn_bss_co_2040 = (u8 *) (current_ptr + - sizeof(struct ieee_types_header)); + bss_entry->bcn_bss_co_2040 = current_ptr + + sizeof(struct ieee_types_header); bss_entry->bss_co_2040_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - bss_entry->beacon_buf); break; case WLAN_EID_EXT_CAPABILITY: - bss_entry->bcn_ext_cap = (u8 *) (current_ptr + - sizeof(struct ieee_types_header)); + bss_entry->bcn_ext_cap = current_ptr + + sizeof(struct ieee_types_header); bss_entry->ext_cap_offset = (u16) (current_ptr + sizeof(struct ieee_types_header) - bss_entry->beacon_buf); @@ -1683,8 +1681,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, goto done; } if (element_id == WLAN_EID_DS_PARAMS) { - channel = *(u8 *) (current_ptr + - sizeof(struct ieee_types_header)); + channel = *(current_ptr + sizeof(struct ieee_types_header)); break; } @@ -2010,12 +2007,11 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv) if (curr_bss->bcn_bss_co_2040) curr_bss->bcn_bss_co_2040 = - (u8 *) (curr_bss->beacon_buf + - curr_bss->bss_co_2040_offset); + (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); if (curr_bss->bcn_ext_cap) - curr_bss->bcn_ext_cap = (u8 *) (curr_bss->beacon_buf + - curr_bss->ext_cap_offset); + curr_bss->bcn_ext_cap = curr_bss->beacon_buf + + curr_bss->ext_cap_offset; } /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 40e025da6bc2..1ff1362d8cdf 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -793,8 +793,7 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, struct host_cmd_ds_mac_reg_access *mac_reg; cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); - mac_reg = (struct host_cmd_ds_mac_reg_access *) &cmd-> - params.mac_reg; + mac_reg = &cmd->params.mac_reg; mac_reg->action = cpu_to_le16(cmd_action); mac_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); @@ -806,8 +805,7 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, struct host_cmd_ds_bbp_reg_access *bbp_reg; cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); - bbp_reg = (struct host_cmd_ds_bbp_reg_access *) - &cmd->params.bbp_reg; + bbp_reg = &cmd->params.bbp_reg; bbp_reg->action = cpu_to_le16(cmd_action); bbp_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); @@ -819,8 +817,7 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, struct host_cmd_ds_rf_reg_access *rf_reg; cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); - rf_reg = (struct host_cmd_ds_rf_reg_access *) - &cmd->params.rf_reg; + rf_reg = &cmd->params.rf_reg; rf_reg->action = cpu_to_le16(cmd_action); rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); rf_reg->value = (u8) le32_to_cpu(reg_rw->value); @@ -831,8 +828,7 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, struct host_cmd_ds_pmic_reg_access *pmic_reg; cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); - pmic_reg = (struct host_cmd_ds_pmic_reg_access *) &cmd-> - params.pmic_reg; + pmic_reg = &cmd->params.pmic_reg; pmic_reg->action = cpu_to_le16(cmd_action); pmic_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); @@ -844,8 +840,7 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, struct host_cmd_ds_rf_reg_access *cau_reg; cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); - cau_reg = (struct host_cmd_ds_rf_reg_access *) - &cmd->params.rf_reg; + cau_reg = &cmd->params.rf_reg; cau_reg->action = cpu_to_le16(cmd_action); cau_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); @@ -856,7 +851,6 @@ static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, { struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf; struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = - (struct host_cmd_ds_802_11_eeprom_access *) &cmd->params.eeprom; cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index a79ed9bd9695..bd40541ebd5a 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -227,7 +227,7 @@ static int mwifiex_ret_get_log(struct mwifiex_private *priv, struct mwifiex_ds_get_stats *stats) { struct host_cmd_ds_802_11_get_log *get_log = - (struct host_cmd_ds_802_11_get_log *) &resp->params.get_log; + &resp->params.get_log; if (stats) { stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); @@ -282,7 +282,7 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, u32 i; int ret = 0; - tlv_buf = (u8 *) ((u8 *) rate_cfg) + + tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16)); @@ -679,39 +679,33 @@ static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, eeprom = data_buf; switch (type) { case HostCmd_CMD_MAC_REG_ACCESS: - r.mac = (struct host_cmd_ds_mac_reg_access *) - &resp->params.mac_reg; + r.mac = &resp->params.mac_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset)); reg_rw->value = r.mac->value; break; case HostCmd_CMD_BBP_REG_ACCESS: - r.bbp = (struct host_cmd_ds_bbp_reg_access *) - &resp->params.bbp_reg; + r.bbp = &resp->params.bbp_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset)); reg_rw->value = cpu_to_le32((u32) r.bbp->value); break; case HostCmd_CMD_RF_REG_ACCESS: - r.rf = (struct host_cmd_ds_rf_reg_access *) - &resp->params.rf_reg; + r.rf = &resp->params.rf_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); reg_rw->value = cpu_to_le32((u32) r.bbp->value); break; case HostCmd_CMD_PMIC_REG_ACCESS: - r.pmic = (struct host_cmd_ds_pmic_reg_access *) - &resp->params.pmic_reg; + r.pmic = &resp->params.pmic_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset)); reg_rw->value = cpu_to_le32((u32) r.pmic->value); break; case HostCmd_CMD_CAU_REG_ACCESS: - r.rf = (struct host_cmd_ds_rf_reg_access *) - &resp->params.rf_reg; + r.rf = &resp->params.rf_reg; reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); reg_rw->value = cpu_to_le32((u32) r.rf->value); break; case HostCmd_CMD_802_11_EEPROM_ACCESS: - r.eeprom = (struct host_cmd_ds_802_11_eeprom_access *) - &resp->params.eeprom; + r.eeprom = &resp->params.eeprom; pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count); if (le16_to_cpu(eeprom->byte_count) < le16_to_cpu(r.eeprom->byte_count)) { @@ -787,7 +781,7 @@ static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, struct mwifiex_ds_misc_subsc_evt *sub_event) { struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = - (struct host_cmd_ds_802_11_subsc_evt *)&resp->params.subsc_evt; + &resp->params.subsc_evt; /* For every subscribe event command (Get/Set/Clear), FW reports the * current set of subscribed events*/ diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 4ace5a3dcd23..e8b27c305367 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -422,7 +422,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) if (len != -1) { sinfo.filled = STATION_INFO_ASSOC_REQ_IES; - sinfo.assoc_req_ies = (u8 *)&event->data[len]; + sinfo.assoc_req_ies = &event->data[len]; len = (u8 *)sinfo.assoc_req_ies - (u8 *)&event->frame_control; sinfo.assoc_req_ies_len = diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index fa8ce5104781..636daf2860cc 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -905,7 +905,7 @@ int p54_read_eeprom(struct ieee80211_hw *dev) while (eeprom_size) { blocksize = min(eeprom_size, maxblocksize); - ret = p54_download_eeprom(priv, (void *) (eeprom + offset), + ret = p54_download_eeprom(priv, eeprom + offset, offset, blocksize); if (unlikely(ret)) goto free; diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c index 18e82b31afa6..9ba85106eec0 100644 --- a/drivers/net/wireless/p54/fwio.c +++ b/drivers/net/wireless/p54/fwio.c @@ -478,7 +478,7 @@ int p54_scan(struct p54_common *priv, u16 mode, u16 dwell) if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { memcpy(&body->longbow.curve_data, - (void *) entry + sizeof(__le16), + entry + sizeof(__le16), priv->curve_data->entry_size); } else { struct p54_scan_body *chan = &body->normal; diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index 266d45bf86f5..799e148d0370 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -455,7 +455,7 @@ islpci_eth_receive(islpci_private *priv) "Error mapping DMA address\n"); /* free the skbuf structure before aborting */ - dev_kfree_skb_irq((struct sk_buff *) skb); + dev_kfree_skb_irq(skb); skb = NULL; break; } diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 86a738bf591c..598ca1cafb95 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -1849,7 +1849,7 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id) pr_debug("ray_cs: interrupt for *dev=%p\n", dev); local = netdev_priv(dev); - link = (struct pcmcia_device *)local->finder; + link = local->finder; if (!pcmcia_dev_present(link)) { pr_debug( "ray_cs interrupt from device not present or suspended.\n"); diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index f4c852c6749b..58e1f7bb4df1 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -907,7 +907,7 @@ bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) struct ieee80211_hdr *hdr = rtl_get_hdr(skb); struct rtl_priv *rtlpriv = rtl_priv(hw); __le16 fc = hdr->frame_control; - u8 *act = (u8 *) (((u8 *) skb->data + MAC80211_3ADDR_LEN)); + u8 *act = (u8 *)skb->data + MAC80211_3ADDR_LEN; u8 category; if (!ieee80211_is_action(fc)) diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c index 3d8cc4a0c86d..2d1a8220d5c0 100644 --- a/drivers/net/wireless/rtlwifi/cam.c +++ b/drivers/net/wireless/rtlwifi/cam.c @@ -146,7 +146,7 @@ u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, } rtl_cam_program_entry(hw, ul_entry_idx, mac_addr, - (u8 *) key_content, us_config); + key_content, us_config); RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "<===\n"); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 278e9f957e0d..a18ad2a98938 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -680,7 +680,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, mac->short_preamble = bss_conf->use_short_preamble; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACK_PREAMBLE, - (u8 *) (&mac->short_preamble)); + &mac->short_preamble); } if (changed & BSS_CHANGED_ERP_SLOT) { @@ -693,7 +693,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, mac->slot_time = RTL_SLOT_TIME_20; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *) (&mac->slot_time)); + &mac->slot_time); } if (changed & BSS_CHANGED_HT) { @@ -713,7 +713,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, rcu_read_unlock(); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SHORTGI_DENSITY, - (u8 *) (&mac->max_mss_density)); + &mac->max_mss_density); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AMPDU_FACTOR, &mac->current_ampdu_factor); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AMPDU_MIN_SPACE, @@ -801,7 +801,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, u8 mstatus = RT_MEDIA_CONNECT; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_JOINBSSRPT, - (u8 *) (&mstatus)); + &mstatus); ppsc->report_linked = true; } } else { @@ -809,7 +809,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, u8 mstatus = RT_MEDIA_DISCONNECT; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_JOINBSSRPT, - (u8 *)(&mstatus)); + &mstatus); ppsc->report_linked = false; } } @@ -836,7 +836,7 @@ static void rtl_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 bibss = (mac->opmode == NL80211_IFTYPE_ADHOC) ? 1 : 0; mac->tsf = tsf; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_CORRECT_TSF, (u8 *) (&bibss)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_CORRECT_TSF, &bibss); } static void rtl_op_reset_tsf(struct ieee80211_hw *hw, @@ -845,7 +845,7 @@ static void rtl_op_reset_tsf(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp = 0; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_DUAL_TSF_RST, (u8 *) (&tmp)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_DUAL_TSF_RST, &tmp); } static void rtl_op_sta_notify(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c index 1f143800a8d7..8e2f9afb125a 100644 --- a/drivers/net/wireless/rtlwifi/efuse.c +++ b/drivers/net/wireless/rtlwifi/efuse.c @@ -352,7 +352,7 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_EFUSE_BYTES, (u8 *)&efuse_utilized); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_EFUSE_USAGE, - (u8 *)&efuse_usage); + &efuse_usage); done: for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) kfree(efuse_word[i]); @@ -409,7 +409,7 @@ void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, else if (type == 2) efuse_shadow_read_2byte(hw, offset, (u16 *) value); else if (type == 4) - efuse_shadow_read_4byte(hw, offset, (u32 *) value); + efuse_shadow_read_4byte(hw, offset, value); } diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 2062ea1d7c80..82d3afcfecd1 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -756,10 +756,10 @@ done: if (index == rtlpci->rxringcount - 1) rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXERO, - (u8 *)&tmp_one); + &tmp_one); rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXOWN, - (u8 *)&tmp_one); + &tmp_one); index = (index + 1) % rtlpci->rxringcount; } @@ -934,7 +934,7 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) __skb_queue_tail(&ring->queue, pskb); rtlpriv->cfg->ops->set_desc((u8 *) pdesc, true, HW_DESC_OWN, - (u8 *)&temp_one); + &temp_one); return; } @@ -1126,11 +1126,11 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw) rxbuffersize); rtlpriv->cfg->ops->set_desc((u8 *) entry, false, HW_DESC_RXOWN, - (u8 *)&tmp_one); + &tmp_one); } rtlpriv->cfg->ops->set_desc((u8 *) entry, false, - HW_DESC_RXERO, (u8 *)&tmp_one); + HW_DESC_RXERO, &tmp_one); } return 0; } @@ -1263,7 +1263,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) rtlpriv->cfg->ops->set_desc((u8 *) entry, false, HW_DESC_RXOWN, - (u8 *)&tmp_one); + &tmp_one); } rtlpci->rx_ring[rx_queue_idx].idx = 0; } @@ -1422,7 +1422,7 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, struct sk_buff *skb, __skb_queue_tail(&ring->queue, skb); rtlpriv->cfg->ops->set_desc((u8 *)pdesc, true, - HW_DESC_OWN, (u8 *)&temp_one); + HW_DESC_OWN, &temp_one); if ((ring->entries - skb_queue_len(&ring->queue)) < 2 && diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c index 5ae26647f340..13ad33e85577 100644 --- a/drivers/net/wireless/rtlwifi/ps.c +++ b/drivers/net/wireless/rtlwifi/ps.c @@ -333,10 +333,10 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) rpwm_val = 0x0C; /* RF on */ fw_pwrmode = FW_PS_ACTIVE_MODE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *) (&rpwm_val)); + &rpwm_val); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *) (&fw_pwrmode)); + &fw_pwrmode); fw_current_inps = false; rtlpriv->cfg->ops->set_hw_reg(hw, @@ -356,11 +356,11 @@ static void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode) (u8 *) (&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *) (&ppsc->fwctrl_psmode)); + &ppsc->fwctrl_psmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *) (&rpwm_val)); + &rpwm_val); } else { /* Reset the power save related parameters. */ ppsc->dot11_psmode = EACTIVE; @@ -446,7 +446,7 @@ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - struct ieee80211_hdr *hdr = (void *) data; + struct ieee80211_hdr *hdr = data; struct ieee80211_tim_ie *tim_ie; u8 *tim; u8 tim_len; diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index f7f48c7ac854..a45afda8259c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -656,9 +656,8 @@ static void rtl92c_dm_check_edca_turbo(struct ieee80211_hw *hw) } else { if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_AC_PARAM, - (u8 *) (&tmp)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, + &tmp); rtlpriv->dm.current_turbo_edca = false; } } diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c index 692c8ef5ee89..44febfde9493 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c @@ -168,7 +168,7 @@ static void _rtl92c_write_fw(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - u8 *bufferPtr = (u8 *) buffer; + u8 *bufferPtr = buffer; RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes\n", size); @@ -262,7 +262,7 @@ int rtl92c_download_fw(struct ieee80211_hw *hw) return 1; pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware; - pfwdata = (u8 *) rtlhal->pfirmware; + pfwdata = rtlhal->pfirmware; fwsize = rtlhal->fwsize; if (IS_FW_HEADER_EXIST(pfwheader)) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index 5c4d9bc040f1..bd0da7ef290b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -214,13 +214,13 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) for (e_aci = 0; e_aci < AC_MAX; e_aci++) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *) (&e_aci)); + &e_aci); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; - u8 short_preamble = (bool) (*(u8 *) val); + u8 short_preamble = (bool)*val; reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) reg_tmp |= 0x80; @@ -232,7 +232,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 min_spacing_to_set; u8 sec_min_space; - min_spacing_to_set = *((u8 *) val); + min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { sec_min_space = 0; @@ -257,7 +257,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; - density_to_set = *((u8 *) val); + density_to_set = *val; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, @@ -284,7 +284,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) else p_regtoset = regtoset_normal; - factor_toset = *((u8 *) val); + factor_toset = *(val); if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) @@ -316,17 +316,17 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_AC_PARAM:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *(val); rtl92c_dm_init_edca_turbo(hw); if (rtlpci->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, - (u8 *) (&e_aci)); + (&e_aci)); break; } case HW_VAR_ACM_CTRL:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *(val); union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; @@ -382,7 +382,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_RETRY_LIMIT:{ - u8 retry_limit = ((u8 *) (val))[0]; + u8 retry_limit = val[0]; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | @@ -396,13 +396,13 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: - rtlefuse->efuse_usedpercentage = *((u8 *) val); + rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl92c_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: - rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val)); + rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM:{ u8 rpwm_val; @@ -411,31 +411,30 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) udelay(1); if (rpwm_val & BIT(7)) { - rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, - (*(u8 *) val)); + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val); } else { rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, - ((*(u8 *) val) | BIT(7))); + *val | BIT(7)); } break; } case HW_VAR_H2C_FW_PWRMODE:{ - u8 psmode = (*(u8 *) val); + u8 psmode = *val; if ((psmode != FW_PS_ACTIVE_MODE) && (!IS_92C_SERIAL(rtlhal->version))) { rtl92c_dm_rf_saving(hw, true); } - rtl92c_set_fw_pwrmode_cmd(hw, (*(u8 *) val)); + rtl92c_set_fw_pwrmode_cmd(hw, *val); break; } case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT:{ - u8 mstatus = (*(u8 *) val); + u8 mstatus = *val; u8 tmp_regcr, tmp_reg422; bool recover = false; @@ -472,7 +471,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr & ~(BIT(0)))); } - rtl92c_set_fw_joinbss_report_cmd(hw, (*(u8 *) val)); + rtl92c_set_fw_joinbss_report_cmd(hw, *val); break; } @@ -486,7 +485,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_CORRECT_TSF:{ - u8 btype_ibss = ((u8 *) (val))[0]; + u8 btype_ibss = val[0]; if (btype_ibss) _rtl92ce_stop_tx_beacon(hw); @@ -1589,10 +1588,10 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw) rtlefuse->autoload_failflag, hwinfo); - rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_channelplan = *&hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; rtlefuse->txpwr_fromeprom = true; - rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID]; + rtlefuse->eeprom_oemid = *&hwinfo[EEPROM_CUSTOMER_ID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); @@ -1939,7 +1938,7 @@ void rtl92ce_update_channel_access_setting(struct ieee80211_hw *hw) u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *)&mac->slot_time); + &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 3af874e69595..52166640f167 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -605,7 +605,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; struct ieee80211_sta *sta; - u8 *pdesc = (u8 *) pdesc_tx; + u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; u8 fw_qsel = _rtl92ce_map_hwqueue_to_fwqueue(skb, hw_queue); @@ -806,7 +806,7 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, SET_TX_DESC_OWN(pdesc, 1); - SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len)); + SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len)); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index 0c74d4f2eeb4..4bbb711a36c5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -381,11 +381,11 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw) rtlefuse->eeprom_did = le16_to_cpu(*(__le16 *)&hwinfo[EEPROM_DID]); RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, " VID = 0x%02x PID = 0x%02x\n", rtlefuse->eeprom_vid, rtlefuse->eeprom_did); - rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->eeprom_version = le16_to_cpu(*(__le16 *)&hwinfo[EEPROM_VERSION]); rtlefuse->txpwr_fromeprom = true; - rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID]; + rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); if (rtlhal->oem_id == RT_CID_DEFAULT) { @@ -1660,7 +1660,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) for (e_aci = 0; e_aci < AC_MAX; e_aci++) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *)(&e_aci)); + &e_aci); } else { u8 sifstime = 0; u8 u1bAIFS; @@ -1685,7 +1685,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; - u8 short_preamble = (bool) (*(u8 *) val); + u8 short_preamble = (bool)*val; reg_tmp = 0; if (short_preamble) reg_tmp |= 0x80; @@ -1696,7 +1696,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 min_spacing_to_set; u8 sec_min_space; - min_spacing_to_set = *((u8 *) val); + min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { switch (rtlpriv->sec.pairwise_enc_algorithm) { case NO_ENCRYPTION: @@ -1729,7 +1729,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; - density_to_set = *((u8 *) val); + density_to_set = *val; density_to_set &= 0x1f; mac->min_space_cfg &= 0x07; mac->min_space_cfg |= (density_to_set << 3); @@ -1747,7 +1747,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 index = 0; p_regtoset = regtoset_normal; - factor_toset = *((u8 *) val); + factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) @@ -1774,7 +1774,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_AC_PARAM:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; u32 u4b_ac_param; u16 cw_min = le16_to_cpu(mac->ac[e_aci].cw_min); u16 cw_max = le16_to_cpu(mac->ac[e_aci].cw_max); @@ -1814,11 +1814,11 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) } if (rtlusb->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_ACM_CTRL, (u8 *)(&e_aci)); + HW_VAR_ACM_CTRL, &e_aci); break; } case HW_VAR_ACM_CTRL:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *) (&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; @@ -1874,7 +1874,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_RETRY_LIMIT:{ - u8 retry_limit = ((u8 *) (val))[0]; + u8 retry_limit = val[0]; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | @@ -1891,39 +1891,38 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: - rtlefuse->efuse_usedpercentage = *((u8 *) val); + rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl92c_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: - rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val)); + rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM:{ u8 rpwm_val = rtl_read_byte(rtlpriv, REG_USB_HRPWM); if (rpwm_val & BIT(7)) - rtl_write_byte(rtlpriv, REG_USB_HRPWM, - (*(u8 *)val)); + rtl_write_byte(rtlpriv, REG_USB_HRPWM, *val); else rtl_write_byte(rtlpriv, REG_USB_HRPWM, - ((*(u8 *)val) | BIT(7))); + *val | BIT(7)); break; } case HW_VAR_H2C_FW_PWRMODE:{ - u8 psmode = (*(u8 *) val); + u8 psmode = *val; if ((psmode != FW_PS_ACTIVE_MODE) && (!IS_92C_SERIAL(rtlhal->version))) rtl92c_dm_rf_saving(hw, true); - rtl92c_set_fw_pwrmode_cmd(hw, (*(u8 *) val)); + rtl92c_set_fw_pwrmode_cmd(hw, (*val)); break; } case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT:{ - u8 mstatus = (*(u8 *) val); + u8 mstatus = *val; u8 tmp_reg422; bool recover = false; @@ -1948,7 +1947,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) tmp_reg422 | BIT(6)); rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); } - rtl92c_set_fw_joinbss_report_cmd(hw, (*(u8 *) val)); + rtl92c_set_fw_joinbss_report_cmd(hw, (*val)); break; } case HW_VAR_AID:{ @@ -1961,7 +1960,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_CORRECT_TSF:{ - u8 btype_ibss = ((u8 *) (val))[0]; + u8 btype_ibss = val[0]; if (btype_ibss) _rtl92cu_stop_tx_beacon(hw); @@ -2184,7 +2183,7 @@ void rtl92cu_update_channel_access_setting(struct ieee80211_hw *hw) u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *)&mac->slot_time); + &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 21bc827c5fa6..2e6eb356a93e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -668,7 +668,7 @@ void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, SET_TX_DESC_RATE_ID(pdesc, 7); SET_TX_DESC_MACID(pdesc, 0); SET_TX_DESC_OWN(pdesc, 1); - SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len)); + SET_TX_DESC_PKT_SIZE(pdesc, (u16)skb->len); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, 0x20); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index a7d63a84551a..c0201ed69dd7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -696,7 +696,7 @@ static void rtl92d_dm_check_edca_turbo(struct ieee80211_hw *hw) if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *) (&tmp)); + &tmp); rtlpriv->dm.current_turbo_edca = false; } } diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c index f548a8d0068d..895ae6c1f354 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c @@ -120,7 +120,7 @@ static void _rtl92d_write_fw(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - u8 *bufferPtr = (u8 *) buffer; + u8 *bufferPtr = buffer; u32 pagenums, remainSize; u32 page, offset; @@ -256,8 +256,8 @@ int rtl92d_download_fw(struct ieee80211_hw *hw) if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware) return 1; fwsize = rtlhal->fwsize; - pfwheader = (u8 *) rtlhal->pfirmware; - pfwdata = (u8 *) rtlhal->pfirmware; + pfwheader = rtlhal->pfirmware; + pfwdata = rtlhal->pfirmware; rtlhal->fw_version = (u16) GET_FIRMWARE_HDR_VERSION(pfwheader); rtlhal->fw_subversion = (u16) GET_FIRMWARE_HDR_SUB_VER(pfwheader); RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index b338d526c422..f4051f4f0390 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -235,12 +235,12 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) for (e_aci = 0; e_aci < AC_MAX; e_aci++) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *) (&e_aci)); + (&e_aci)); break; } case HW_VAR_ACK_PREAMBLE: { u8 reg_tmp; - u8 short_preamble = (bool) (*(u8 *) val); + u8 short_preamble = (bool) (*val); reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) @@ -252,7 +252,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 min_spacing_to_set; u8 sec_min_space; - min_spacing_to_set = *((u8 *) val); + min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { sec_min_space = 0; if (min_spacing_to_set < sec_min_space) @@ -271,7 +271,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_SHORTGI_DENSITY: { u8 density_to_set; - density_to_set = *((u8 *) val); + density_to_set = *val; mac->min_space_cfg = rtlpriv->rtlhal.minspace_cfg; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, @@ -293,7 +293,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) regtoSet = 0x66626641; else regtoSet = 0xb972a841; - factor_toset = *((u8 *) val); + factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) @@ -316,15 +316,15 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_AC_PARAM: { - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; rtl92d_dm_init_edca_turbo(hw); if (rtlpci->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, - (u8 *) (&e_aci)); + &e_aci); break; } case HW_VAR_ACM_CTRL: { - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; @@ -376,7 +376,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlpci->receive_config = ((u32 *) (val))[0]; break; case HW_VAR_RETRY_LIMIT: { - u8 retry_limit = ((u8 *) (val))[0]; + u8 retry_limit = val[0]; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | @@ -390,16 +390,16 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: - rtlefuse->efuse_usedpercentage = *((u8 *) val); + rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl92d_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: - rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val)); + rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM: - rtl92d_fill_h2c_cmd(hw, H2C_PWRM, 1, (u8 *) (val)); + rtl92d_fill_h2c_cmd(hw, H2C_PWRM, 1, (val)); break; case HW_VAR_H2C_FW_PWRMODE: break; @@ -407,7 +407,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT: { - u8 mstatus = (*(u8 *) val); + u8 mstatus = (*val); u8 tmp_regcr, tmp_reg422; bool recover = false; @@ -435,7 +435,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr & ~(BIT(0)))); } - rtl92d_set_fw_joinbss_report_cmd(hw, (*(u8 *) val)); + rtl92d_set_fw_joinbss_report_cmd(hw, (*val)); break; } case HW_VAR_AID: { @@ -447,7 +447,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_CORRECT_TSF: { - u8 btype_ibss = ((u8 *) (val))[0]; + u8 btype_ibss = val[0]; if (btype_ibss) _rtl92de_stop_tx_beacon(hw); @@ -1794,7 +1794,7 @@ static void _rtl92de_read_adapter_info(struct ieee80211_hw *hw) "RTL819X Not boot from eeprom, check it !!\n"); return; } - rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID]; + rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; _rtl92de_read_macphymode_and_bandtype(hw, hwinfo); /* VID, DID SE 0xA-D */ @@ -2115,7 +2115,7 @@ void rtl92de_update_channel_access_setting(struct ieee80211_hw *hw) u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *)&mac->slot_time); + &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 1666ef7fd87b..f80690d82c11 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -560,7 +560,7 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_hal *rtlhal = rtl_hal(rtlpriv); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); struct ieee80211_sta *sta = info->control.sta; - u8 *pdesc = (u8 *) pdesc_tx; + u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; unsigned int buf_len = 0; @@ -761,11 +761,11 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); - SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) (skb->len)); + SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)skb->len); SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); SET_TX_DESC_RATE_ID(pdesc, 7); SET_TX_DESC_MACID(pdesc, 0); - SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len)); + SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len)); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); SET_TX_DESC_OFFSET(pdesc, 0x20); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c index 2e1158026fb7..465f58157101 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c @@ -146,7 +146,7 @@ static void _rtl92s_dm_check_edca_turbo(struct ieee80211_hw *hw) if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *)(&tmp)); + &tmp); rtlpriv->dm.current_turbo_edca = false; } } diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index b141c35bf926..4542e6952b97 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -145,13 +145,13 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) for (e_aci = 0; e_aci < AC_MAX; e_aci++) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *)(&e_aci)); + (&e_aci)); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; - u8 short_preamble = (bool) (*(u8 *) val); + u8 short_preamble = (bool) (*val); reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) reg_tmp |= 0x80; @@ -163,7 +163,7 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 min_spacing_to_set; u8 sec_min_space; - min_spacing_to_set = *((u8 *)val); + min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { if (rtlpriv->sec.pairwise_enc_algorithm == NO_ENCRYPTION) @@ -194,7 +194,7 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; - density_to_set = *((u8 *) val); + density_to_set = *val; mac->min_space_cfg = rtlpriv->rtlhal.minspace_cfg; mac->min_space_cfg |= (density_to_set << 3); @@ -216,7 +216,7 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) 15, 15, 15, 15, 0}; u8 index = 0; - factor_toset = *((u8 *) val); + factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) @@ -248,17 +248,17 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_AC_PARAM:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; rtl92s_dm_init_edca_turbo(hw); if (rtlpci->acm_method != eAcmWay2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, - (u8 *)(&e_aci)); + &e_aci); break; } case HW_VAR_ACM_CTRL:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&( mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; @@ -313,7 +313,7 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_RETRY_LIMIT:{ - u8 retry_limit = ((u8 *) (val))[0]; + u8 retry_limit = val[0]; rtl_write_word(rtlpriv, RETRY_LIMIT, retry_limit << RETRY_LIMIT_SHORT_SHIFT | @@ -328,14 +328,14 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_EFUSE_USAGE: { - rtlefuse->efuse_usedpercentage = *((u8 *) val); + rtlefuse->efuse_usedpercentage = *val; break; } case HW_VAR_IO_CMD: { break; } case HW_VAR_WPA_CONFIG: { - rtl_write_byte(rtlpriv, REG_SECR, *((u8 *) val)); + rtl_write_byte(rtlpriv, REG_SECR, *val); break; } case HW_VAR_SET_RPWM:{ @@ -1813,8 +1813,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) else index = 2; - tempval = (*(u8 *)&hwinfo[EEPROM_TX_PWR_HT20_DIFF + - index]) & 0xff; + tempval = hwinfo[EEPROM_TX_PWR_HT20_DIFF + index] & 0xff; rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); @@ -1830,14 +1829,13 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) else index = 1; - tempval = (*(u8 *)&hwinfo[EEPROM_TX_PWR_OFDM_DIFF + index]) - & 0xff; + tempval = hwinfo[EEPROM_TX_PWR_OFDM_DIFF + index] & 0xff; rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i] = (tempval & 0xF); rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i] = ((tempval >> 4) & 0xF); - tempval = (*(u8 *)&hwinfo[TX_PWR_SAFETY_CHK]); + tempval = hwinfo[TX_PWR_SAFETY_CHK]; rtlefuse->txpwr_safetyflag = (tempval & 0x01); } @@ -1876,7 +1874,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) /* Read RF-indication and Tx Power gain * index diff of legacy to HT OFDM rate. */ - tempval = (*(u8 *)&hwinfo[EEPROM_RFIND_POWERDIFF]) & 0xff; + tempval = hwinfo[EEPROM_RFIND_POWERDIFF] & 0xff; rtlefuse->eeprom_txpowerdiff = tempval; rtlefuse->legacy_httxpowerdiff = rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][0]; @@ -1887,7 +1885,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) /* Get TSSI value for each path. */ usvalue = *(u16 *)&hwinfo[EEPROM_TSSI_A]; rtlefuse->eeprom_tssi[RF90_PATH_A] = (u8)((usvalue & 0xff00) >> 8); - usvalue = *(u8 *)&hwinfo[EEPROM_TSSI_B]; + usvalue = hwinfo[EEPROM_TSSI_B]; rtlefuse->eeprom_tssi[RF90_PATH_B] = (u8)(usvalue & 0xff); RTPRINT(rtlpriv, FINIT, INIT_TxPower, "TSSI_A = 0x%x, TSSI_B = 0x%x\n", @@ -1896,7 +1894,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) /* Read antenna tx power offset of B/C/D to A from EEPROM */ /* and read ThermalMeter from EEPROM */ - tempval = *(u8 *)&hwinfo[EEPROM_THERMALMETER]; + tempval = hwinfo[EEPROM_THERMALMETER]; rtlefuse->eeprom_thermalmeter = tempval; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); @@ -1906,20 +1904,20 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) rtlefuse->tssi_13dbm = rtlefuse->eeprom_thermalmeter * 100; /* Read CrystalCap from EEPROM */ - tempval = (*(u8 *)&hwinfo[EEPROM_CRYSTALCAP]) >> 4; + tempval = hwinfo[EEPROM_CRYSTALCAP] >> 4; rtlefuse->eeprom_crystalcap = tempval; /* CrystalCap, BIT(12)~15 */ rtlefuse->crystalcap = rtlefuse->eeprom_crystalcap; /* Read IC Version && Channel Plan */ /* Version ID, Channel plan */ - rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->txpwr_fromeprom = true; RTPRINT(rtlpriv, FINIT, INIT_TxPower, "EEPROM ChannelPlan = 0x%4x\n", rtlefuse->eeprom_channelplan); /* Read Customer ID or Board Type!!! */ - tempval = *(u8 *)&hwinfo[EEPROM_BOARDTYPE]; + tempval = hwinfo[EEPROM_BOARDTYPE]; /* Change RF type definition */ if (tempval == 0) rtlphy->rf_type = RF_2T2R; @@ -1941,7 +1939,7 @@ static void _rtl92se_read_adapter_info(struct ieee80211_hw *hw) } } rtlefuse->b1ss_support = rtlefuse->b1x1_recvcombine; - rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMID]; + rtlefuse->eeprom_oemid = *&hwinfo[EEPROM_CUSTOMID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x", rtlefuse->eeprom_oemid); @@ -2251,7 +2249,7 @@ void rtl92se_update_channel_access_setting(struct ieee80211_hw *hw) u16 sifs_timer; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *)&mac->slot_time); + &mac->slot_time); sifs_timer = 0x0e0e; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 812b5858f14a..36d1cb3aef8a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -599,7 +599,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct ieee80211_sta *sta = info->control.sta; - u8 *pdesc = (u8 *) pdesc_tx; + u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; u8 reserved_macid = 0; diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 6893bc207994..3fb595546538 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -871,8 +871,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, /* Called upon reception of a TX complete interrupt */ void wl1271_tx_complete(struct wl1271 *wl) { - struct wl1271_acx_mem_map *memmap = - (struct wl1271_acx_mem_map *)wl->target_mem_map; + struct wl1271_acx_mem_map *memmap = wl->target_mem_map; u32 count, fw_counter; u32 i; diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index 117c4123943c..7ab922209b25 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -827,7 +827,7 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, const zd_addr_t addr) { - return zd_ioread32v_locked(chip, value, (const zd_addr_t *)&addr, 1); + return zd_ioread32v_locked(chip, value, &addr, 1); } static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h index 99193b456a79..45e3bb28a01c 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.h +++ b/drivers/net/wireless/zd1211rw/zd_usb.h @@ -274,7 +274,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, const zd_addr_t addr) { - return zd_usb_ioread16v(usb, value, (const zd_addr_t *)&addr, 1); + return zd_usb_ioread16v(usb, value, &addr, 1); } void zd_usb_iowrite16v_async_start(struct zd_usb *usb); -- cgit v1.2.3 From c2fd03a0115a244c5f622453b2b1f038ed5700a6 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 4 Jun 2012 12:44:18 +0000 Subject: drivers: net: Remove casts to same type Adding casts of objects to the same type is unnecessary and confusing for a human reader. For example, this cast: int y; int *p = (int *)&y; I used the coccinelle script below to find and remove these unnecessary casts. I manually removed the conversions this script produces of casts with __force, __iomem and __user. @@ type T; T *p; @@ - (T *)p + p Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/appletalk/cops.c | 2 +- drivers/net/can/bfin_can.c | 2 +- drivers/net/can/mcp251x.c | 3 +-- drivers/net/fddi/defxx.c | 4 ++-- drivers/net/fddi/skfp/pmf.c | 8 ++++---- drivers/net/hamradio/mkiss.c | 8 ++++---- drivers/net/hyperv/netvsc.c | 2 +- drivers/net/irda/ali-ircc.c | 6 +++--- drivers/net/irda/au1k_ir.c | 2 +- drivers/net/slip/slip.c | 4 ++-- drivers/net/vmxnet3/vmxnet3_drv.c | 2 +- drivers/net/wan/x25_asy.c | 2 +- drivers/net/wimax/i2400m/fw.c | 2 +- 13 files changed, 23 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index dd5e04813b76..545c09ed9079 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -936,7 +936,7 @@ static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct cops_local *lp = netdev_priv(dev); struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; - struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr; + struct atalk_addr *aa = &lp->node_addr; switch(cmd) { diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index 3f88473423e9..ea3143895e6d 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -597,7 +597,7 @@ static int __devinit bfin_can_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s device registered" "(®_base=%p, rx_irq=%d, tx_irq=%d, err_irq=%d, sclk=%d)\n", - DRV_NAME, (void *)priv->membase, priv->rx_irq, + DRV_NAME, priv->membase, priv->rx_irq, priv->tx_irq, priv->err_irq, priv->can.clock.freq); return 0; diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index 346785c56a25..9120a36ec702 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -1020,8 +1020,7 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) GFP_DMA); if (priv->spi_tx_buf) { - priv->spi_rx_buf = (u8 *)(priv->spi_tx_buf + - (PAGE_SIZE / 2)); + priv->spi_rx_buf = (priv->spi_tx_buf + (PAGE_SIZE / 2)); priv->spi_rx_dma = (dma_addr_t)(priv->spi_tx_dma + (PAGE_SIZE / 2)); } else { diff --git a/drivers/net/fddi/defxx.c b/drivers/net/fddi/defxx.c index 4ad80f771099..6695a1dadf4e 100644 --- a/drivers/net/fddi/defxx.c +++ b/drivers/net/fddi/defxx.c @@ -2962,7 +2962,7 @@ static int dfx_rcv_init(DFX_board_t *bp, int get_buffers) bp->descr_block_virt->rcv_data[i+j].long_0 = (u32) (PI_RCV_DESCR_M_SOP | ((PI_RCV_DATA_K_SIZE_MAX / PI_ALIGN_K_RCV_DATA_BUFF) << PI_RCV_DESCR_V_SEG_LEN)); bp->descr_block_virt->rcv_data[i+j].long_1 = (u32) (bp->rcv_block_phys + (i * PI_RCV_DATA_K_SIZE_MAX)); - bp->p_rcv_buff_va[i+j] = (char *) (bp->rcv_block_virt + (i * PI_RCV_DATA_K_SIZE_MAX)); + bp->p_rcv_buff_va[i+j] = (bp->rcv_block_virt + (i * PI_RCV_DATA_K_SIZE_MAX)); } #endif } @@ -3030,7 +3030,7 @@ static void dfx_rcv_queue_process( #ifdef DYNAMIC_BUFFERS p_buff = (char *) (((struct sk_buff *)bp->p_rcv_buff_va[entry])->data); #else - p_buff = (char *) bp->p_rcv_buff_va[entry]; + p_buff = bp->p_rcv_buff_va[entry]; #endif memcpy(&descr, p_buff + RCV_BUFF_K_DESCR, sizeof(u32)); diff --git a/drivers/net/fddi/skfp/pmf.c b/drivers/net/fddi/skfp/pmf.c index 9ac4665d7411..24d8566cfd8b 100644 --- a/drivers/net/fddi/skfp/pmf.c +++ b/drivers/net/fddi/skfp/pmf.c @@ -1242,7 +1242,7 @@ static int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, if (len < 8) goto len_error ; if (set) - memcpy((char *) to,(char *) from+2,6) ; + memcpy(to,from+2,6) ; to += 8 ; from += 8 ; len -= 8 ; @@ -1251,7 +1251,7 @@ static int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, if (len < 4) goto len_error ; if (set) - memcpy((char *) to,(char *) from,4) ; + memcpy(to,from,4) ; to += 4 ; from += 4 ; len -= 4 ; @@ -1260,7 +1260,7 @@ static int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, if (len < 8) goto len_error ; if (set) - memcpy((char *) to,(char *) from,8) ; + memcpy(to,from,8) ; to += 8 ; from += 8 ; len -= 8 ; @@ -1269,7 +1269,7 @@ static int smt_set_para(struct s_smc *smc, struct smt_para *pa, int index, if (len < 32) goto len_error ; if (set) - memcpy((char *) to,(char *) from,32) ; + memcpy(to,from,32) ; to += 32 ; from += 32 ; len -= 32 ; diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index aed1a6105b24..2c0894a92abd 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -485,7 +485,7 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) return; default: - count = kiss_esc(p, (unsigned char *)ax->xbuff, len); + count = kiss_esc(p, ax->xbuff, len); } } else { unsigned short crc; @@ -497,7 +497,7 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) case CRC_MODE_SMACK: *p |= 0x80; crc = swab16(crc16(0, p, len)); - count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2); + count = kiss_esc_crc(p, ax->xbuff, crc, len+2); break; case CRC_MODE_FLEX_TEST: ax->crcmode = CRC_MODE_NONE; @@ -506,11 +506,11 @@ static void ax_encaps(struct net_device *dev, unsigned char *icp, int len) case CRC_MODE_FLEX: *p |= 0x20; crc = calc_crc_flex(p, len); - count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2); + count = kiss_esc_crc(p, ax->xbuff, crc, len+2); break; default: - count = kiss_esc(p, (unsigned char *)ax->xbuff, len); + count = kiss_esc(p, ax->xbuff, len); } } spin_unlock_bh(&ax->buflock); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 8b919471472f..4363c76e0d63 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -614,7 +614,7 @@ retry_send_cmplt: static void netvsc_receive_completion(void *context) { struct hv_netvsc_packet *packet = context; - struct hv_device *device = (struct hv_device *)packet->device; + struct hv_device *device = packet->device; struct netvsc_device *net_device; u64 transaction_id = 0; bool fsend_receive_comp = false; diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index dcc80d652b78..84872043b5c6 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1017,7 +1017,7 @@ static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) { int iobase; - struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + struct ali_ircc_cb *self = priv; struct net_device *dev; IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __func__ ); @@ -1052,7 +1052,7 @@ static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) */ static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) { - struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + struct ali_ircc_cb *self = priv; unsigned long flags; int iobase; int fcr; /* FIFO control reg */ @@ -1121,7 +1121,7 @@ static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) { - struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + struct ali_ircc_cb *self = priv; int iobase,dongle_id; int tmp = 0; diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index fc503aa5288e..e09417df8f39 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -794,7 +794,7 @@ static int __devinit au1k_irda_net_init(struct net_device *dev) /* allocate the data buffers */ aup->db[0].vaddr = - (void *)dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp); + dma_alloc(MAX_BUF_SIZE * 2 * NUM_IR_DESC, &temp); if (!aup->db[0].vaddr) goto out3; diff --git a/drivers/net/slip/slip.c b/drivers/net/slip/slip.c index d4c9db3da22a..a34d6bf5e43b 100644 --- a/drivers/net/slip/slip.c +++ b/drivers/net/slip/slip.c @@ -390,10 +390,10 @@ static void sl_encaps(struct slip *sl, unsigned char *icp, int len) #endif #ifdef CONFIG_SLIP_MODE_SLIP6 if (sl->mode & SL_MODE_SLIP6) - count = slip_esc6(p, (unsigned char *) sl->xbuff, len); + count = slip_esc6(p, sl->xbuff, len); else #endif - count = slip_esc(p, (unsigned char *) sl->xbuff, len); + count = slip_esc(p, sl->xbuff, len); /* Order of next two lines is *very* important. * When we are sending a little amount of data, diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index 3f04ba0a5454..93e0cfb739b8 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1037,7 +1037,7 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, #endif dev_dbg(&adapter->netdev->dev, "txd[%u]: SOP 0x%Lx 0x%x 0x%x\n", - (u32)((union Vmxnet3_GenericDesc *)ctx.sop_txd - + (u32)(ctx.sop_txd - tq->tx_ring.base), le64_to_cpu(gdesc->txd.addr), le32_to_cpu(gdesc->dword[2]), le32_to_cpu(gdesc->dword[3])); diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index d7a65e141d1a..44db8b75a531 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -231,7 +231,7 @@ static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len) } p = icp; - count = x25_asy_esc(p, (unsigned char *) sl->xbuff, len); + count = x25_asy_esc(p, sl->xbuff, len); /* Order of next two lines is *very* important. * When we are sending a little amount of data, diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c index 7cbd7d231e11..d09e44970e63 100644 --- a/drivers/net/wimax/i2400m/fw.c +++ b/drivers/net/wimax/i2400m/fw.c @@ -1268,7 +1268,7 @@ int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) size_t leftover, offset, header_len, size; leftover = top - itr; - offset = itr - (const void *) bcf; + offset = itr - bcf; if (leftover <= sizeof(*bcf_hdr)) { dev_err(dev, "firmware %s: %zu B left at @%zx, " "not enough for BCF header\n", -- cgit v1.2.3 From 78f85f5066b721666f869d537eaca6a0818aa301 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Wed, 16 May 2012 11:34:17 +0300 Subject: wlcore: add role_id to all the sched_scan commands Due to a need by the firmware when working in multirole the role id needs to be added to the structs of the following commands: CMD_CONNECTION_SCAN_CFG, CMD_CONNECTION_SCAN_SSID_CFG, CMD_START_PERIODIC_SCAN, CMD_STOP_PERIODIC_SCAN Signed-off-by: Yoni Divinsky Signed-off-by: Igal Chernobelsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 5 +++-- drivers/net/wireless/ti/wlcore/scan.c | 9 +++++++-- drivers/net/wireless/ti/wlcore/scan.h | 14 +++++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 54da16501e42..8800a63539e3 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2421,7 +2421,7 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif, } else { /* The current firmware only supports sched_scan in idle */ if (wl->sched_scanning) { - wl1271_scan_sched_scan_stop(wl); + wl1271_scan_sched_scan_stop(wl, wlvif); ieee80211_sched_scan_stopped(wl->hw); } @@ -3152,6 +3152,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int ret; wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop"); @@ -3165,7 +3166,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, if (ret < 0) goto out; - wl1271_scan_sched_scan_stop(wl); + wl1271_scan_sched_scan_stop(wl, wlvif); wl1271_ps_elp_sleep(wl); out: diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index ade21a011c45..94668d8ba14e 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -537,6 +537,7 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl, /* Returns the scan type to be used or a negative value on error */ static int wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req) { struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL; @@ -565,6 +566,7 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, goto out; } + cmd->role_id = wlvif->dev_role_id; if (!n_match_ssids) { /* No filter, with ssids */ type = SCAN_SSID_FILTER_DISABLED; @@ -652,6 +654,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, if (!cfg) return -ENOMEM; + cfg->role_id = wlvif->dev_role_id; cfg->rssi_threshold = c->rssi_threshold; cfg->snr_threshold = c->snr_threshold; cfg->n_probe_reqs = c->num_probe_reqs; @@ -669,7 +672,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, cfg->intervals[i] = cpu_to_le32(req->interval); cfg->ssid_len = 0; - ret = wl12xx_scan_sched_scan_ssid_list(wl, req); + ret = wl12xx_scan_sched_scan_ssid_list(wl, wlvif, req); if (ret < 0) goto out; @@ -741,6 +744,7 @@ int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (!start) return -ENOMEM; + start->role_id = wlvif->dev_role_id; start->tag = WL1271_SCAN_DEFAULT_TAG; ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, @@ -762,7 +766,7 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl) ieee80211_sched_scan_results(wl->hw); } -void wl1271_scan_sched_scan_stop(struct wl1271 *wl) +void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) { struct wl1271_cmd_sched_scan_stop *stop; int ret = 0; @@ -776,6 +780,7 @@ void wl1271_scan_sched_scan_stop(struct wl1271 *wl) return; } + stop->role_id = wlvif->dev_role_id; stop->tag = WL1271_SCAN_DEFAULT_TAG; ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 81ee36ac2078..5b6a609fb460 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -40,7 +40,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies); int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif); -void wl1271_scan_sched_scan_stop(struct wl1271 *wl); +void wl1271_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 @@ -185,7 +185,8 @@ struct wl1271_cmd_sched_scan_config { u8 dfs; - u8 padding[3]; + u8 role_id; + u8 padding[2]; struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; @@ -212,21 +213,24 @@ struct wl1271_cmd_sched_scan_ssid_list { u8 n_ssids; struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS]; - u8 padding[3]; + u8 role_id; + u8 padding[2]; } __packed; struct wl1271_cmd_sched_scan_start { struct wl1271_cmd_header header; u8 tag; - u8 padding[3]; + u8 role_id; + u8 padding[2]; } __packed; struct wl1271_cmd_sched_scan_stop { struct wl1271_cmd_header header; u8 tag; - u8 padding[3]; + u8 role_id; + u8 padding[2]; } __packed; -- cgit v1.2.3 From 587cc286c8a8e22bbe4521c331b88d62d0cb1076 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 15 May 2012 18:31:58 +0300 Subject: wlcore: compare ssid_len before comparing ssids When comparing 2 ssids the ssid_len must be taken into account. Otherwise, a substring will be treated as equal. This bug might cause ssids to get scanned as public ssids (rather than hidden), resulting in broadcast probe request (instead of ssid-specific ones) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/scan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index 94668d8ba14e..e1dbe812615f 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -605,7 +605,9 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, continue; for (j = 0; j < cmd->n_ssids; j++) - if (!memcmp(req->ssids[i].ssid, + if ((req->ssids[i].ssid_len == + req->ssids[j].ssid_len) && + !memcmp(req->ssids[i].ssid, cmd->ssids[j].ssid, req->ssids[i].ssid_len)) { cmd->ssids[j].type = -- cgit v1.2.3 From 97511b15b1926337f90226b3cb735e52f2dc8a85 Mon Sep 17 00:00:00 2001 From: Victor Goldenshtein Date: Tue, 15 May 2012 17:04:40 +0300 Subject: wlcore: set channels 12-14 as pactive for sched scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce “pactive” scan mode – which instructs the fw to perform a passive scan until an activity/energy is detected on these channels, once energy detected the channel becomes active. Signed-off-by: Victor Goldenshtein Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/scan.c | 39 +++++++++++++++++++++++++++++------ drivers/net/wireless/ti/wlcore/scan.h | 7 +++++-- 2 files changed, 38 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index e1dbe812615f..d9daed53ceb7 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -411,7 +411,8 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, struct cfg80211_sched_scan_request *req, struct conn_scan_ch_params *channels, u32 band, bool radar, bool passive, - int start, int max_channels) + int start, int max_channels, + u8 *n_pactive_ch) { struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int i, j; @@ -479,6 +480,23 @@ wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, channels[j].tx_power_att = req->channels[i]->max_power; channels[j].channel = req->channels[i]->hw_value; + if ((band == IEEE80211_BAND_2GHZ) && + (channels[j].channel >= 12) && + (channels[j].channel <= 14) && + (flags & IEEE80211_CHAN_PASSIVE_SCAN) && + !force_passive) { + /* pactive channels treated as DFS */ + channels[j].flags = SCAN_CHANNEL_FLAGS_DFS; + + /* + * n_pactive_ch is counted down from the end of + * the passive channel list + */ + (*n_pactive_ch)++; + wl1271_debug(DEBUG_SCAN, "n_pactive_ch = %d", + *n_pactive_ch); + } + j++; } } @@ -491,38 +509,47 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl, struct cfg80211_sched_scan_request *req, struct wl1271_cmd_sched_scan_config *cfg) { + u8 n_pactive_ch = 0; + cfg->passive[0] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, IEEE80211_BAND_2GHZ, false, true, 0, - MAX_CHANNELS_2GHZ); + MAX_CHANNELS_2GHZ, + &n_pactive_ch); cfg->active[0] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, IEEE80211_BAND_2GHZ, false, false, cfg->passive[0], - MAX_CHANNELS_2GHZ); + MAX_CHANNELS_2GHZ, + &n_pactive_ch); cfg->passive[1] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, IEEE80211_BAND_5GHZ, false, true, 0, - MAX_CHANNELS_5GHZ); + MAX_CHANNELS_5GHZ, + &n_pactive_ch); cfg->dfs = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, IEEE80211_BAND_5GHZ, true, true, cfg->passive[1], - MAX_CHANNELS_5GHZ); + MAX_CHANNELS_5GHZ, + &n_pactive_ch); cfg->active[1] = wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, IEEE80211_BAND_5GHZ, false, false, cfg->passive[1] + cfg->dfs, - MAX_CHANNELS_5GHZ); + MAX_CHANNELS_5GHZ, + &n_pactive_ch); /* 802.11j channels are not supported yet */ cfg->passive[2] = 0; cfg->active[2] = 0; + cfg->n_pactive_ch = n_pactive_ch; + wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", cfg->active[0], cfg->passive[0]); wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h index 5b6a609fb460..29f3c8d6b046 100644 --- a/drivers/net/wireless/ti/wlcore/scan.h +++ b/drivers/net/wireless/ti/wlcore/scan.h @@ -142,7 +142,8 @@ enum { SCAN_BSS_TYPE_ANY, }; -#define SCAN_CHANNEL_FLAGS_DFS BIT(0) +#define SCAN_CHANNEL_FLAGS_DFS BIT(0) /* channel is passive until an + activity is detected on it */ #define SCAN_CHANNEL_FLAGS_DFS_ENABLED BIT(1) struct conn_scan_ch_params { @@ -185,8 +186,10 @@ struct wl1271_cmd_sched_scan_config { u8 dfs; + u8 n_pactive_ch; /* number of pactive (passive until fw detects energy) + channels in BG band */ u8 role_id; - u8 padding[2]; + u8 padding[1]; struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; struct conn_scan_ch_params channels_5[MAX_CHANNELS_5GHZ]; -- cgit v1.2.3 From e8c9bd5b8d807cfe6c923265969a523b1ba1e6c2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Jun 2012 08:18:22 +0200 Subject: cfg80211: clarify set_channel APIs Now that we've removed all uses of the set_channel API except for the monitor channel and in libertas, clarify this. Split the libertas mesh use into a new libertas_set_mesh_channel() operation, just to keep backward compatibility, and rename the normal set_channel() to set_monitor_channel(). Also describe the desired set_monitor_channel() semantics more clearly. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/cfg.c | 39 ++++++++++++++++++++++++++---------- drivers/net/wireless/libertas/dev.h | 1 + drivers/net/wireless/libertas/mesh.c | 7 ++----- drivers/net/wireless/orinoco/cfg.c | 9 ++++----- 4 files changed, 35 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 2fa879b015b6..f4a203049fb4 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -435,24 +435,40 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) * Set Channel */ -static int lbs_cfg_set_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) +static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = -ENOTSUPP; - lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d, type %d", - netdev_name(netdev), channel->center_freq, channel_type); + lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", + channel->center_freq, channel_type); if (channel_type != NL80211_CHAN_NO_HT) goto out; - if (netdev == priv->mesh_dev) - ret = lbs_mesh_set_channel(priv, channel->hw_value); - else - ret = lbs_set_channel(priv, channel->hw_value); + ret = lbs_set_channel(priv, channel->hw_value); + + out: + lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); + return ret; +} + +static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy, + struct net_device *netdev, + struct ieee80211_channel *channel) +{ + struct lbs_private *priv = wiphy_priv(wiphy); + int ret = -ENOTSUPP; + + lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d", + netdev_name(netdev), channel->center_freq); + + if (netdev != priv->mesh_dev) + goto out; + + ret = lbs_mesh_set_channel(priv, channel->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); @@ -2029,7 +2045,8 @@ static int lbs_leave_ibss(struct wiphy *wiphy, struct net_device *dev) */ static struct cfg80211_ops lbs_cfg80211_ops = { - .set_channel = lbs_cfg_set_channel, + .set_monitor_channel = lbs_cfg_set_monitor_channel, + .libertas_set_mesh_channel = lbs_cfg_set_mesh_channel, .scan = lbs_cfg_scan, .connect = lbs_cfg_connect, .disconnect = lbs_cfg_disconnect, diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 672005430aca..60996ce89f77 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -58,6 +58,7 @@ struct lbs_private { uint16_t mesh_tlv; u8 mesh_ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 mesh_ssid_len; + u8 mesh_channel; #endif /* Debugfs */ diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index e87c031b298f..97807751ebcf 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -131,16 +131,13 @@ static int lbs_mesh_config(struct lbs_private *priv, uint16_t action, int lbs_mesh_set_channel(struct lbs_private *priv, u8 channel) { + priv->mesh_channel = channel; return lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START, channel); } static uint16_t lbs_mesh_get_channel(struct lbs_private *priv) { - struct wireless_dev *mesh_wdev = priv->mesh_dev->ieee80211_ptr; - if (mesh_wdev->channel) - return mesh_wdev->channel->hw_value; - else - return 1; + return priv->mesh_channel ?: 1; } /*************************************************************************** diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index f7b15b8934fa..e15675585fb1 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -160,10 +160,9 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev, return err; } -static int orinoco_set_channel(struct wiphy *wiphy, - struct net_device *netdev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +static int orinoco_set_monitor_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) { struct orinoco_private *priv = wiphy_priv(wiphy); int err = 0; @@ -286,7 +285,7 @@ static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed) const struct cfg80211_ops orinoco_cfg_ops = { .change_virtual_intf = orinoco_change_vif, - .set_channel = orinoco_set_channel, + .set_monitor_channel = orinoco_set_monitor_channel, .scan = orinoco_scan, .set_wiphy_params = orinoco_set_wiphy_params, }; -- cgit v1.2.3 From 620a55687b20c23ca7b8d8560b0e8fb3ca13869a Mon Sep 17 00:00:00 2001 From: Zefir Kurtisi Date: Wed, 23 May 2012 13:34:31 +0200 Subject: ath9k: fix/add bits for spectral scanning Updates for spectral scanning register modification for AR93x: * fix bits for spectral_scan_count * add bits for new parameters Signed-off-by: Zefir Kurtisi Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.h | 32 ++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 7268a48a92a1..ed662c3bae5b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -820,18 +820,26 @@ #define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 #define AR_PHY_RX_DELAY_DELAY 0x00003FFF #define AR_PHY_CCK_TX_CTRL_JAPAN 0x00000010 -#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x00000001 -#define AR_PHY_SPECTRAL_SCAN_ENABLE_S 0 -#define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 -#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 -#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 -#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4 -#define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 -#define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8 -#define AR_PHY_SPECTRAL_SCAN_COUNT 0x00FF0000 -#define AR_PHY_SPECTRAL_SCAN_COUNT_S 16 -#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x01000000 -#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 24 + +#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x00000001 +#define AR_PHY_SPECTRAL_SCAN_ENABLE_S 0 +#define AR_PHY_SPECTRAL_SCAN_ACTIVE 0x00000002 +#define AR_PHY_SPECTRAL_SCAN_ACTIVE_S 1 +#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD 0x000000F0 +#define AR_PHY_SPECTRAL_SCAN_FFT_PERIOD_S 4 +#define AR_PHY_SPECTRAL_SCAN_PERIOD 0x0000FF00 +#define AR_PHY_SPECTRAL_SCAN_PERIOD_S 8 +#define AR_PHY_SPECTRAL_SCAN_COUNT 0x0FFF0000 +#define AR_PHY_SPECTRAL_SCAN_COUNT_S 16 +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT 0x10000000 +#define AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT_S 28 +#define AR_PHY_SPECTRAL_SCAN_PRIORITY 0x20000000 +#define AR_PHY_SPECTRAL_SCAN_PRIORITY_S 29 +#define AR_PHY_SPECTRAL_SCAN_USE_ERR5 0x40000000 +#define AR_PHY_SPECTRAL_SCAN_USE_ERR5_S 30 +#define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT 0x80000000 +#define AR_PHY_SPECTRAL_SCAN_COMPRESSED_RPT_S 31 + #define AR_PHY_CHANNEL_STATUS_RX_CLEAR 0x00000004 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION 0x00000001 #define AR_PHY_RTT_CTRL_ENA_RADIO_RETENTION_S 0 -- cgit v1.2.3 From 6851dff3e6b858f2759fc9ecebc4ec608df81805 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 28 May 2012 11:53:11 +0200 Subject: rt2x00: claim RSN IBSS support Similar like other drivers, do not configure group keys to the hardware (on Ad-Hoc mode) to make IBSS RSN work. Signed-off-by: Stanislaw Gruszka Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00dev.c | 2 ++ drivers/net/wireless/rt2x00/rt2x00mac.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index e5404e576251..a6b88bd4a1a5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1161,6 +1161,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_WDS); + rt2x00dev->hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + /* * Initialize work. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index dd24b2663b5e..4ff26c2159bf 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -506,9 +506,19 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; - else if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + + if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + return -EOPNOTSUPP; + + /* + * To support IBSS RSN, don't program group keys in IBSS, the + * hardware will then not attempt to decrypt the frames. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) return -EOPNOTSUPP; - else if (key->keylen > 32) + + if (key->keylen > 32) return -ENOSPC; memset(&crypto, 0, sizeof(crypto)); -- cgit v1.2.3 From d7fbcada37ec71b61901bf4344ca832a1bb9f5d1 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 28 May 2012 11:54:11 +0200 Subject: iwl4965: claim RSN IBSS support Similar like other drivers including iwl3945, do not configure group keys to the hardware (on Ad-Hoc mode) to make IBSS RSN work. Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/4965-mac.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 509301a5e7e2..d24eaf89ffb5 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5724,7 +5724,8 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); hw->wiphy->flags |= - WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS; + WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | + WIPHY_FLAG_IBSS_RSN; /* * For now, disable PS by default because it affects @@ -5873,6 +5874,16 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return -EOPNOTSUPP; } + /* + * To support IBSS RSN, don't program group keys in IBSS, the + * hardware will then not attempt to decrypt the frames. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + D_MAC80211("leave - ad-hoc group key\n"); + return -EOPNOTSUPP; + } + sta_id = il_sta_id_or_broadcast(il, sta); if (sta_id == IL_INVALID_STATION) return -EINVAL; -- cgit v1.2.3 From cff3d1f0931d0e6189f5ee718112b235bad1bf99 Mon Sep 17 00:00:00 2001 From: "Zero.Lin" Date: Tue, 29 May 2012 16:11:09 +0800 Subject: rt2x00:Add RT5392 chipset support Signed-off-by: Zero.Lin Reviewed-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800.h | 6 ++++++ drivers/net/wireless/rt2x00/rt2800lib.c | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index 6403f49da419..1ca88cdc6ece 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -72,6 +72,7 @@ #define RF5370 0x5370 #define RF5372 0x5372 #define RF5390 0x5390 +#define RF5392 0x5392 /* * Chipset revisions. @@ -1945,6 +1946,11 @@ struct mac_iveiv_entry { */ #define RFCSR49_TX FIELD8(0x3f) +/* + * RFCSR 50: + */ +#define RFCSR50_TX FIELD8(0x3f) + /* * RF registers */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 11488e743f08..4d3747c3010b 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -1958,7 +1958,22 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); + if (info->default_power1 > RT5390_POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR50_TX, + RT5390_POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR50_TX, + info->default_power2); + rt2800_rfcsr_write(rt2x00dev, 50, rfcsr); + } + rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); + if (rt2x00_rt(rt2x00dev, RT5392)) { + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); + } rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1); rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); @@ -2064,6 +2079,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF5370: case RF5372: case RF5390: + case RF5392: rt2800_config_channel_rf53xx(rt2x00dev, conf, rf, info); break; default: @@ -2554,6 +2570,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) case RF5370: case RF5372: case RF5390: + case RF5392: rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); @@ -4269,6 +4286,7 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF5370: case RF5372: case RF5390: + case RF5392: break; default: ERROR(rt2x00dev, "Invalid RF chipset 0x%04x detected.\n", @@ -4583,7 +4601,8 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || rt2x00_rf(rt2x00dev, RF5372) || - rt2x00_rf(rt2x00dev, RF5390)) { + rt2x00_rf(rt2x00dev, RF5390) || + rt2x00_rf(rt2x00dev, RF5392)) { spec->num_channels = 14; spec->channels = rf_vals_3x; } else if (rt2x00_rf(rt2x00dev, RF3052)) { @@ -4670,6 +4689,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF5370: case RF5372: case RF5390: + case RF5392: __set_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags); break; } -- cgit v1.2.3 From d202caff28d69f4ec6cf56568f79b0916294cade Mon Sep 17 00:00:00 2001 From: Vivek Natarajan Date: Tue, 29 May 2012 15:59:55 +0530 Subject: ath9k: Ensure a fair beacon distribution in IBSS mode Configure CWmin based on slot time for IBSS mode. This helps in increasing the beacon distribution of ath9k to accepted levels in 11a mode. Signed-off-by: Vivek Natarajan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/beacon.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 11bc55e3d697..70b802529123 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -48,7 +48,10 @@ int ath_beaconq_config(struct ath_softc *sc) txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; - qi.tqi_cwmin = 4*qi_be.tqi_cwmin; + if (ah->slottime == ATH9K_SLOT_TIME_20) + qi.tqi_cwmin = 2*qi_be.tqi_cwmin; + else + qi.tqi_cwmin = 4*qi_be.tqi_cwmin; qi.tqi_cwmax = qi_be.tqi_cwmax; } -- cgit v1.2.3 From ccaf8c32d5ed08bfb4c45492f8f1c145fd45f4e8 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Thu, 31 May 2012 22:38:22 +0200 Subject: ssb: recognize ARM Cortex M3 I found this core on a BCM4322, a PCI card in the Linksys WRT610N V1. This core is not used by the driver, this patch just makes ssb show the correct name. Signed-off-by: Hauke Mehrtens Signed-off-by: John W. Linville --- drivers/ssb/scan.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c index 266c7c5c86dc..ab4627cf1114 100644 --- a/drivers/ssb/scan.c +++ b/drivers/ssb/scan.c @@ -90,6 +90,8 @@ const char *ssb_core_name(u16 coreid) return "ARM 1176"; case SSB_DEV_ARM_7TDMI: return "ARM 7TDMI"; + case SSB_DEV_ARM_CM3: + return "ARM Cortex M3"; } return "UNKNOWN"; } -- cgit v1.2.3 From 2b0446c4205fb3e77b205276ecd36d30b82cbb84 Mon Sep 17 00:00:00 2001 From: Chris Yungmann Date: Sun, 3 Jun 2012 00:57:57 -0500 Subject: brcm80211: brcmsmac: fixed checkpatch and sparse warnings Fixed checkpatch and sparse warnings related to aiutils.* Signed-off-by: Chris Yungmann Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/aiutils.c | 3 +-- drivers/net/wireless/brcm80211/brcmsmac/aiutils.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c index 6d8b7213643a..3c6f9b1e8d05 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c @@ -631,9 +631,8 @@ uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val) cc = sii->icbus->drv_cc.core; /* mask and set */ - if (mask || val) { + if (mask || val) bcma_maskset32(cc, regoff, ~mask, val); - } /* readback */ w = bcma_read32(cc, regoff); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h index d9f04a683bdb..d6fa9829af9a 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h @@ -193,7 +193,7 @@ extern void ai_detach(struct si_pub *sih); extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); extern void ai_clkctl_init(struct si_pub *sih); extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); -extern bool ai_clkctl_cc(struct si_pub *sih, uint mode); +extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); extern bool ai_deviceremoved(struct si_pub *sih); extern void ai_pci_down(struct si_pub *sih); -- cgit v1.2.3 From aaa1ec46b3be258b851477024fb41e140b14d1a6 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:08 +0530 Subject: ath9k: Prune ath9k_init_device Instead of cluttering ath9k_init_device() with 'ath_softc' specific initialization code, use ath9k_init_softc() to setup driver-specific work routines, timers etc. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/init.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index dee9e092449a..3e2283970ee3 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -489,6 +489,7 @@ static void ath9k_init_misc(struct ath_softc *sc) setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); + sc->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->config.txpowlimit = ATH_TXPOWER_MAX; memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; @@ -560,6 +561,12 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, (unsigned long)sc); + INIT_WORK(&sc->hw_reset_work, ath_reset_work); + INIT_WORK(&sc->hw_check_work, ath_hw_check); + INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); + INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); + setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc); + /* * Cache line size is used to size and align various * structures used to communicate with the hardware. @@ -782,11 +789,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, ARRAY_SIZE(ath9k_tpt_blink)); #endif - INIT_WORK(&sc->hw_reset_work, ath_reset_work); - INIT_WORK(&sc->hw_check_work, ath_hw_check); - INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); - INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); - /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) @@ -805,9 +807,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, goto error_world; } - setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc); - sc->last_rssi = ATH_RSSI_DUMMY_MARKER; - ath_init_leds(sc); ath_start_rfkill_poll(sc); -- cgit v1.2.3 From d09f5f4cfb9f4c6aa8c18ea522e824660d4096d7 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:14 +0530 Subject: ath9k: Handle ASPM properly ASPM has to be disabled when BTCOEX is in use, do this properly by calling the bus-specific ASPM init routine after the BTCOEX scheme has been determined. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 11 ----------- drivers/net/wireless/ath/ath9k/init.c | 3 +++ drivers/net/wireless/ath/ath9k/pci.c | 5 +++++ 3 files changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 7db1890448f2..febf6971b40a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -390,14 +390,6 @@ static void ath9k_hw_disablepcie(struct ath_hw *ah) REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); } -static void ath9k_hw_aspm_init(struct ath_hw *ah) -{ - struct ath_common *common = ath9k_hw_common(ah); - - if (common->bus_ops->aspm_init) - common->bus_ops->aspm_init(common); -} - /* This should work for all families including legacy */ static bool ath9k_hw_chip_test(struct ath_hw *ah) { @@ -693,9 +685,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) if (r) return r; - if (ah->is_pciexpress) - ath9k_hw_aspm_init(ah); - r = ath9k_hw_init_macaddr(ah); if (r) { ath_err(common, "Failed to initialize MAC address\n"); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 3e2283970ee3..9dfce1a69c73 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -597,6 +597,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); + if (common->bus_ops->aspm_init) + common->bus_ops->aspm_init(common); + return 0; err_btcoex: diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index a856b51255f4..6ec9f88712d0 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -115,6 +115,9 @@ static void ath_pci_aspm_init(struct ath_common *common) int pos; u8 aspm; + if (!ah->is_pciexpress) + return; + pos = pci_pcie_cap(pdev); if (!pos) return; @@ -138,6 +141,7 @@ static void ath_pci_aspm_init(struct ath_common *common) aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm); + ath_info(common, "Disabling ASPM since BTCOEX is enabled\n"); return; } @@ -147,6 +151,7 @@ static void ath_pci_aspm_init(struct ath_common *common) ah->aspm_enabled = true; /* Initialize PCIe PM and SERDES registers. */ ath9k_hw_configpcipowersave(ah, false); + ath_info(common, "ASPM enabled: 0x%x\n", aspm); } } -- cgit v1.2.3 From e270e776a0985511146555e79edbe53539809adb Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:19 +0530 Subject: ath9k: Setup MCI interrupts properly MCI interrupts have to be enabled only when BTCOEX is actually in use. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 3 +-- drivers/net/wireless/ath/ath9k/mci.c | 11 +++++++++++ drivers/net/wireless/ath/ath9k/mci.h | 11 ++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index cfbcc047c61e..3809fc8ec8a4 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1045,8 +1045,7 @@ static int ath9k_start(struct ieee80211_hw *hw) if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - ah->imask |= ATH9K_INT_MCI; + ath_mci_enable(sc); sc->sc_flags &= ~SC_OP_INVALID; sc->sc_ah->is_monitoring = false; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 29fe52d69973..7b553f6a97b1 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -538,3 +538,14 @@ void ath_mci_intr(struct ath_softc *sc) mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR | AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT); } + +void ath_mci_enable(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + if (!common->btcoex_enabled) + return; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) + sc->sc_ah->imask |= ATH9K_INT_MCI; +} diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h index c841444f53c2..fc14eea034eb 100644 --- a/drivers/net/wireless/ath/ath9k/mci.h +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -130,4 +130,13 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci); int ath_mci_setup(struct ath_softc *sc); void ath_mci_cleanup(struct ath_softc *sc); void ath_mci_intr(struct ath_softc *sc); -#endif + +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT +void ath_mci_enable(struct ath_softc *sc); +#else +static inline void ath_mci_enable(struct ath_softc *sc) +{ +} +#endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ + +#endif /* MCI_H*/ -- cgit v1.2.3 From 83c7a52dcce256fc20cd43dbf6866e3e05f2bf71 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:25 +0530 Subject: ath9k_hw: Fix AR_RTC_KEEP_AWAKE usage The default value that is being programmed to AR_RTC_KEEP_AWAKE is 0x2, there is no need to program it manually in various functions. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 1 - drivers/net/wireless/ath/ath9k/hw.c | 11 ----------- 2 files changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index ffbb180f91e1..9331d73f69aa 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -330,7 +330,6 @@ void ar9003_mci_set_full_sleep(struct ath_hw *ah) } mci->ready = false; - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); } static void ar9003_mci_disable_interrupt(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index febf6971b40a..42cf3c8db95b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1432,9 +1432,6 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type) break; } - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); - return ret; } @@ -2167,10 +2164,6 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) switch (mode) { case ATH9K_PM_AWAKE: status = ath9k_hw_set_power_awake(ah, setChip); - - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); - break; case ATH9K_PM_FULL_SLEEP: if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) @@ -2180,10 +2173,6 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) ah->chip_fullsleep = true; break; case ATH9K_PM_NETWORK_SLEEP: - - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) - REG_WRITE(ah, AR_RTC_KEEP_AWAKE, 0x2); - ath9k_set_power_network_sleep(ah, setChip); break; default: -- cgit v1.2.3 From 5955b2b0ef208e03188ef1c2810c004bde493253 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:30 +0530 Subject: ath9k_hw: Fix MCI usage MCI has to be handled only when BTCOEX is actually enabled. Check for this condition before calling MCI related functions from various reset/calibration call-sites. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 5 ++--- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 8 -------- drivers/net/wireless/ath/ath9k/hw.c | 15 +++++++-------- drivers/net/wireless/ath/ath9k/hw.h | 9 +++++++++ 4 files changed, 18 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 9fdd70fcaf5b..f83a4a2e1da2 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -901,7 +901,6 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, bool is_reusable = true, status = true; bool run_rtt_cal = false, run_agc_cal; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); - bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI); u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL; @@ -970,7 +969,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, } else if (caldata && !caldata->done_txiqcal_once) run_agc_cal = true; - if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal) + if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_req(ah, &is_reusable); if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) { @@ -993,7 +992,7 @@ skip_tx_iqcal: 0, AH_WAIT_TIMEOUT); } - if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal) + if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_done(ah); if (rtt && !run_rtt_cal) { diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 4842f6c06b8c..652ab8c39e2b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -273,10 +273,6 @@ void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT -static inline bool ar9003_mci_is_ready(struct ath_hw *ah) -{ - return ah->btcoex_hw.mci.ready; -} void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep); void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable); void ar9003_mci_init_cal_done(struct ath_hw *ah); @@ -292,10 +288,6 @@ void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked); #else -static inline bool ar9003_mci_is_ready(struct ath_hw *ah) -{ - return false; -} static inline void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep) { } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 42cf3c8db95b..a7231cadec40 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1707,7 +1707,7 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) ath9k_hw_loadnf(ah, ah->curchan); ath9k_hw_start_nfcal(ah, true); - if ((ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && ar9003_mci_is_ready(ah)) + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_2g5g_switch(ah, true); if (AR_SREV_9271(ah)) @@ -1728,10 +1728,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, u64 tsf = 0; int i, r; bool start_mci_reset = false; - bool mci = !!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI); bool save_fullsleep = ah->chip_fullsleep; - if (mci) { + if (ath9k_hw_mci_is_enabled(ah)) { start_mci_reset = ar9003_mci_start_reset(ah, chan); if (start_mci_reset) return 0; @@ -1760,7 +1759,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, return r; } - if (mci) + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_stop_bt(ah, save_fullsleep); saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA); @@ -1818,7 +1817,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (r) return r; - if (mci) + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep); /* @@ -1937,7 +1936,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); - if (mci && ar9003_mci_end_reset(ah, chan, caldata)) + if (ath9k_hw_mci_is_enabled(ah) && ar9003_mci_end_reset(ah, chan, caldata)) return -EIO; ENABLE_REGWRITE_BUFFER(ah); @@ -1982,7 +1981,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ath9k_hw_btcoex_is_enabled(ah)) ath9k_hw_btcoex_enable(ah); - if (mci) + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_check_bt(ah); if (AR_SREV_9300_20_OR_LATER(ah)) { @@ -2166,7 +2165,7 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) status = ath9k_hw_set_power_awake(ah, setChip); break; case ATH9K_PM_FULL_SLEEP: - if (ah->caps.hw_caps & ATH9K_HW_CAP_MCI) + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_set_full_sleep(ah); ath9k_set_power_sleep(ah, setChip); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index b620c557c2a6..3740aab864f4 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1037,6 +1037,11 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { return ah->btcoex_hw.enabled; } +static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah) +{ + return ah->btcoex_hw.enabled && (ah->caps.hw_caps & ATH9K_HW_CAP_MCI); + +} void ath9k_hw_btcoex_enable(struct ath_hw *ah); static inline enum ath_btcoex_scheme ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) @@ -1048,6 +1053,10 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) { return false; } +static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah) +{ + return false; +} static inline void ath9k_hw_btcoex_enable(struct ath_hw *ah) { } -- cgit v1.2.3 From 31604cf0a73ffd63929f3a646d5402d5901c6e4b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:36 +0530 Subject: ath9k_hw: Cleanup power mode API The 'setChip' variable is unused, remove it and simplify the functions. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 175 ++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 90 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index a7231cadec40..9f0f5f74f17a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2004,39 +2004,38 @@ EXPORT_SYMBOL(ath9k_hw_reset); * Notify Power Mgt is disabled in self-generated frames. * If requested, force chip to sleep. */ -static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip) +static void ath9k_set_power_sleep(struct ath_hw *ah) { REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); - if (setChip) { - if (AR_SREV_9462(ah)) { - REG_WRITE(ah, AR_TIMER_MODE, - REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00); - REG_WRITE(ah, AR_NDP2_TIMER_MODE, REG_READ(ah, - AR_NDP2_TIMER_MODE) & 0xFFFFFF00); - REG_WRITE(ah, AR_SLP32_INC, - REG_READ(ah, AR_SLP32_INC) & 0xFFF00000); - /* xxx Required for WLAN only case ? */ - REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); - udelay(100); - } - /* - * Clear the RTC force wake bit to allow the - * mac to go to sleep. - */ - REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); + if (AR_SREV_9462(ah)) { + REG_WRITE(ah, AR_TIMER_MODE, + REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00); + REG_WRITE(ah, AR_NDP2_TIMER_MODE, + REG_READ(ah, AR_NDP2_TIMER_MODE) & 0xFFFFFF00); + REG_WRITE(ah, AR_SLP32_INC, + REG_READ(ah, AR_SLP32_INC) & 0xFFF00000); + /* xxx Required for WLAN only case ? */ + REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); + udelay(100); + } - if (AR_SREV_9462(ah)) - udelay(100); + /* + * Clear the RTC force wake bit to allow the + * mac to go to sleep. + */ + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); - if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) - REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); + if (AR_SREV_9462(ah)) + udelay(100); - /* Shutdown chip. Active low */ - if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) { - REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN); - udelay(2); - } + if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) + REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); + + /* Shutdown chip. Active low */ + if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) { + REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN); + udelay(2); } /* Clear Bit 14 of AR_WA after putting chip into Full Sleep mode. */ @@ -2049,44 +2048,42 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip) * frames. If request, set power mode of chip to * auto/normal. Duration in units of 128us (1/8 TU). */ -static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip) +static void ath9k_set_power_network_sleep(struct ath_hw *ah) { + struct ath9k_hw_capabilities *pCap = &ah->caps; u32 val; REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); - if (setChip) { - struct ath9k_hw_capabilities *pCap = &ah->caps; - if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { - /* Set WakeOnInterrupt bit; clear ForceWake bit */ - REG_WRITE(ah, AR_RTC_FORCE_WAKE, - AR_RTC_FORCE_WAKE_ON_INT); - } else { + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { + /* Set WakeOnInterrupt bit; clear ForceWake bit */ + REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_ON_INT); + } else { - /* When chip goes into network sleep, it could be waken - * up by MCI_INT interrupt caused by BT's HW messages - * (LNA_xxx, CONT_xxx) which chould be in a very fast - * rate (~100us). This will cause chip to leave and - * re-enter network sleep mode frequently, which in - * consequence will have WLAN MCI HW to generate lots of - * SYS_WAKING and SYS_SLEEPING messages which will make - * BT CPU to busy to process. - */ - if (AR_SREV_9462(ah)) { - val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) & - ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK; - REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val); - } - /* - * Clear the RTC force wake bit to allow the - * mac to go to sleep. - */ - REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, - AR_RTC_FORCE_WAKE_EN); - - if (AR_SREV_9462(ah)) - udelay(30); + /* When chip goes into network sleep, it could be waken + * up by MCI_INT interrupt caused by BT's HW messages + * (LNA_xxx, CONT_xxx) which chould be in a very fast + * rate (~100us). This will cause chip to leave and + * re-enter network sleep mode frequently, which in + * consequence will have WLAN MCI HW to generate lots of + * SYS_WAKING and SYS_SLEEPING messages which will make + * BT CPU to busy to process. + */ + if (AR_SREV_9462(ah)) { + val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) & + ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK; + REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val); } + /* + * Clear the RTC force wake bit to allow the + * mac to go to sleep. + */ + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + + if (AR_SREV_9462(ah)) + udelay(30); } /* Clear Bit 14 of AR_WA after putting chip into Net Sleep mode. */ @@ -2094,7 +2091,7 @@ static void ath9k_set_power_network_sleep(struct ath_hw *ah, int setChip) REG_WRITE(ah, AR_WA, ah->WARegVal & ~AR_WA_D3_L1_DISABLE); } -static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) +static bool ath9k_hw_set_power_awake(struct ath_hw *ah) { u32 val; int i; @@ -2105,37 +2102,35 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) udelay(10); } - if (setChip) { - if ((REG_READ(ah, AR_RTC_STATUS) & - AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) { - if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { - return false; - } - if (!AR_SREV_9300_20_OR_LATER(ah)) - ath9k_hw_init_pll(ah, NULL); + if ((REG_READ(ah, AR_RTC_STATUS) & + AR_RTC_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) { + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { + return false; } - if (AR_SREV_9100(ah)) - REG_SET_BIT(ah, AR_RTC_RESET, - AR_RTC_RESET_EN); + if (!AR_SREV_9300_20_OR_LATER(ah)) + ath9k_hw_init_pll(ah, NULL); + } + if (AR_SREV_9100(ah)) + REG_SET_BIT(ah, AR_RTC_RESET, + AR_RTC_RESET_EN); + REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + udelay(50); + + for (i = POWER_UP_TIME / 50; i > 0; i--) { + val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; + if (val == AR_RTC_STATUS_ON) + break; + udelay(50); REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); - udelay(50); - - for (i = POWER_UP_TIME / 50; i > 0; i--) { - val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; - if (val == AR_RTC_STATUS_ON) - break; - udelay(50); - REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, - AR_RTC_FORCE_WAKE_EN); - } - if (i == 0) { - ath_err(ath9k_hw_common(ah), - "Failed to wakeup in %uus\n", - POWER_UP_TIME / 20); - return false; - } + } + if (i == 0) { + ath_err(ath9k_hw_common(ah), + "Failed to wakeup in %uus\n", + POWER_UP_TIME / 20); + return false; } REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); @@ -2146,7 +2141,7 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) { struct ath_common *common = ath9k_hw_common(ah); - int status = true, setChip = true; + int status = true; static const char *modes[] = { "AWAKE", "FULL-SLEEP", @@ -2162,17 +2157,17 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) switch (mode) { case ATH9K_PM_AWAKE: - status = ath9k_hw_set_power_awake(ah, setChip); + status = ath9k_hw_set_power_awake(ah); break; case ATH9K_PM_FULL_SLEEP: if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_set_full_sleep(ah); - ath9k_set_power_sleep(ah, setChip); + ath9k_set_power_sleep(ah); ah->chip_fullsleep = true; break; case ATH9K_PM_NETWORK_SLEEP: - ath9k_set_power_network_sleep(ah, setChip); + ath9k_set_power_network_sleep(ah); break; default: ath_err(common, "Unknown power mode %u\n", mode); -- cgit v1.2.3 From 97ba515a201bc61c7d36c41ad728b0a7debd5d88 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:41 +0530 Subject: ath9k: Add MCI interrupt to debugfs statistics Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 3 +++ drivers/net/wireless/ath/ath9k/debug.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index fde700c4e490..c134ddaa10a1 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -374,6 +374,8 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) sc->debug.stats.istats.dtim++; if (status & ATH9K_INT_TSFOOR) sc->debug.stats.istats.tsfoor++; + if (status & ATH9K_INT_MCI) + sc->debug.stats.istats.mci++; } static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, @@ -418,6 +420,7 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, PR_IS("DTIMSYNC", dtimsync); PR_IS("DTIM", dtim); PR_IS("TSFOOR", tsfoor); + PR_IS("MCI", mci); PR_IS("TOTAL", total); len += snprintf(buf + len, mxlen - len, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index c34da09d9103..d0f851cea43a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -86,6 +86,7 @@ struct ath_interrupt_stats { u32 dtim; u32 bb_watchdog; u32 tsfoor; + u32 mci; /* Sync-cause stats */ u32 sync_cause_all; -- cgit v1.2.3 From 2fd5d35bc87a301d5c10397c5b38a4ec8532a975 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:47 +0530 Subject: ath9k_hw: Fix variable usage Exposed by sparse: ar9003_mci.c:1134:31: warning: Using plain integer as NULL pointer Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 9331d73f69aa..b501bf143437 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1131,7 +1131,7 @@ void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable) if (ar9003_mci_wait_for_gpm(ah, MCI_GPM_BT_CAL_GRANT, 0, 50000)) { ath_dbg(common, MCI, "MCI BT_CAL_GRANT received\n"); } else { - is_reusable = false; + *is_reusable = false; ath_dbg(common, MCI, "MCI BT_CAL_GRANT not received\n"); } } -- cgit v1.2.3 From 4cb54fa3a2456e66d8e3bae663d66f00cbb9e483 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:52 +0530 Subject: ath9k: Remove SC_OP_OFFCHANNEL mac80211's IEEE80211_CONF_OFFCHANNEL can be used directly to track offchannel state. Also pass the correct boolean value to ath9k_hw_startpcureceive(). Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 13 ++++++------- drivers/net/wireless/ath/ath9k/main.c | 11 +++-------- drivers/net/wireless/ath/ath9k/recv.c | 4 ++-- 3 files changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a277cf6f339d..e204648a84fe 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -586,13 +586,12 @@ struct ath_ant_comb { #define SC_OP_INVALID BIT(0) #define SC_OP_BEACONS BIT(1) -#define SC_OP_OFFCHANNEL BIT(2) -#define SC_OP_RXFLUSH BIT(3) -#define SC_OP_TSF_RESET BIT(4) -#define SC_OP_BT_PRIORITY_DETECTED BIT(5) -#define SC_OP_BT_SCAN BIT(6) -#define SC_OP_ANI_RUN BIT(7) -#define SC_OP_PRIM_STA_VIF BIT(8) +#define SC_OP_RXFLUSH BIT(2) +#define SC_OP_TSF_RESET BIT(3) +#define SC_OP_BT_PRIORITY_DETECTED BIT(4) +#define SC_OP_BT_SCAN BIT(5) +#define SC_OP_ANI_RUN BIT(6) +#define SC_OP_PRIM_STA_VIF BIT(7) /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3809fc8ec8a4..d739c8e6a012 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -152,7 +152,7 @@ void ath_start_ani(struct ath_common *common) if (!(sc->sc_flags & SC_OP_ANI_RUN)) return; - if (sc->sc_flags & SC_OP_OFFCHANNEL) + if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) return; common->ani.longcal_timer = timestamp; @@ -282,7 +282,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); - if (!(sc->sc_flags & (SC_OP_OFFCHANNEL)) && start) { + if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { if (sc->sc_flags & SC_OP_BEACONS) ath_set_beacon(sc); @@ -328,7 +328,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan, spin_lock_bh(&sc->sc_pcu_lock); - if (!(sc->sc_flags & SC_OP_OFFCHANNEL)) { + if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) { fastcc = false; caldata = &sc->caldata; } @@ -1626,11 +1626,6 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (ah->curchan) old_pos = ah->curchan - &ah->channels[0]; - if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) - sc->sc_flags |= SC_OP_OFFCHANNEL; - else - sc->sc_flags &= ~SC_OP_OFFCHANNEL; - ath_dbg(common, CONFIG, "Set channel: %d MHz type: %d\n", curchan->center_freq, conf->channel_type); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index e1fcc68124dc..18acdbcbb03f 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -303,7 +303,7 @@ static void ath_edma_start_recv(struct ath_softc *sc) ath_opmode_init(sc); - ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL)); + ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); spin_unlock_bh(&sc->rx.rxbuflock); } @@ -500,7 +500,7 @@ int ath_startrecv(struct ath_softc *sc) start_recv: ath_opmode_init(sc); - ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL)); + ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)); spin_unlock_bh(&sc->rx.rxbuflock); -- cgit v1.2.3 From e6930c4b32aaa30fb9510984509afcc9f61fa39c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 16:27:58 +0530 Subject: ath9k: Use separate operational flags for BTCOEX Also, use atomic operations to check the flags. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 12 ++++++++---- drivers/net/wireless/ath/ath9k/gpio.c | 23 ++++++++++++----------- 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index e204648a84fe..ee946b8fe00c 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -441,12 +441,18 @@ void ath_start_ani(struct ath_common *common); /* BTCOEX */ /**********/ +enum bt_op_flags { + BT_OP_PRIORITY_DETECTED, + BT_OP_SCAN, +}; + struct ath_btcoex { bool hw_timer_enabled; spinlock_t btcoex_lock; struct timer_list period_timer; /* Timer for BT period */ u32 bt_priority_cnt; unsigned long bt_priority_time; + unsigned long op_flags; int bt_stomp_type; /* Types of BT stomping */ u32 btcoex_no_stomp; /* in usec */ u32 btcoex_period; /* in usec */ @@ -588,10 +594,8 @@ struct ath_ant_comb { #define SC_OP_BEACONS BIT(1) #define SC_OP_RXFLUSH BIT(2) #define SC_OP_TSF_RESET BIT(3) -#define SC_OP_BT_PRIORITY_DETECTED BIT(4) -#define SC_OP_BT_SCAN BIT(5) -#define SC_OP_ANI_RUN BIT(6) -#define SC_OP_PRIM_STA_VIF BIT(7) +#define SC_OP_ANI_RUN BIT(4) +#define SC_OP_PRIM_STA_VIF BIT(5) /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 281a9af0f1b6..9397a6d2ed5d 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -132,17 +132,18 @@ static void ath_detect_bt_priority(struct ath_softc *sc) if (time_after(jiffies, btcoex->bt_priority_time + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { - sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN); + clear_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); + clear_bit(BT_OP_SCAN, &btcoex->op_flags); /* Detect if colocated bt started scanning */ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX, "BT scan detected\n"); - sc->sc_flags |= (SC_OP_BT_SCAN | - SC_OP_BT_PRIORITY_DETECTED); + set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); + set_bit(BT_OP_SCAN, &btcoex->op_flags); } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { ath_dbg(ath9k_hw_common(sc->sc_ah), BTCOEX, "BT priority traffic detected\n"); - sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; + set_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags); } btcoex->bt_priority_cnt = 0; @@ -196,7 +197,7 @@ static void ath_btcoex_period_timer(unsigned long data) ath9k_ps_wakeup(sc); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) ath_detect_bt_priority(sc); - is_btscan = sc->sc_flags & SC_OP_BT_SCAN; + is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags); spin_lock_bh(&btcoex->btcoex_lock); @@ -219,8 +220,7 @@ static void ath_btcoex_period_timer(unsigned long data) ath9k_ps_restore(sc); timer_period = btcoex->btcoex_period / 1000; - mod_timer(&btcoex->period_timer, jiffies + - msecs_to_jiffies(timer_period)); + mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period)); } /* @@ -233,14 +233,14 @@ static void ath_btcoex_no_stomp_timer(void *arg) struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath_common *common = ath9k_hw_common(ah); - bool is_btscan = sc->sc_flags & SC_OP_BT_SCAN; ath_dbg(common, BTCOEX, "no stomp timer running\n"); ath9k_ps_wakeup(sc); spin_lock_bh(&btcoex->btcoex_lock); - if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) + if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || + test_bit(BT_OP_SCAN, &btcoex->op_flags)) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); @@ -292,7 +292,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; - sc->sc_flags &= ~(SC_OP_BT_PRIORITY_DETECTED | SC_OP_BT_SCAN); + btcoex->op_flags &= ~(BT_OP_PRIORITY_DETECTED | BT_OP_SCAN); mod_timer(&btcoex->period_timer, jiffies); } @@ -316,12 +316,13 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc) u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) { + struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &sc->btcoex.mci; u16 aggr_limit = 0; if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit) aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4; - else if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED) + else if (test_bit(BT_OP_PRIORITY_DETECTED, &btcoex->op_flags)) aggr_limit = min((max_4ms_framelen * 3) / 8, (u32)ATH_AMPDU_LIMIT_MAX); -- cgit v1.2.3 From 5d294a168012ec9a5258efae0a1282e94691a5d4 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:03 +0530 Subject: ath9k_hw: update ar9462 initval table to fix rx The wrong ADC sample phase that happen between awake and network/full sleep will lead to improper rx noisefloor calibration. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 1d6658e139b5..a10ece0cb6cd 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -958,7 +958,7 @@ static const u32 ar9462_2p0_radio_core[][2] = { {0x0001604c, 0x2699e04f}, {0x00016050, 0x6db6db6c}, {0x00016058, 0x6c200000}, - {0x00016080, 0x00040000}, + {0x00016080, 0x000c0000}, {0x00016084, 0x9a68048c}, {0x00016088, 0x54214514}, {0x0001608c, 0x1203040b}, @@ -981,7 +981,7 @@ static const u32 ar9462_2p0_radio_core[][2] = { {0x00016144, 0x02084080}, {0x00016148, 0x000080c0}, {0x00016280, 0x050a0001}, - {0x00016284, 0x3d841400}, + {0x00016284, 0x3d841418}, {0x00016288, 0x00000000}, {0x0001628c, 0xe3000000}, {0x00016290, 0xa1005080}, -- cgit v1.2.3 From 4f6bd1a8dfcc649912310777a44c49849ae2a63d Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:08 +0530 Subject: ath9k: cleanup MCI indentation Fix bad indentation & if nesting to lose about two levels of unnecessary indentation. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 435 ++++++++++++++-------------- 1 file changed, 215 insertions(+), 220 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index b501bf143437..854dff66a63f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -35,31 +35,30 @@ static int ar9003_mci_wait_for_interrupt(struct ath_hw *ah, u32 address, struct ath_common *common = ath9k_hw_common(ah); while (time_out) { - if (REG_READ(ah, address) & bit_position) { - REG_WRITE(ah, address, bit_position); - - if (address == AR_MCI_INTERRUPT_RX_MSG_RAW) { - if (bit_position & - AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) - ar9003_mci_reset_req_wakeup(ah); - - if (bit_position & - (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING | - AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) - REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, - AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); - - REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, - AR_MCI_INTERRUPT_RX_MSG); - } - break; - } + if (!(REG_READ(ah, address) & bit_position)) { + udelay(10); + time_out -= 10; - udelay(10); - time_out -= 10; + if (time_out < 0) + break; + else + continue; + } + REG_WRITE(ah, address, bit_position); - if (time_out < 0) + if (address != AR_MCI_INTERRUPT_RX_MSG_RAW) break; + + if (bit_position & AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE) + ar9003_mci_reset_req_wakeup(ah); + + if (bit_position & (AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING | + AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) + REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, + AR_MCI_INTERRUPT_REMOTE_SLEEP_UPDATE); + + REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_RX_MSG); + break; } if (time_out <= 0) { @@ -127,14 +126,13 @@ static void ar9003_mci_send_coex_version_query(struct ath_hw *ah, struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; - if (!mci->bt_version_known && - (mci->bt_state != MCI_BT_SLEEP)) { - MCI_GPM_SET_TYPE_OPCODE(payload, - MCI_GPM_COEX_AGENT, - MCI_GPM_COEX_VERSION_QUERY); - ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, - wait_done, true); - } + if (mci->bt_version_known || + (mci->bt_state == MCI_BT_SLEEP)) + return; + + MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, + MCI_GPM_COEX_VERSION_QUERY); + ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); } static void ar9003_mci_send_coex_version_response(struct ath_hw *ah, @@ -158,15 +156,14 @@ static void ar9003_mci_send_coex_wlan_channels(struct ath_hw *ah, struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 *payload = &mci->wlan_channels[0]; - if ((mci->wlan_channels_update == true) && - (mci->bt_state != MCI_BT_SLEEP)) { - MCI_GPM_SET_TYPE_OPCODE(payload, - MCI_GPM_COEX_AGENT, - MCI_GPM_COEX_WLAN_CHANNELS); - ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, - wait_done, true); - MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff); - } + if (!mci->wlan_channels_update || + (mci->bt_state == MCI_BT_SLEEP)) + return; + + MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, + MCI_GPM_COEX_WLAN_CHANNELS); + ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, wait_done, true); + MCI_GPM_SET_TYPE_OPCODE(payload, 0xff, 0xff); } static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah, @@ -174,29 +171,30 @@ static void ar9003_mci_send_coex_bt_status_query(struct ath_hw *ah, { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; - bool query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO | - MCI_GPM_COEX_QUERY_BT_TOPOLOGY)); + bool query_btinfo; - if (mci->bt_state != MCI_BT_SLEEP) { - - MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, - MCI_GPM_COEX_STATUS_QUERY); + if (mci->bt_state == MCI_BT_SLEEP) + return; - *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type; + query_btinfo = !!(query_type & (MCI_GPM_COEX_QUERY_BT_ALL_INFO | + MCI_GPM_COEX_QUERY_BT_TOPOLOGY)); + MCI_GPM_SET_TYPE_OPCODE(payload, MCI_GPM_COEX_AGENT, + MCI_GPM_COEX_STATUS_QUERY); - /* - * If bt_status_query message is not sent successfully, - * then need_flush_btinfo should be set again. - */ - if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, - wait_done, true)) { - if (query_btinfo) - mci->need_flush_btinfo = true; - } + *(((u8 *)payload) + MCI_GPM_COEX_B_BT_BITMAP) = query_type; + /* + * If bt_status_query message is not sent successfully, + * then need_flush_btinfo should be set again. + */ + if (!ar9003_mci_send_message(ah, MCI_GPM, 0, payload, 16, + wait_done, true)) { if (query_btinfo) - mci->query_bt = false; + mci->need_flush_btinfo = true; } + + if (query_btinfo) + mci->query_bt = false; } static void ar9003_mci_send_coex_halt_bt_gpm(struct ath_hw *ah, bool halt, @@ -241,73 +239,73 @@ static void ar9003_mci_prep_interface(struct ath_hw *ah) ar9003_mci_remote_reset(ah, true); ar9003_mci_send_req_wake(ah, true); - if (ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, - AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) { + if (!ar9003_mci_wait_for_interrupt(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING, 500)) + goto clear_redunt; - mci->bt_state = MCI_BT_AWAKE; + mci->bt_state = MCI_BT_AWAKE; - /* - * we don't need to send more remote_reset at this moment. - * If BT receive first remote_reset, then BT HW will - * be cleaned up and will be able to receive req_wake - * and BT HW will respond sys_waking. - * In this case, WLAN will receive BT's HW sys_waking. - * Otherwise, if BT SW missed initial remote_reset, - * that remote_reset will still clean up BT MCI RX, - * and the req_wake will wake BT up, - * and BT SW will respond this req_wake with a remote_reset and - * sys_waking. In this case, WLAN will receive BT's SW - * sys_waking. In either case, BT's RX is cleaned up. So we - * don't need to reply BT's remote_reset now, if any. - * Similarly, if in any case, WLAN can receive BT's sys_waking, - * that means WLAN's RX is also fine. - */ - ar9003_mci_send_sys_waking(ah, true); - udelay(10); + /* + * we don't need to send more remote_reset at this moment. + * If BT receive first remote_reset, then BT HW will + * be cleaned up and will be able to receive req_wake + * and BT HW will respond sys_waking. + * In this case, WLAN will receive BT's HW sys_waking. + * Otherwise, if BT SW missed initial remote_reset, + * that remote_reset will still clean up BT MCI RX, + * and the req_wake will wake BT up, + * and BT SW will respond this req_wake with a remote_reset and + * sys_waking. In this case, WLAN will receive BT's SW + * sys_waking. In either case, BT's RX is cleaned up. So we + * don't need to reply BT's remote_reset now, if any. + * Similarly, if in any case, WLAN can receive BT's sys_waking, + * that means WLAN's RX is also fine. + */ + ar9003_mci_send_sys_waking(ah, true); + udelay(10); - /* - * Set BT priority interrupt value to be 0xff to - * avoid having too many BT PRIORITY interrupts. - */ - REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF); - REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF); - REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF); - REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF); - REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF); + /* + * Set BT priority interrupt value to be 0xff to + * avoid having too many BT PRIORITY interrupts. + */ + REG_WRITE(ah, AR_MCI_BT_PRI0, 0xFFFFFFFF); + REG_WRITE(ah, AR_MCI_BT_PRI1, 0xFFFFFFFF); + REG_WRITE(ah, AR_MCI_BT_PRI2, 0xFFFFFFFF); + REG_WRITE(ah, AR_MCI_BT_PRI3, 0xFFFFFFFF); + REG_WRITE(ah, AR_MCI_BT_PRI, 0X000000FF); - /* - * A contention reset will be received after send out - * sys_waking. Also BT priority interrupt bits will be set. - * Clear those bits before the next step. - */ + /* + * A contention reset will be received after send out + * sys_waking. Also BT priority interrupt bits will be set. + * Clear those bits before the next step. + */ - REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, - AR_MCI_INTERRUPT_RX_MSG_CONT_RST); - REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, - AR_MCI_INTERRUPT_BT_PRI); + REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + AR_MCI_INTERRUPT_RX_MSG_CONT_RST); + REG_WRITE(ah, AR_MCI_INTERRUPT_RAW, AR_MCI_INTERRUPT_BT_PRI); - if (mci->is_2g) { - ar9003_mci_send_lna_transfer(ah, true); - udelay(5); - } + if (mci->is_2g) { + ar9003_mci_send_lna_transfer(ah, true); + udelay(5); + } - if ((mci->is_2g && !mci->update_2g5g)) { - if (ar9003_mci_wait_for_interrupt(ah, - AR_MCI_INTERRUPT_RX_MSG_RAW, - AR_MCI_INTERRUPT_RX_MSG_LNA_INFO, - mci_timeout)) - ath_dbg(common, MCI, - "MCI WLAN has control over the LNA & BT obeys it\n"); - else - ath_dbg(common, MCI, - "MCI BT didn't respond to LNA_TRANS\n"); - } + if ((mci->is_2g && !mci->update_2g5g)) { + if (ar9003_mci_wait_for_interrupt(ah, + AR_MCI_INTERRUPT_RX_MSG_RAW, + AR_MCI_INTERRUPT_RX_MSG_LNA_INFO, + mci_timeout)) + ath_dbg(common, MCI, + "MCI WLAN has control over the LNA & BT obeys it\n"); + else + ath_dbg(common, MCI, + "MCI BT didn't respond to LNA_TRANS\n"); } +clear_redunt: /* Clear the extra redundant SYS_WAKING from BT */ if ((mci->bt_state == MCI_BT_AWAKE) && - (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, - AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) && + (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING)) && (REG_READ_FIELD(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) == 0)) { REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, @@ -614,9 +612,9 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, } break; } - } else if ((recv_type == gpm_type) && (recv_opcode == gpm_opcode)) { + } else if ((recv_type == gpm_type) && + (recv_opcode == gpm_opcode)) break; - } /* * check if it's cal_grant @@ -730,38 +728,38 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (!IS_CHAN_2GHZ(chan) || (mci_hw->bt_state != MCI_BT_SLEEP)) goto exit; - if (ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) || - ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) { + if (!ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET) && + !ar9003_mci_check_int(ah, AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)) + goto exit; - /* - * BT is sleeping. Check if BT wakes up during - * WLAN calibration. If BT wakes up during - * WLAN calibration, need to go through all - * message exchanges again and recal. - */ - REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, - AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | - AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE); + /* + * BT is sleeping. Check if BT wakes up during + * WLAN calibration. If BT wakes up during + * WLAN calibration, need to go through all + * message exchanges again and recal. + */ + REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + (AR_MCI_INTERRUPT_RX_MSG_REMOTE_RESET | + AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE)); - ar9003_mci_remote_reset(ah, true); - ar9003_mci_send_sys_waking(ah, true); - udelay(1); + ar9003_mci_remote_reset(ah, true); + ar9003_mci_send_sys_waking(ah, true); + udelay(1); - if (IS_CHAN_2GHZ(chan)) - ar9003_mci_send_lna_transfer(ah, true); + if (IS_CHAN_2GHZ(chan)) + ar9003_mci_send_lna_transfer(ah, true); - mci_hw->bt_state = MCI_BT_AWAKE; + mci_hw->bt_state = MCI_BT_AWAKE; - if (caldata) { - caldata->done_txiqcal_once = false; - caldata->done_txclcal_once = false; - caldata->rtt_done = false; - } + if (caldata) { + caldata->done_txiqcal_once = false; + caldata->done_txclcal_once = false; + caldata->rtt_done = false; + } - if (!ath9k_hw_init_cal(ah, chan)) - return -EIO; + if (!ath9k_hw_init_cal(ah, chan)) + return -EIO; - } exit: ar9003_mci_enable_interrupt(ah); return 0; @@ -797,29 +795,27 @@ static void ar9003_mci_osla_setup(struct ath_hw *ah, bool enable) struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 thresh; - if (enable) { - REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, - AR_MCI_SCHD_TABLE_2_HW_BASED, 1); - REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, - AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); - - if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { - thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); - REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_AGGR_THRESH, thresh); - REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1); - } else { - REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0); - } - - REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1); - } else { + if (!enable) { REG_CLR_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); + return; } + REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, AR_MCI_SCHD_TABLE_2_HW_BASED, 1); + REG_RMW_FIELD(ah, AR_MCI_SCHD_TABLE_2, + AR_MCI_SCHD_TABLE_2_MEM_BASED, 1); + + if (!(mci->config & ATH_MCI_CONFIG_DISABLE_AGGR_THRESH)) { + thresh = MS(mci->config, ATH_MCI_CONFIG_AGGR_THRESH); + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, + AR_BTCOEX_CTRL_AGGR_THRESH, thresh); + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, + AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 1); + } else + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, + AR_BTCOEX_CTRL_TIME_TO_NEXT_BT_THRESH_EN, 0); + + REG_RMW_FIELD(ah, AR_BTCOEX_CTRL, + AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN, 1); } void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, @@ -942,26 +938,27 @@ static void ar9003_mci_send_2g5g_status(struct ath_hw *ah, bool wait_done) struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 new_flags, to_set, to_clear; - if (mci->update_2g5g && (mci->bt_state != MCI_BT_SLEEP)) { - if (mci->is_2g) { - new_flags = MCI_2G_FLAGS; - to_clear = MCI_2G_FLAGS_CLEAR_MASK; - to_set = MCI_2G_FLAGS_SET_MASK; - } else { - new_flags = MCI_5G_FLAGS; - to_clear = MCI_5G_FLAGS_CLEAR_MASK; - to_set = MCI_5G_FLAGS_SET_MASK; - } + if (!mci->update_2g5g || (mci->bt_state == MCI_BT_SLEEP)) + return; - if (to_clear) - ar9003_mci_send_coex_bt_flags(ah, wait_done, + if (mci->is_2g) { + new_flags = MCI_2G_FLAGS; + to_clear = MCI_2G_FLAGS_CLEAR_MASK; + to_set = MCI_2G_FLAGS_SET_MASK; + } else { + new_flags = MCI_5G_FLAGS; + to_clear = MCI_5G_FLAGS_CLEAR_MASK; + to_set = MCI_5G_FLAGS_SET_MASK; + } + + if (to_clear) + ar9003_mci_send_coex_bt_flags(ah, wait_done, MCI_GPM_COEX_BT_FLAGS_CLEAR, to_clear); - if (to_set) - ar9003_mci_send_coex_bt_flags(ah, wait_done, + if (to_set) + ar9003_mci_send_coex_bt_flags(ah, wait_done, MCI_GPM_COEX_BT_FLAGS_SET, to_set); - } } static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header, @@ -1017,34 +1014,34 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool wait_done) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - if (mci->update_2g5g) { - if (mci->is_2g) { - ar9003_mci_send_2g5g_status(ah, true); - ar9003_mci_send_lna_transfer(ah, true); - udelay(5); + if (!mci->update_2g5g) + return; - REG_CLR_BIT(ah, AR_MCI_TX_CTRL, - AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); - REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, - AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); + if (mci->is_2g) { + ar9003_mci_send_2g5g_status(ah, true); + ar9003_mci_send_lna_transfer(ah, true); + udelay(5); - if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) { - REG_SET_BIT(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); - } - } else { - ar9003_mci_send_lna_take(ah, true); - udelay(5); - - REG_SET_BIT(ah, AR_MCI_TX_CTRL, - AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); - REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, - AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); - REG_CLR_BIT(ah, AR_BTCOEX_CTRL, + REG_CLR_BIT(ah, AR_MCI_TX_CTRL, + AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); + REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, + AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); + + if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) + REG_SET_BIT(ah, AR_BTCOEX_CTRL, AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); + } else { + ar9003_mci_send_lna_take(ah, true); + udelay(5); - ar9003_mci_send_2g5g_status(ah, true); - } + REG_SET_BIT(ah, AR_MCI_TX_CTRL, + AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); + REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, + AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); + REG_CLR_BIT(ah, AR_BTCOEX_CTRL, + AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); + + ar9003_mci_send_2g5g_status(ah, true); } } @@ -1258,12 +1255,12 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) } if (p_data) *p_data = more_gpm; - } + } - if (value != MCI_GPM_INVALID) - value <<= 4; + if (value != MCI_GPM_INVALID) + value <<= 4; - break; + break; case MCI_STATE_LAST_SCHD_MSG_OFFSET: value = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_LAST_SCHD_MSG_INDEX); @@ -1358,24 +1355,22 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) ar9003_mci_send_coex_bt_status_query(ah, true, query_type); break; case MCI_STATE_NEED_FLUSH_BT_INFO: - /* - * btcoex_hw.mci.unhalt_bt_gpm means whether it's - * needed to send UNHALT message. It's set whenever - * there's a request to send HALT message. - * mci_halted_bt_gpm means whether HALT message is sent - * out successfully. - * - * Checking (mci_unhalt_bt_gpm == false) instead of - * checking (ah->mci_halted_bt_gpm == false) will make - * sure currently is in UNHALT-ed mode and BT can - * respond to status query. - */ - value = (!mci->unhalt_bt_gpm && - mci->need_flush_btinfo) ? 1 : 0; - if (p_data) - mci->need_flush_btinfo = - (*p_data != 0) ? true : false; - break; + /* + * btcoex_hw.mci.unhalt_bt_gpm means whether it's + * needed to send UNHALT message. It's set whenever + * there's a request to send HALT message. + * mci_halted_bt_gpm means whether HALT message is sent + * out successfully. + * + * Checking (mci_unhalt_bt_gpm == false) instead of + * checking (ah->mci_halted_bt_gpm == false) will make + * sure currently is in UNHALT-ed mode and BT can + * respond to status query. + */ + value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; + if (p_data) + mci->need_flush_btinfo = (*p_data != 0) ? true : false; + break; case MCI_STATE_RECOVER_RX: ar9003_mci_prep_interface(ah); mci->query_bt = true; -- cgit v1.2.3 From 4fb7175bbd6a32899617b96b0b73c2ad651dfd03 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:14 +0530 Subject: ath9k_hw: fix IQ calibration chain index The chain index to perform IQ calibration is counted to number of valid tx chains and then used for indexing chain specific registers. If the chainmask is set to 0x2 (i.e chain 1 only), still it accesses chain 0 registers for chain 1. So use real chain index instead sequential one. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_calib.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index f83a4a2e1da2..d7deb8c9f299 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -653,7 +653,6 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, } static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, - u8 num_chains, struct coeff *coeff, bool is_reusable) { @@ -677,7 +676,9 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, } /* Load the average of 2 passes */ - for (i = 0; i < num_chains; i++) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->txchainmask & (1 << i))) + continue; nmeasurement = REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0, AR_PHY_CALIBRATED_GAINS_0); @@ -767,16 +768,13 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) }; struct coeff coeff; s32 iq_res[6]; - u8 num_chains = 0; int i, im, j; int nmeasurement; for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (ah->txchainmask & (1 << i)) - num_chains++; - } + if (!(ah->txchainmask & (1 << i))) + continue; - for (i = 0; i < num_chains; i++) { nmeasurement = REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_STATUS_B0, AR_PHY_CALIBRATED_GAINS_0); @@ -839,8 +837,7 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) coeff.phs_coeff[i][im] -= 128; } } - ar9003_hw_tx_iqcal_load_avg_2_passes(ah, num_chains, - &coeff, is_reusable); + ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable); return; -- cgit v1.2.3 From 9dc08eceb7491331e81acde625e0d9c82aa86a16 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:20 +0530 Subject: ath9k_hw: program BT to control SPDT program BT to control SPDT everytime while loading switch table from eeprom. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_eeprom.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index f794828c08e2..2cdf82bdb11d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3613,6 +3613,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) value = ar9003_switch_com_spdt_get(ah, is2ghz); REG_RMW_FIELD(ah, AR_PHY_GLB_CONTROL, AR_SWITCH_TABLE_COM_SPDT_ALL, value); + REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_SPDT_ENABLE); } value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz); -- cgit v1.2.3 From c8b6fbe1f1d38aa19882263d6b0c644269e94244 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:25 +0530 Subject: ath9k_hw: configure ar9462 switching regulator Enable WLAN and BT mode for switching regulator discontinuous orverride for AR9462 chips. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 4 ++++ drivers/net/wireless/ath/ath9k/hw.h | 1 - drivers/net/wireless/ath/ath9k/reg.h | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 11abb972be1f..d6baf69cdc14 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -676,6 +676,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, if (chan->channel == 2484) ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1); + if (AR_SREV_9462(ah)) + REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, + AR_GLB_SWREG_DISCONT_EN_BT_WLAN); + ah->modes_index = modesIndex; ar9003_hw_override_ini(ah); ar9003_hw_set_channel_regs(ah, chan); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 3740aab864f4..03d590924c64 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -824,7 +824,6 @@ struct ath_hw { struct ar5416IniArray ini_japan2484; struct ar5416IniArray iniModes_9271_ANI_reg; struct ar5416IniArray ini_radio_post_sys2ant; - struct ar5416IniArray ini_BTCOEX_MAX_TXPWR; struct ar5416IniArray iniMac[ATH_INI_NUM_SPLIT]; struct ar5416IniArray iniBB[ATH_INI_NUM_SPLIT]; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 458f81b4a7cb..560d6effac7a 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -2211,5 +2211,7 @@ enum { #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT 0x00000fff #define AR_BTCOEX_CTRL3_CONT_INFO_TIMEOUT_S 0 +#define AR_GLB_SWREG_DISCONT_MODE 0x2002c +#define AR_GLB_SWREG_DISCONT_EN_BT_WLAN 0x3 #endif -- cgit v1.2.3 From 99922a45e96b22df387823ad5ecfe4dc26a96c9e Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:31 +0530 Subject: ath9k_hw: fix BT RF performance When software rfkill is triggered, before put the chip in reset state, give LNA and SPDT control to BT to make sure BT can have good RF performance. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 + drivers/net/wireless/ath/ath9k/hw.c | 3 +++ 3 files changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 854dff66a63f..8b09a8239c1c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1391,3 +1391,19 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) return value; } EXPORT_SYMBOL(ar9003_mci_state); + +void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + + ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n"); + + REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); + mci->is_2g = false; + mci->update_2g5g = true; + ar9003_mci_send_2g5g_status(ah, true); + + /* Force another 2g5g update at next scanning */ + mci->update_2g5g = true; +} diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 652ab8c39e2b..f4a6a4450ba8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -266,6 +266,7 @@ void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, void ar9003_mci_cleanup(struct ath_hw *ah); void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr); +void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah); /* * These functions are used by ath9k_hw. diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 9f0f5f74f17a..98478fd5d284 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2737,6 +2737,9 @@ EXPORT_SYMBOL(ath9k_hw_setrxfilter); bool ath9k_hw_phy_disable(struct ath_hw *ah) { + if (ath9k_hw_mci_is_enabled(ah)) + ar9003_mci_bt_gain_ctrl(ah); + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) return false; -- cgit v1.2.3 From 0603143e47a34d0485a13fd7f46e56a97e687e34 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:36 +0530 Subject: ath9k: BT coex performance tuning for AR9462 Use smaller aggregation limit and increasing aggregation threshold for osla that could improve wlan performance when there is HID profile. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 -- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 - drivers/net/wireless/ath/ath9k/mci.c | 54 +++++++++++++++++++---------- 3 files changed, 35 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 8b09a8239c1c..b1ced2a76da3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1381,9 +1381,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) case MCI_STATE_NEED_FTP_STOMP: value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); break; - case MCI_STATE_NEED_TUNING: - value = !(mci->config & ATH_MCI_CONFIG_DISABLE_TUNING); - break; default: break; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index f4a6a4450ba8..10282e2bcdc9 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -212,7 +212,6 @@ enum mci_state_type { MCI_STATE_SET_CONCUR_TX_PRI, MCI_STATE_RECOVER_RX, MCI_STATE_NEED_FTP_STOMP, - MCI_STATE_NEED_TUNING, MCI_STATE_DEBUG, MCI_STATE_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 7b553f6a97b1..49137f477b05 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -116,42 +116,58 @@ static void ath_mci_update_scheme(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; + struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; struct ath_mci_profile_info *info; u32 num_profile = NUM_PROF(mci); + if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) + goto skip_tuning; + if (num_profile == 1) { info = list_first_entry(&mci->info, struct ath_mci_profile_info, list); - if (mci->num_sco && info->T == 12) { - mci->aggr_limit = 8; + if (mci->num_sco) { + if (info->T == 12) + mci->aggr_limit = 8; + else if (info->T == 6) { + mci->aggr_limit = 6; + btcoex->duty_cycle = 30; + } ath_dbg(common, MCI, - "Single SCO, aggregation limit 2 ms\n"); - } else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) && - !info->master) { - btcoex->btcoex_period = 60; + "Single SCO, aggregation limit %d 1/4 ms\n", + mci->aggr_limit); + } else if (mci->num_pan || mci->num_other_acl) { + /* + * For single PAN/FTP profile, allocate 35% for BT + * to improve WLAN throughput. + */ + btcoex->duty_cycle = 35; + btcoex->btcoex_period = 53; ath_dbg(common, MCI, - "Single slave PAN/FTP, bt period 60 ms\n"); - } else if ((info->type == MCI_GPM_COEX_PROFILE_HID) && - (info->T > 0 && info->T < 50) && - (info->A > 1 || info->W > 1)) { + "Single PAN/FTP bt period %d ms dutycycle %d\n", + btcoex->duty_cycle, btcoex->btcoex_period); + } else if (mci->num_hid) { btcoex->duty_cycle = 30; - mci->aggr_limit = 8; + mci->aggr_limit = 6; ath_dbg(common, MCI, "Multiple attempt/timeout single HID " - "aggregation limit 2 ms dutycycle 30%%\n"); + "aggregation limit 1.5 ms dutycycle 30%%\n"); } - } else if ((num_profile == 2) && (mci->num_hid == 2)) { - btcoex->duty_cycle = 30; - mci->aggr_limit = 8; - ath_dbg(common, MCI, - "Two HIDs aggregation limit 2 ms dutycycle 30%%\n"); - } else if (num_profile > 3) { + } else if (num_profile == 2) { + if (mci->num_hid == 2) + btcoex->duty_cycle = 30; mci->aggr_limit = 6; ath_dbg(common, MCI, - "Three or more profiles aggregation limit 1.5 ms\n"); + "Two BT profiles aggr limit 1.5 ms dutycycle %d%%\n", + btcoex->duty_cycle); + } else if (num_profile >= 3) { + mci->aggr_limit = 4; + ath_dbg(common, MCI, + "Three or more profiles aggregation limit 1 ms\n"); } +skip_tuning: if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) { if (IS_CHAN_HT(sc->sc_ah->curchan)) ath_mci_adjust_aggr_limit(btcoex); -- cgit v1.2.3 From c9ae6ab4c7d7aa29f9607ac69daafbc241fc123e Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:41 +0530 Subject: ath: do not update cycle counters with sleep mode When the chip is waking up from sleep state, the cycle counters might have incorrect readings. So it is better not to update those readings with software counters. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index d739c8e6a012..623b04f63fe4 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -101,6 +101,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc) spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); memset(&common->cc_survey, 0, sizeof(common->cc_survey)); + memset(&common->cc_ani, 0, sizeof(common->cc_ani)); spin_unlock(&common->cc_lock); } -- cgit v1.2.3 From 153dccd467b818b1dd3a6801b14e94a7a48ef859 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:47 +0530 Subject: ath9k_hw: fix power state for MCI Program MCI related power registers only if MCI interrupts are enabled. This could help to reduce power consumptions when WLAN alone is enabled in BT coex chips. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 98478fd5d284..45e670087e1c 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2009,12 +2009,9 @@ static void ath9k_set_power_sleep(struct ath_hw *ah) REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (AR_SREV_9462(ah)) { - REG_WRITE(ah, AR_TIMER_MODE, - REG_READ(ah, AR_TIMER_MODE) & 0xFFFFFF00); - REG_WRITE(ah, AR_NDP2_TIMER_MODE, - REG_READ(ah, AR_NDP2_TIMER_MODE) & 0xFFFFFF00); - REG_WRITE(ah, AR_SLP32_INC, - REG_READ(ah, AR_SLP32_INC) & 0xFFF00000); + REG_CLR_BIT(ah, AR_TIMER_MODE, 0xff); + REG_CLR_BIT(ah, AR_NDP2_TIMER_MODE, 0xff); + REG_CLR_BIT(ah, AR_SLP32_INC, 0xfffff); /* xxx Required for WLAN only case ? */ REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, 0); udelay(100); @@ -2026,7 +2023,7 @@ static void ath9k_set_power_sleep(struct ath_hw *ah) */ REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); - if (AR_SREV_9462(ah)) + if (ath9k_hw_mci_is_enabled(ah)) udelay(100); if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) @@ -2051,7 +2048,6 @@ static void ath9k_set_power_sleep(struct ath_hw *ah) static void ath9k_set_power_network_sleep(struct ath_hw *ah) { struct ath9k_hw_capabilities *pCap = &ah->caps; - u32 val; REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); @@ -2070,19 +2066,16 @@ static void ath9k_set_power_network_sleep(struct ath_hw *ah) * SYS_WAKING and SYS_SLEEPING messages which will make * BT CPU to busy to process. */ - if (AR_SREV_9462(ah)) { - val = REG_READ(ah, AR_MCI_INTERRUPT_RX_MSG_EN) & - ~AR_MCI_INTERRUPT_RX_HW_MSG_MASK; - REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_EN, val); - } + if (ath9k_hw_mci_is_enabled(ah)) + REG_CLR_BIT(ah, AR_MCI_INTERRUPT_RX_MSG_EN, + AR_MCI_INTERRUPT_RX_HW_MSG_MASK); /* * Clear the RTC force wake bit to allow the * mac to go to sleep. */ - REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, - AR_RTC_FORCE_WAKE_EN); + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); - if (AR_SREV_9462(ah)) + if (ath9k_hw_mci_is_enabled(ah)) udelay(30); } -- cgit v1.2.3 From 6995fb805ed5de852a7d49413730980bc7173e82 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 4 Jun 2012 16:28:52 +0530 Subject: ath9k: improve BT FTP/PAN performance When BT FTP/PAN transmits while WLAN is idle, the one of 9462 chain often picks up BT's tx signal and starts receiving. If the current weight is set to be higher than BT tx, BT tx will be aborted and this also degrades BT performance. Hence lower WLAN rx priority in this case only when there are no WLAN traffic. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 2 ++ drivers/net/wireless/ath/ath9k/btcoex.c | 10 ++++++++-- drivers/net/wireless/ath/ath9k/btcoex.h | 4 ++++ drivers/net/wireless/ath/ath9k/gpio.c | 13 +++++++++++++ drivers/net/wireless/ath/ath9k/recv.c | 1 + 5 files changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ee946b8fe00c..ce10d1a9e493 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -307,6 +307,7 @@ struct ath_rx { u8 defant; u8 rxotherant; u32 *rxlink; + u32 num_pkts; unsigned int rxfilter; spinlock_t rxbuflock; struct list_head rxbuf; @@ -458,6 +459,7 @@ struct ath_btcoex { u32 btcoex_period; /* in usec */ u32 btscan_no_stomp; /* in usec */ u32 duty_cycle; + u32 bt_wait_time; struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; }; diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 1ca6da80d4ad..acd437384fe4 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -336,10 +336,16 @@ static void ar9003_btcoex_bt_stomp(struct ath_hw *ah, enum ath_stomp_type stomp_type) { struct ath_btcoex_hw *btcoex = &ah->btcoex_hw; - const u32 *weight = AR_SREV_9462(ah) ? ar9003_wlan_weights[stomp_type] : - ar9462_wlan_weights[stomp_type]; + const u32 *weight = ar9003_wlan_weights[stomp_type]; int i; + if (AR_SREV_9462(ah)) { + if ((stomp_type == ATH_BTCOEX_STOMP_LOW) && + btcoex->mci.stomp_ftp) + stomp_type = ATH_BTCOEX_STOMP_LOW_FTP; + weight = ar9462_wlan_weights[stomp_type]; + } + for (i = 0; i < AR9300_NUM_WLAN_WEIGHTS; i++) { btcoex->bt_weight[i] = AR9300_BT_WGHT; btcoex->wlan_weight[i] = weight[i]; diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 3a1e1cfabd5e..20092f98658f 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -36,6 +36,9 @@ #define ATH_BT_CNT_THRESHOLD 3 #define ATH_BT_CNT_SCAN_THRESHOLD 15 +#define ATH_BTCOEX_RX_WAIT_TIME 100 +#define ATH_BTCOEX_STOMP_FTP_THRESH 5 + #define AR9300_NUM_BT_WEIGHTS 4 #define AR9300_NUM_WLAN_WEIGHTS 4 /* Defines the BT AR_BT_COEX_WGHT used */ @@ -80,6 +83,7 @@ struct ath9k_hw_mci { u8 bt_ver_major; u8 bt_ver_minor; u8 bt_state; + u8 stomp_ftp; }; struct ath_btcoex_hw { diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 9397a6d2ed5d..af6d27350291 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -191,6 +191,7 @@ static void ath_btcoex_period_timer(unsigned long data) struct ath_softc *sc = (struct ath_softc *) data; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; u32 timer_period; bool is_btscan; @@ -199,6 +200,18 @@ static void ath_btcoex_period_timer(unsigned long data) ath_detect_bt_priority(sc); is_btscan = test_bit(BT_OP_SCAN, &btcoex->op_flags); + btcoex->bt_wait_time += btcoex->btcoex_period; + if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { + if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP, NULL) && + (mci->num_pan || mci->num_other_acl)) + ah->btcoex_hw.mci.stomp_ftp = + (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH); + else + ah->btcoex_hw.mci.stomp_ftp = false; + btcoex->bt_wait_time = 0; + sc->rx.num_pkts = 0; + } + spin_lock_bh(&btcoex->btcoex_lock); ath9k_hw_btcoex_bt_stomp(ah, is_btscan ? ATH_BTCOEX_STOMP_ALL : diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 18acdbcbb03f..611be4f934e7 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -1841,6 +1841,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) else rs.is_mybeacon = false; + sc->rx.num_pkts++; ath_debug_stat_rx(sc, &rs); /* -- cgit v1.2.3 From ef1b6cd9a1ba06a3daf9a03aa8f25d52d1f2c31a Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:23:37 +0530 Subject: ath9k: Group link monitoring logic Add link.c and move all the link/connection monitoring code to it. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Makefile | 3 +- drivers/net/wireless/ath/ath9k/ath9k.h | 13 +- drivers/net/wireless/ath/ath9k/link.c | 497 ++++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/main.c | 431 --------------------------- drivers/net/wireless/ath/ath9k/xmit.c | 46 +-- 5 files changed, 511 insertions(+), 479 deletions(-) create mode 100644 drivers/net/wireless/ath/ath9k/link.c (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 3f0b84723789..4d60fa84cbb4 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -3,7 +3,8 @@ ath9k-y += beacon.o \ init.o \ main.o \ recv.o \ - xmit.o + xmit.o \ + link.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ce10d1a9e493..d804416092ce 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -326,6 +326,9 @@ int ath_rx_init(struct ath_softc *sc, int nbufs); void ath_rx_cleanup(struct ath_softc *sc); int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp); struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype); +void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq); +void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq); +void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq); void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx); void ath_draintxq(struct ath_softc *sc, @@ -415,9 +418,9 @@ int ath_beaconq_config(struct ath_softc *sc); void ath_set_beacon(struct ath_softc *sc); void ath9k_set_beaconing_status(struct ath_softc *sc, bool status); -/*******/ -/* ANI */ -/*******/ +/*******************/ +/* Link Monitoring */ +/*******************/ #define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ #define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ @@ -429,6 +432,7 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status); #define ATH_PAPRD_TIMEOUT 100 /* msecs */ +void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); void ath_hw_check(struct work_struct *work); void ath_hw_pll_work(struct work_struct *work); @@ -437,6 +441,8 @@ void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon); void ath_paprd_calibrate(struct work_struct *work); void ath_ani_calibrate(unsigned long data); void ath_start_ani(struct ath_common *common); +int ath_update_survey_stats(struct ath_softc *sc); +void ath_update_survey_nf(struct ath_softc *sc, int channel); /**********/ /* BTCOEX */ @@ -741,5 +747,4 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ath9k_vif_iter_data *iter_data); - #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c new file mode 100644 index 000000000000..7368b9630b99 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +/* + * TX polling - checks if the TX engine is stuck somewhere + * and issues a chip reset if so. + */ +void ath_tx_complete_poll_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + tx_complete_work.work); + struct ath_txq *txq; + int i; + bool needreset = false; +#ifdef CONFIG_ATH9K_DEBUGFS + sc->tx_complete_poll_work_seen++; +#endif + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) { + txq = &sc->tx.txq[i]; + ath_txq_lock(sc, txq); + if (txq->axq_depth) { + if (txq->axq_tx_inprogress) { + needreset = true; + ath_txq_unlock(sc, txq); + break; + } else { + txq->axq_tx_inprogress = true; + } + } + ath_txq_unlock_complete(sc, txq); + } + + if (needreset) { + ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, + "tx hung, resetting the chip\n"); + RESET_STAT_INC(sc, RESET_TYPE_TX_HANG); + ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + } + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, + msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); +} + +/* + * Checks if the BB/MAC is hung. + */ +void ath_hw_check(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + unsigned long flags; + int busy; + u8 is_alive, nbeacon = 1; + + ath9k_ps_wakeup(sc); + is_alive = ath9k_hw_check_alive(sc->sc_ah); + + if (is_alive && !AR_SREV_9300(sc->sc_ah)) + goto out; + else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { + ath_dbg(common, RESET, + "DCU stuck is detected. Schedule chip reset\n"); + RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG); + goto sched_reset; + } + + spin_lock_irqsave(&common->cc_lock, flags); + busy = ath_update_survey_stats(sc); + spin_unlock_irqrestore(&common->cc_lock, flags); + + ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", + busy, sc->hw_busy_count + 1); + if (busy >= 99) { + if (++sc->hw_busy_count >= 3) { + RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); + goto sched_reset; + } + } else if (busy >= 0) { + sc->hw_busy_count = 0; + nbeacon = 3; + } + + ath_start_rx_poll(sc, nbeacon); + goto out; + +sched_reset: + ieee80211_queue_work(sc->hw, &sc->hw_reset_work); +out: + ath9k_ps_restore(sc); +} + +/* + * PLL-WAR for AR9485. + */ +static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) +{ + static int count; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + if (pll_sqsum >= 0x40000) { + count++; + if (count == 3) { + /* Rx is hung for more than 500ms. Reset it */ + ath_dbg(common, RESET, "Possible RX hang, resetting\n"); + RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG); + ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + count = 0; + } + } else + count = 0; +} + +void ath_hw_pll_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + hw_pll_work.work); + u32 pll_sqsum; + + if (AR_SREV_9485(sc->sc_ah)) { + ath9k_ps_wakeup(sc); + pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); + ath9k_ps_restore(sc); + ath_hw_pll_rx_hang_check(sc, pll_sqsum); + ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); + } +} + +/* + * RX Polling - monitors baseband hangs. + */ +void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) +{ + if (!AR_SREV_9300(sc->sc_ah)) + return; + + if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) + return; + + mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies + (nbeacon * sc->cur_beacon_conf.beacon_interval)); +} + +void ath_rx_poll(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + + ieee80211_queue_work(sc->hw, &sc->hw_check_work); +} + +/* + * PA Pre-distortion. + */ +static void ath_paprd_activate(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_hw_cal_data *caldata = ah->caldata; + int chain; + + if (!caldata || !caldata->paprd_done) + return; + + ath9k_ps_wakeup(sc); + ar9003_paprd_enable(ah, false); + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->txchainmask & BIT(chain))) + continue; + + ar9003_paprd_populate_single_table(ah, caldata, chain); + } + + ar9003_paprd_enable(ah, true); + ath9k_ps_restore(sc); +} + +static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_tx_control txctl; + int time_left; + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[WME_AC_BE]; + + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->band = hw->conf.channel->band; + tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; + tx_info->control.rates[0].idx = 0; + tx_info->control.rates[0].count = 1; + tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; + tx_info->control.rates[1].idx = -1; + + init_completion(&sc->paprd_complete); + txctl.paprd = BIT(chain); + + if (ath_tx_start(hw, skb, &txctl) != 0) { + ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); + dev_kfree_skb_any(skb); + return false; + } + + time_left = wait_for_completion_timeout(&sc->paprd_complete, + msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); + + if (!time_left) + ath_dbg(common, CALIBRATE, + "Timeout waiting for paprd training on TX chain %d\n", + chain); + + return !!time_left; +} + +void ath_paprd_calibrate(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); + int ftype; + int chain_ok = 0; + int chain; + int len = 1800; + + if (!caldata) + return; + + ath9k_ps_wakeup(sc); + + if (ar9003_paprd_init_table(ah) < 0) + goto fail_paprd; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + goto fail_paprd; + + skb_put(skb, len); + memset(skb->data, 0, len); + hdr = (struct ieee80211_hdr *)skb->data; + ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; + hdr->frame_control = cpu_to_le16(ftype); + hdr->duration_id = cpu_to_le16(10); + memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->txchainmask & BIT(chain))) + continue; + + chain_ok = 0; + + ath_dbg(common, CALIBRATE, + "Sending PAPRD frame for thermal measurement on chain %d\n", + chain); + if (!ath_paprd_send_frame(sc, skb, chain)) + goto fail_paprd; + + ar9003_paprd_setup_gain_table(ah, chain); + + ath_dbg(common, CALIBRATE, + "Sending PAPRD training frame on chain %d\n", chain); + if (!ath_paprd_send_frame(sc, skb, chain)) + goto fail_paprd; + + if (!ar9003_paprd_is_done(ah)) { + ath_dbg(common, CALIBRATE, + "PAPRD not yet done on chain %d\n", chain); + break; + } + + if (ar9003_paprd_create_curve(ah, caldata, chain)) { + ath_dbg(common, CALIBRATE, + "PAPRD create curve failed on chain %d\n", + chain); + break; + } + + chain_ok = 1; + } + kfree_skb(skb); + + if (chain_ok) { + caldata->paprd_done = true; + ath_paprd_activate(sc); + } + +fail_paprd: + ath9k_ps_restore(sc); +} + +/* + * ANI performs periodic noise floor calibration + * that is used to adjust and optimize the chip performance. This + * takes environmental changes (location, temperature) into account. + * When the task is complete, it reschedules itself depending on the + * appropriate interval that was calculated. + */ +void ath_ani_calibrate(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + bool longcal = false; + bool shortcal = false; + bool aniflag = false; + unsigned int timestamp = jiffies_to_msecs(jiffies); + u32 cal_interval, short_cal_interval, long_cal_interval; + unsigned long flags; + + if (ah->caldata && ah->caldata->nfcal_interference) + long_cal_interval = ATH_LONG_CALINTERVAL_INT; + else + long_cal_interval = ATH_LONG_CALINTERVAL; + + short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? + ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; + + /* Only calibrate if awake */ + if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) + goto set_timer; + + ath9k_ps_wakeup(sc); + + /* Long calibration runs independently of short calibration. */ + if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { + longcal = true; + common->ani.longcal_timer = timestamp; + } + + /* Short calibration applies only while caldone is false */ + if (!common->ani.caldone) { + if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { + shortcal = true; + common->ani.shortcal_timer = timestamp; + common->ani.resetcal_timer = timestamp; + } + } else { + if ((timestamp - common->ani.resetcal_timer) >= + ATH_RESTART_CALINTERVAL) { + common->ani.caldone = ath9k_hw_reset_calvalid(ah); + if (common->ani.caldone) + common->ani.resetcal_timer = timestamp; + } + } + + /* Verify whether we must check ANI */ + if (sc->sc_ah->config.enable_ani + && (timestamp - common->ani.checkani_timer) >= + ah->config.ani_poll_interval) { + aniflag = true; + common->ani.checkani_timer = timestamp; + } + + /* Call ANI routine if necessary */ + if (aniflag) { + spin_lock_irqsave(&common->cc_lock, flags); + ath9k_hw_ani_monitor(ah, ah->curchan); + ath_update_survey_stats(sc); + spin_unlock_irqrestore(&common->cc_lock, flags); + } + + /* Perform calibration if necessary */ + if (longcal || shortcal) { + common->ani.caldone = + ath9k_hw_calibrate(ah, ah->curchan, + ah->rxchainmask, longcal); + } + + ath_dbg(common, ANI, + "Calibration @%lu finished: %s %s %s, caldone: %s\n", + jiffies, + longcal ? "long" : "", shortcal ? "short" : "", + aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); + + ath9k_ps_restore(sc); + +set_timer: + /* + * Set timer interval based on previous results. + * The interval must be the shortest necessary to satisfy ANI, + * short calibration and long calibration. + */ + ath9k_debug_samp_bb_mac(sc); + cal_interval = ATH_LONG_CALINTERVAL; + if (sc->sc_ah->config.enable_ani) + cal_interval = min(cal_interval, + (u32)ah->config.ani_poll_interval); + if (!common->ani.caldone) + cal_interval = min(cal_interval, (u32)short_cal_interval); + + mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) { + if (!ah->caldata->paprd_done) + ieee80211_queue_work(sc->hw, &sc->paprd_work); + else if (!ah->paprd_table_write_done) + ath_paprd_activate(sc); + } +} + +void ath_start_ani(struct ath_common *common) +{ + struct ath_hw *ah = common->ah; + unsigned long timestamp = jiffies_to_msecs(jiffies); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + if (!(sc->sc_flags & SC_OP_ANI_RUN)) + return; + + if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) + return; + + common->ani.longcal_timer = timestamp; + common->ani.shortcal_timer = timestamp; + common->ani.checkani_timer = timestamp; + + mod_timer(&common->ani.timer, + jiffies + msecs_to_jiffies((u32)ah->config.ani_poll_interval)); +} + +void ath_update_survey_nf(struct ath_softc *sc, int channel) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath9k_channel *chan = &ah->channels[channel]; + struct survey_info *survey = &sc->survey[channel]; + + if (chan->noisefloor) { + survey->filled |= SURVEY_INFO_NOISE_DBM; + survey->noise = ath9k_hw_getchan_noise(ah, chan); + } +} + +/* + * Updates the survey statistics and returns the busy time since last + * update in %, if the measurement duration was long enough for the + * result to be useful, -1 otherwise. + */ +int ath_update_survey_stats(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + int pos = ah->curchan - &ah->channels[0]; + struct survey_info *survey = &sc->survey[pos]; + struct ath_cycle_counters *cc = &common->cc_survey; + unsigned int div = common->clockrate * 1000; + int ret = 0; + + if (!ah->curchan) + return -1; + + if (ah->power_mode == ATH9K_PM_AWAKE) + ath_hw_cycle_counters_update(common); + + if (cc->cycles > 0) { + survey->filled |= SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_RX | + SURVEY_INFO_CHANNEL_TIME_TX; + survey->channel_time += cc->cycles / div; + survey->channel_time_busy += cc->rx_busy / div; + survey->channel_time_rx += cc->rx_frame / div; + survey->channel_time_tx += cc->tx_frame / div; + } + + if (cc->cycles < div) + return -1; + + if (cc->cycles > 0) + ret = cc->rx_busy * 100 / cc->cycles; + + memset(cc, 0, sizeof(*cc)); + + ath_update_survey_nf(sc, pos); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 623b04f63fe4..304769a3e179 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -144,84 +144,6 @@ void ath9k_ps_restore(struct ath_softc *sc) spin_unlock_irqrestore(&sc->sc_pm_lock, flags); } -void ath_start_ani(struct ath_common *common) -{ - struct ath_hw *ah = common->ah; - unsigned long timestamp = jiffies_to_msecs(jiffies); - struct ath_softc *sc = (struct ath_softc *) common->priv; - - if (!(sc->sc_flags & SC_OP_ANI_RUN)) - return; - - if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) - return; - - common->ani.longcal_timer = timestamp; - common->ani.shortcal_timer = timestamp; - common->ani.checkani_timer = timestamp; - - mod_timer(&common->ani.timer, - jiffies + - msecs_to_jiffies((u32)ah->config.ani_poll_interval)); -} - -static void ath_update_survey_nf(struct ath_softc *sc, int channel) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath9k_channel *chan = &ah->channels[channel]; - struct survey_info *survey = &sc->survey[channel]; - - if (chan->noisefloor) { - survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = ath9k_hw_getchan_noise(ah, chan); - } -} - -/* - * Updates the survey statistics and returns the busy time since last - * update in %, if the measurement duration was long enough for the - * result to be useful, -1 otherwise. - */ -static int ath_update_survey_stats(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - int pos = ah->curchan - &ah->channels[0]; - struct survey_info *survey = &sc->survey[pos]; - struct ath_cycle_counters *cc = &common->cc_survey; - unsigned int div = common->clockrate * 1000; - int ret = 0; - - if (!ah->curchan) - return -1; - - if (ah->power_mode == ATH9K_PM_AWAKE) - ath_hw_cycle_counters_update(common); - - if (cc->cycles > 0) { - survey->filled |= SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX | - SURVEY_INFO_CHANNEL_TIME_TX; - survey->channel_time += cc->cycles / div; - survey->channel_time_busy += cc->rx_busy / div; - survey->channel_time_rx += cc->rx_frame / div; - survey->channel_time_tx += cc->tx_frame / div; - } - - if (cc->cycles < div) - return -1; - - if (cc->cycles > 0) - ret = cc->rx_busy * 100 / cc->cycles; - - memset(cc, 0, sizeof(*cc)); - - ath_update_survey_nf(sc, pos); - - return ret; -} - static void __ath_cancel_work(struct ath_softc *sc) { cancel_work_sync(&sc->paprd_work); @@ -380,258 +302,6 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, return r; } -static void ath_paprd_activate(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath9k_hw_cal_data *caldata = ah->caldata; - int chain; - - if (!caldata || !caldata->paprd_done) - return; - - ath9k_ps_wakeup(sc); - ar9003_paprd_enable(ah, false); - for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->txchainmask & BIT(chain))) - continue; - - ar9003_paprd_populate_single_table(ah, caldata, chain); - } - - ar9003_paprd_enable(ah, true); - ath9k_ps_restore(sc); -} - -static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) -{ - struct ieee80211_hw *hw = sc->hw; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_tx_control txctl; - int time_left; - - memset(&txctl, 0, sizeof(txctl)); - txctl.txq = sc->tx.txq_map[WME_AC_BE]; - - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->band = hw->conf.channel->band; - tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; - tx_info->control.rates[0].idx = 0; - tx_info->control.rates[0].count = 1; - tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; - tx_info->control.rates[1].idx = -1; - - init_completion(&sc->paprd_complete); - txctl.paprd = BIT(chain); - - if (ath_tx_start(hw, skb, &txctl) != 0) { - ath_dbg(common, CALIBRATE, "PAPRD TX failed\n"); - dev_kfree_skb_any(skb); - return false; - } - - time_left = wait_for_completion_timeout(&sc->paprd_complete, - msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); - - if (!time_left) - ath_dbg(common, CALIBRATE, - "Timeout waiting for paprd training on TX chain %d\n", - chain); - - return !!time_left; -} - -void ath_paprd_calibrate(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); - struct ieee80211_hw *hw = sc->hw; - struct ath_hw *ah = sc->sc_ah; - struct ieee80211_hdr *hdr; - struct sk_buff *skb = NULL; - struct ath9k_hw_cal_data *caldata = ah->caldata; - struct ath_common *common = ath9k_hw_common(ah); - int ftype; - int chain_ok = 0; - int chain; - int len = 1800; - - if (!caldata) - return; - - ath9k_ps_wakeup(sc); - - if (ar9003_paprd_init_table(ah) < 0) - goto fail_paprd; - - skb = alloc_skb(len, GFP_KERNEL); - if (!skb) - goto fail_paprd; - - skb_put(skb, len); - memset(skb->data, 0, len); - hdr = (struct ieee80211_hdr *)skb->data; - ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC; - hdr->frame_control = cpu_to_le16(ftype); - hdr->duration_id = cpu_to_le16(10); - memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); - memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); - memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); - - for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { - if (!(ah->txchainmask & BIT(chain))) - continue; - - chain_ok = 0; - - ath_dbg(common, CALIBRATE, - "Sending PAPRD frame for thermal measurement on chain %d\n", - chain); - if (!ath_paprd_send_frame(sc, skb, chain)) - goto fail_paprd; - - ar9003_paprd_setup_gain_table(ah, chain); - - ath_dbg(common, CALIBRATE, - "Sending PAPRD training frame on chain %d\n", chain); - if (!ath_paprd_send_frame(sc, skb, chain)) - goto fail_paprd; - - if (!ar9003_paprd_is_done(ah)) { - ath_dbg(common, CALIBRATE, - "PAPRD not yet done on chain %d\n", chain); - break; - } - - if (ar9003_paprd_create_curve(ah, caldata, chain)) { - ath_dbg(common, CALIBRATE, - "PAPRD create curve failed on chain %d\n", - chain); - break; - } - - chain_ok = 1; - } - kfree_skb(skb); - - if (chain_ok) { - caldata->paprd_done = true; - ath_paprd_activate(sc); - } - -fail_paprd: - ath9k_ps_restore(sc); -} - -/* - * This routine performs the periodic noise floor calibration function - * that is used to adjust and optimize the chip performance. This - * takes environmental changes (location, temperature) into account. - * When the task is complete, it reschedules itself depending on the - * appropriate interval that was calculated. - */ -void ath_ani_calibrate(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *)data; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - bool longcal = false; - bool shortcal = false; - bool aniflag = false; - unsigned int timestamp = jiffies_to_msecs(jiffies); - u32 cal_interval, short_cal_interval, long_cal_interval; - unsigned long flags; - - if (ah->caldata && ah->caldata->nfcal_interference) - long_cal_interval = ATH_LONG_CALINTERVAL_INT; - else - long_cal_interval = ATH_LONG_CALINTERVAL; - - short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? - ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; - - /* Only calibrate if awake */ - if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) - goto set_timer; - - ath9k_ps_wakeup(sc); - - /* Long calibration runs independently of short calibration. */ - if ((timestamp - common->ani.longcal_timer) >= long_cal_interval) { - longcal = true; - common->ani.longcal_timer = timestamp; - } - - /* Short calibration applies only while caldone is false */ - if (!common->ani.caldone) { - if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) { - shortcal = true; - common->ani.shortcal_timer = timestamp; - common->ani.resetcal_timer = timestamp; - } - } else { - if ((timestamp - common->ani.resetcal_timer) >= - ATH_RESTART_CALINTERVAL) { - common->ani.caldone = ath9k_hw_reset_calvalid(ah); - if (common->ani.caldone) - common->ani.resetcal_timer = timestamp; - } - } - - /* Verify whether we must check ANI */ - if (sc->sc_ah->config.enable_ani - && (timestamp - common->ani.checkani_timer) >= - ah->config.ani_poll_interval) { - aniflag = true; - common->ani.checkani_timer = timestamp; - } - - /* Call ANI routine if necessary */ - if (aniflag) { - spin_lock_irqsave(&common->cc_lock, flags); - ath9k_hw_ani_monitor(ah, ah->curchan); - ath_update_survey_stats(sc); - spin_unlock_irqrestore(&common->cc_lock, flags); - } - - /* Perform calibration if necessary */ - if (longcal || shortcal) { - common->ani.caldone = - ath9k_hw_calibrate(ah, ah->curchan, - ah->rxchainmask, longcal); - } - - ath_dbg(common, ANI, - "Calibration @%lu finished: %s %s %s, caldone: %s\n", - jiffies, - longcal ? "long" : "", shortcal ? "short" : "", - aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); - - ath9k_ps_restore(sc); - -set_timer: - /* - * Set timer interval based on previous results. - * The interval must be the shortest necessary to satisfy ANI, - * short calibration and long calibration. - */ - ath9k_debug_samp_bb_mac(sc); - cal_interval = ATH_LONG_CALINTERVAL; - if (sc->sc_ah->config.enable_ani) - cal_interval = min(cal_interval, - (u32)ah->config.ani_poll_interval); - if (!common->ani.caldone) - cal_interval = min(cal_interval, (u32)short_cal_interval); - - mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) { - if (!ah->caldata->paprd_done) - ieee80211_queue_work(sc->hw, &sc->paprd_work); - else if (!ah->paprd_table_write_done) - ath_paprd_activate(sc); - } -} - static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { @@ -669,7 +339,6 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) ath_tx_node_cleanup(sc, an); } - void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; @@ -903,87 +572,6 @@ void ath_reset_work(struct work_struct *work) ath_reset(sc, true); } -void ath_hw_check(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - unsigned long flags; - int busy; - u8 is_alive, nbeacon = 1; - - ath9k_ps_wakeup(sc); - is_alive = ath9k_hw_check_alive(sc->sc_ah); - - if (is_alive && !AR_SREV_9300(sc->sc_ah)) - goto out; - else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { - ath_dbg(common, RESET, - "DCU stuck is detected. Schedule chip reset\n"); - RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG); - goto sched_reset; - } - - spin_lock_irqsave(&common->cc_lock, flags); - busy = ath_update_survey_stats(sc); - spin_unlock_irqrestore(&common->cc_lock, flags); - - ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", - busy, sc->hw_busy_count + 1); - if (busy >= 99) { - if (++sc->hw_busy_count >= 3) { - RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); - goto sched_reset; - } - } else if (busy >= 0) { - sc->hw_busy_count = 0; - nbeacon = 3; - } - - ath_start_rx_poll(sc, nbeacon); - goto out; - -sched_reset: - ieee80211_queue_work(sc->hw, &sc->hw_reset_work); -out: - ath9k_ps_restore(sc); -} - -static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) -{ - static int count; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - - if (pll_sqsum >= 0x40000) { - count++; - if (count == 3) { - /* Rx is hung for more than 500ms. Reset it */ - ath_dbg(common, RESET, "Possible RX hang, resetting\n"); - RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG); - ieee80211_queue_work(sc->hw, &sc->hw_reset_work); - count = 0; - } - } else - count = 0; -} - -void ath_hw_pll_work(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, - hw_pll_work.work); - u32 pll_sqsum; - - if (AR_SREV_9485(sc->sc_ah)) { - - ath9k_ps_wakeup(sc); - pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); - ath9k_ps_restore(sc); - - ath_hw_pll_rx_hang_check(sc, pll_sqsum); - - ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); - } -} - /**********************/ /* mac80211 callbacks */ /**********************/ @@ -1389,25 +977,6 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw, } } -void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) -{ - if (!AR_SREV_9300(sc->sc_ah)) - return; - - if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) - return; - - mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies - (nbeacon * sc->cur_beacon_conf.beacon_interval)); -} - -void ath_rx_poll(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *)data; - - ieee80211_queue_work(sc->hw, &sc->hw_check_work); -} - static int ath9k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index d59dd01d6cde..6619a39b8c27 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -105,19 +105,19 @@ static int ath_max_4ms_framelen[4][32] = { /* Aggregation logic */ /*********************/ -static void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq) +void ath_txq_lock(struct ath_softc *sc, struct ath_txq *txq) __acquires(&txq->axq_lock) { spin_lock_bh(&txq->axq_lock); } -static void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq) +void ath_txq_unlock(struct ath_softc *sc, struct ath_txq *txq) __releases(&txq->axq_lock) { spin_unlock_bh(&txq->axq_lock); } -static void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) +void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq) __releases(&txq->axq_lock) { struct sk_buff_head q; @@ -2231,46 +2231,6 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) ath_txq_unlock_complete(sc, txq); } -static void ath_tx_complete_poll_work(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, - tx_complete_work.work); - struct ath_txq *txq; - int i; - bool needreset = false; -#ifdef CONFIG_ATH9K_DEBUGFS - sc->tx_complete_poll_work_seen++; -#endif - - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) { - txq = &sc->tx.txq[i]; - ath_txq_lock(sc, txq); - if (txq->axq_depth) { - if (txq->axq_tx_inprogress) { - needreset = true; - ath_txq_unlock(sc, txq); - break; - } else { - txq->axq_tx_inprogress = true; - } - } - ath_txq_unlock_complete(sc, txq); - } - - if (needreset) { - ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, - "tx hung, resetting the chip\n"); - RESET_STAT_INC(sc, RESET_TYPE_TX_HANG); - ieee80211_queue_work(sc->hw, &sc->hw_reset_work); - } - - ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, - msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); -} - - - void ath_tx_tasklet(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; -- cgit v1.2.3 From af68abadac378f7a4a44fe766383e7b88c7f9c3b Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:23:43 +0530 Subject: ath9k: Fix work handling * Currently, there is no synchronization between the reset work and the tx-poll work. Fix this and make sure that we bail out properly if a reset work is in progress. * Cleanup the PLL WAR and enable it for AR9340 too and use a helper for restarting work/timers after a reset. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/link.c | 33 +++++++++++++++++++-------------- drivers/net/wireless/ath/ath9k/main.c | 22 +++++++++++++++++----- 3 files changed, 37 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index d804416092ce..2faa181d8fcf 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -431,6 +431,7 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status); #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ #define ATH_PAPRD_TIMEOUT 100 /* msecs */ +#define ATH_PLL_WORK_INTERVAL 100 void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 7368b9630b99..89b38a9ab7c5 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -52,6 +52,7 @@ void ath_tx_complete_poll_work(struct work_struct *work) "tx hung, resetting the chip\n"); RESET_STAT_INC(sc, RESET_TYPE_TX_HANG); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + return; } ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, @@ -107,9 +108,9 @@ out: } /* - * PLL-WAR for AR9485. + * PLL-WAR for AR9485/AR9340 */ -static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) +static bool ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) { static int count; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -117,29 +118,33 @@ static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) if (pll_sqsum >= 0x40000) { count++; if (count == 3) { - /* Rx is hung for more than 500ms. Reset it */ - ath_dbg(common, RESET, "Possible RX hang, resetting\n"); + ath_dbg(common, RESET, "PLL WAR, resetting the chip\n"); RESET_STAT_INC(sc, RESET_TYPE_PLL_HANG); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); count = 0; + return true; } - } else + } else { count = 0; + } + + return false; } void ath_hw_pll_work(struct work_struct *work) { + u32 pll_sqsum; struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); - u32 pll_sqsum; - if (AR_SREV_9485(sc->sc_ah)) { - ath9k_ps_wakeup(sc); - pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); - ath9k_ps_restore(sc); - ath_hw_pll_rx_hang_check(sc, pll_sqsum); - ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/5); - } + ath9k_ps_wakeup(sc); + pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); + ath9k_ps_restore(sc); + if (ath_hw_pll_rx_hang_check(sc, pll_sqsum)) + return; + + ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, + msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); } /* @@ -293,7 +298,7 @@ void ath_paprd_calibrate(struct work_struct *work) if (ar9003_paprd_create_curve(ah, caldata, chain)) { ath_dbg(common, CALIBRATE, "PAPRD create curve failed on chain %d\n", - chain); + chain); break; } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 304769a3e179..b228aff481e1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -158,6 +158,22 @@ static void ath_cancel_work(struct ath_softc *sc) cancel_work_sync(&sc->hw_reset_work); } +static void ath_restart_work(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + + if (AR_SREV_9485(sc->sc_ah) || AR_SREV_9340(sc->sc_ah)) + ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, + msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); + + ath_start_rx_poll(sc, 3); + + if (!common->disable_ani) + ath_start_ani(common); +} + static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) { struct ath_hw *ah = sc->sc_ah; @@ -209,11 +225,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) if (sc->sc_flags & SC_OP_BEACONS) ath_set_beacon(sc); - ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); - ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); - ath_start_rx_poll(sc, 3); - if (!common->disable_ani) - ath_start_ani(common); + ath_restart_work(sc); } if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) { -- cgit v1.2.3 From 8da07830e10a91cbe7badf9767230aafdd520b9c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:23:49 +0530 Subject: ath9k: Move LNA code to antenna.c And use a helper function to setup antennae after a reset. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Makefile | 3 +- drivers/net/wireless/ath/ath9k/antenna.c | 776 +++++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/ath9k.h | 7 +- drivers/net/wireless/ath/ath9k/main.c | 17 +- drivers/net/wireless/ath/ath9k/recv.c | 740 ----------------------------- 5 files changed, 786 insertions(+), 757 deletions(-) create mode 100644 drivers/net/wireless/ath/ath9k/antenna.c (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 4d60fa84cbb4..9c41232b0cd0 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -4,7 +4,8 @@ ath9k-y += beacon.o \ main.o \ recv.o \ xmit.o \ - link.o + link.o \ + antenna.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c new file mode 100644 index 000000000000..bbcfeb3b2a60 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -0,0 +1,776 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, + int mindelta, int main_rssi_avg, + int alt_rssi_avg, int pkt_count) +{ + return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + maxdelta)) || + (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); +} + +static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio, + int curr_main_set, int curr_alt_set, + int alt_rssi_avg, int main_rssi_avg) +{ + bool result = false; + switch (div_group) { + case 0: + if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) + result = true; + break; + case 1: + case 2: + if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) && + (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) && + (alt_rssi_avg >= (main_rssi_avg - 5))) || + ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) && + (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) && + (alt_rssi_avg >= (main_rssi_avg - 2)))) && + (alt_rssi_avg >= 4)) + result = true; + else + result = false; + break; + } + + return result; +} + +static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, + struct ath_hw_antcomb_conf ant_conf, + int main_rssi_avg) +{ + antcomb->quick_scan_cnt = 0; + + if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = main_rssi_avg; + else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = main_rssi_avg; + + switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { + case 0x10: /* LNA2 A-B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; + break; + case 0x20: /* LNA1 A-B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; + break; + case 0x21: /* LNA1 LNA2 */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case 0x12: /* LNA2 LNA1 */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case 0x13: /* LNA2 A+B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; + break; + case 0x23: /* LNA1 A+B */ + antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + antcomb->first_quick_scan_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; + break; + default: + break; + } +} + +static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, + struct ath_hw_antcomb_conf *div_ant_conf, + int main_rssi_avg, int alt_rssi_avg, + int alt_ratio) +{ + /* alt_good */ + switch (antcomb->quick_scan_cnt) { + case 0: + /* set alt to main, and alt to first conf */ + div_ant_conf->main_lna_conf = antcomb->main_conf; + div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; + break; + case 1: + /* set alt to main, and alt to first conf */ + div_ant_conf->main_lna_conf = antcomb->main_conf; + div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; + antcomb->rssi_first = main_rssi_avg; + antcomb->rssi_second = alt_rssi_avg; + + if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { + /* main is LNA1 */ + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->first_ratio = true; + else + antcomb->first_ratio = false; + } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_MID, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->first_ratio = true; + else + antcomb->first_ratio = false; + } else { + if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || + (alt_rssi_avg > main_rssi_avg)) && + (antcomb->total_pkt_count > 50)) + antcomb->first_ratio = true; + else + antcomb->first_ratio = false; + } + break; + case 2: + antcomb->alt_good = false; + antcomb->scan_not_start = false; + antcomb->scan = false; + antcomb->rssi_first = main_rssi_avg; + antcomb->rssi_third = alt_rssi_avg; + + if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = alt_rssi_avg; + else if (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = alt_rssi_avg; + else if (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) { + if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) + antcomb->rssi_lna2 = main_rssi_avg; + else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) + antcomb->rssi_lna1 = main_rssi_avg; + } + + if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) + div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + else + div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; + + if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_HI, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->second_ratio = true; + else + antcomb->second_ratio = false; + } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { + if (ath_is_alt_ant_ratio_better(alt_ratio, + ATH_ANT_DIV_COMB_LNA1_DELTA_MID, + ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, + main_rssi_avg, alt_rssi_avg, + antcomb->total_pkt_count)) + antcomb->second_ratio = true; + else + antcomb->second_ratio = false; + } else { + if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && + (alt_rssi_avg > main_rssi_avg + + ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || + (alt_rssi_avg > main_rssi_avg)) && + (antcomb->total_pkt_count > 50)) + antcomb->second_ratio = true; + else + antcomb->second_ratio = false; + } + + /* set alt to the conf with maximun ratio */ + if (antcomb->first_ratio && antcomb->second_ratio) { + if (antcomb->rssi_second > antcomb->rssi_third) { + /* first alt*/ + if ((antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2*/ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if ((antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) { + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } else { + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->second_quick_scan_conf; + } + } else if (antcomb->first_ratio) { + /* first alt */ + if ((antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->first_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->first_quick_scan_conf; + } else if (antcomb->second_ratio) { + /* second alt */ + if ((antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA1) || + (antcomb->second_quick_scan_conf == + ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = + antcomb->second_quick_scan_conf; + } else { + /* main is largest */ + if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || + (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) + /* Set alt LNA1 or LNA2 */ + if (div_ant_conf->main_lna_conf == + ATH_ANT_DIV_COMB_LNA2) + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else + div_ant_conf->alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + else + /* Set alt to A+B or A-B */ + div_ant_conf->alt_lna_conf = antcomb->main_conf; + } + break; + default: + break; + } +} + +static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, + struct ath_ant_comb *antcomb, + int alt_ratio) +{ + if (ant_conf->div_group == 0) { + /* Adjust the fast_div_bias based on main and alt lna conf */ + switch ((ant_conf->main_lna_conf << 4) | + ant_conf->alt_lna_conf) { + case 0x01: /* A-B LNA2 */ + ant_conf->fast_div_bias = 0x3b; + break; + case 0x02: /* A-B LNA1 */ + ant_conf->fast_div_bias = 0x3d; + break; + case 0x03: /* A-B A+B */ + ant_conf->fast_div_bias = 0x1; + break; + case 0x10: /* LNA2 A-B */ + ant_conf->fast_div_bias = 0x7; + break; + case 0x12: /* LNA2 LNA1 */ + ant_conf->fast_div_bias = 0x2; + break; + case 0x13: /* LNA2 A+B */ + ant_conf->fast_div_bias = 0x7; + break; + case 0x20: /* LNA1 A-B */ + ant_conf->fast_div_bias = 0x6; + break; + case 0x21: /* LNA1 LNA2 */ + ant_conf->fast_div_bias = 0x0; + break; + case 0x23: /* LNA1 A+B */ + ant_conf->fast_div_bias = 0x6; + break; + case 0x30: /* A+B A-B */ + ant_conf->fast_div_bias = 0x1; + break; + case 0x31: /* A+B LNA2 */ + ant_conf->fast_div_bias = 0x3b; + break; + case 0x32: /* A+B LNA1 */ + ant_conf->fast_div_bias = 0x3d; + break; + default: + break; + } + } else if (ant_conf->div_group == 1) { + /* Adjust the fast_div_bias based on main and alt_lna_conf */ + switch ((ant_conf->main_lna_conf << 4) | + ant_conf->alt_lna_conf) { + case 0x01: /* A-B LNA2 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x02: /* A-B LNA1 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x03: /* A-B A+B */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x10: /* LNA2 A-B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x3f; + else + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x12: /* LNA2 LNA1 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x13: /* LNA2 A+B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x3f; + else + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x20: /* LNA1 A-B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x3f; + else + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x21: /* LNA1 LNA2 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x23: /* LNA1 A+B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x3f; + else + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x30: /* A+B A-B */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x31: /* A+B LNA2 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x32: /* A+B LNA1 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + default: + break; + } + } else if (ant_conf->div_group == 2) { + /* Adjust the fast_div_bias based on main and alt_lna_conf */ + switch ((ant_conf->main_lna_conf << 4) | + ant_conf->alt_lna_conf) { + case 0x01: /* A-B LNA2 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x02: /* A-B LNA1 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x03: /* A-B A+B */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x10: /* LNA2 A-B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x1; + else + ant_conf->fast_div_bias = 0x2; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x12: /* LNA2 LNA1 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x13: /* LNA2 A+B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x1; + else + ant_conf->fast_div_bias = 0x2; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x20: /* LNA1 A-B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x1; + else + ant_conf->fast_div_bias = 0x2; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x21: /* LNA1 LNA2 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x23: /* LNA1 A+B */ + if (!(antcomb->scan) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) + ant_conf->fast_div_bias = 0x1; + else + ant_conf->fast_div_bias = 0x2; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x30: /* A+B A-B */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x31: /* A+B LNA2 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + case 0x32: /* A+B LNA1 */ + ant_conf->fast_div_bias = 0x1; + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + break; + default: + break; + } + } +} + +void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) +{ + struct ath_hw_antcomb_conf div_ant_conf; + struct ath_ant_comb *antcomb = &sc->ant_comb; + int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; + int curr_main_set; + int main_rssi = rs->rs_rssi_ctl0; + int alt_rssi = rs->rs_rssi_ctl1; + int rx_ant_conf, main_ant_conf; + bool short_scan = false; + + rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & + ATH_ANT_RX_MASK; + main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & + ATH_ANT_RX_MASK; + + /* Record packet only when both main_rssi and alt_rssi is positive */ + if (main_rssi > 0 && alt_rssi > 0) { + antcomb->total_pkt_count++; + antcomb->main_total_rssi += main_rssi; + antcomb->alt_total_rssi += alt_rssi; + if (main_ant_conf == rx_ant_conf) + antcomb->main_recv_cnt++; + else + antcomb->alt_recv_cnt++; + } + + /* Short scan check */ + if (antcomb->scan && antcomb->alt_good) { + if (time_after(jiffies, antcomb->scan_start_time + + msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) + short_scan = true; + else + if (antcomb->total_pkt_count == + ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) + short_scan = true; + } + } + + if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || + rs->rs_moreaggr) && !short_scan) + return; + + if (antcomb->total_pkt_count) { + alt_ratio = ((antcomb->alt_recv_cnt * 100) / + antcomb->total_pkt_count); + main_rssi_avg = (antcomb->main_total_rssi / + antcomb->total_pkt_count); + alt_rssi_avg = (antcomb->alt_total_rssi / + antcomb->total_pkt_count); + } + + + ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); + curr_alt_set = div_ant_conf.alt_lna_conf; + curr_main_set = div_ant_conf.main_lna_conf; + + antcomb->count++; + + if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { + if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { + ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, + main_rssi_avg); + antcomb->alt_good = true; + } else { + antcomb->alt_good = false; + } + + antcomb->count = 0; + antcomb->scan = true; + antcomb->scan_not_start = true; + } + + if (!antcomb->scan) { + if (ath_ant_div_comb_alt_check(div_ant_conf.div_group, + alt_ratio, curr_main_set, curr_alt_set, + alt_rssi_avg, main_rssi_avg)) { + if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { + /* Switch main and alt LNA */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } + + goto div_comb_done; + } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && + (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { + /* Set alt to another LNA */ + if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + + goto div_comb_done; + } + + if ((alt_rssi_avg < (main_rssi_avg + + div_ant_conf.lna1_lna2_delta))) + goto div_comb_done; + } + + if (!antcomb->scan_not_start) { + switch (curr_alt_set) { + case ATH_ANT_DIV_COMB_LNA2: + antcomb->rssi_lna2 = alt_rssi_avg; + antcomb->rssi_lna1 = main_rssi_avg; + antcomb->scan = true; + /* set to A+B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1: + antcomb->rssi_lna1 = alt_rssi_avg; + antcomb->rssi_lna2 = main_rssi_avg; + antcomb->scan = true; + /* set to A+B */ + div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: + antcomb->rssi_add = alt_rssi_avg; + antcomb->scan = true; + /* set to A-B */ + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + break; + case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: + antcomb->rssi_sub = alt_rssi_avg; + antcomb->scan = false; + if (antcomb->rssi_lna2 > + (antcomb->rssi_lna1 + + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { + /* use LNA2 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna1) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA1 */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + } + } else { + /* use LNA1 as main LNA */ + if ((antcomb->rssi_add > antcomb->rssi_lna2) && + (antcomb->rssi_add > antcomb->rssi_sub)) { + /* set to A+B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; + } else if (antcomb->rssi_sub > + antcomb->rssi_lna1) { + /* set to A-B */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; + } else { + /* set to LNA2 */ + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } + } + break; + default: + break; + } + } else { + if (!antcomb->alt_good) { + antcomb->scan_not_start = false; + /* Set alt to another LNA */ + if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { + div_ant_conf.main_lna_conf = + ATH_ANT_DIV_COMB_LNA1; + div_ant_conf.alt_lna_conf = + ATH_ANT_DIV_COMB_LNA2; + } + goto div_comb_done; + } + } + + ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, + main_rssi_avg, alt_rssi_avg, + alt_ratio); + + antcomb->quick_scan_cnt++; + +div_comb_done: + ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio); + ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); + + antcomb->scan_start_time = jiffies; + antcomb->total_pkt_count = 0; + antcomb->main_total_rssi = 0; + antcomb->alt_total_rssi = 0; + antcomb->main_recv_cnt = 0; + antcomb->alt_recv_cnt = 0; +} + +void ath_ant_comb_update(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_hw_antcomb_conf div_ant_conf; + u8 lna_conf; + + ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); + + if (sc->ant_rx == 1) + lna_conf = ATH_ANT_DIV_COMB_LNA1; + else + lna_conf = ATH_ANT_DIV_COMB_LNA2; + + div_ant_conf.main_lna_conf = lna_conf; + div_ant_conf.alt_lna_conf = lna_conf; + + ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); +} diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2faa181d8fcf..4c2ce453af22 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -528,8 +528,10 @@ static inline void ath_deinit_leds(struct ath_softc *sc) } #endif - +/*******************************/ /* Antenna diversity/combining */ +/*******************************/ + #define ATH_ANT_RX_CURRENT_SHIFT 4 #define ATH_ANT_RX_MAIN_SHIFT 2 #define ATH_ANT_RX_MASK 0x3 @@ -582,6 +584,9 @@ struct ath_ant_comb { unsigned long scan_start_time; }; +void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); +void ath_ant_comb_update(struct ath_softc *sc); + /********************/ /* Main driver core */ /********************/ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b228aff481e1..66f215ca9969 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -228,21 +228,8 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath_restart_work(sc); } - if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) { - struct ath_hw_antcomb_conf div_ant_conf; - u8 lna_conf; - - ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); - - if (sc->ant_rx == 1) - lna_conf = ATH_ANT_DIV_COMB_LNA1; - else - lna_conf = ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.main_lna_conf = lna_conf; - div_ant_conf.alt_lna_conf = lna_conf; - - ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); - } + if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) + ath_ant_comb_update(sc); ieee80211_wake_queues(sc->hw); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 611be4f934e7..c2f5fd1c8b87 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -20,43 +20,6 @@ #define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb)) -static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, - int mindelta, int main_rssi_avg, - int alt_rssi_avg, int pkt_count) -{ - return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + maxdelta)) || - (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50); -} - -static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio, - int curr_main_set, int curr_alt_set, - int alt_rssi_avg, int main_rssi_avg) -{ - bool result = false; - switch (div_group) { - case 0: - if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) - result = true; - break; - case 1: - case 2: - if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) && - (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) && - (alt_rssi_avg >= (main_rssi_avg - 5))) || - ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) && - (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) && - (alt_rssi_avg >= (main_rssi_avg - 2)))) && - (alt_rssi_avg >= 4)) - result = true; - else - result = false; - break; - } - - return result; -} - static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) { return sc->ps_enabled && @@ -1067,709 +1030,6 @@ static void ath9k_rx_skb_postprocess(struct ath_common *common, rxs->flag &= ~RX_FLAG_DECRYPTED; } -static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, - struct ath_hw_antcomb_conf ant_conf, - int main_rssi_avg) -{ - antcomb->quick_scan_cnt = 0; - - if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) - antcomb->rssi_lna2 = main_rssi_avg; - else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) - antcomb->rssi_lna1 = main_rssi_avg; - - switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { - case 0x10: /* LNA2 A-B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; - break; - case 0x20: /* LNA1 A-B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; - break; - case 0x21: /* LNA1 LNA2 */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case 0x12: /* LNA2 LNA1 */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case 0x13: /* LNA2 A+B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; - break; - case 0x23: /* LNA1 A+B */ - antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - antcomb->first_quick_scan_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; - break; - default: - break; - } -} - -static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, - struct ath_hw_antcomb_conf *div_ant_conf, - int main_rssi_avg, int alt_rssi_avg, - int alt_ratio) -{ - /* alt_good */ - switch (antcomb->quick_scan_cnt) { - case 0: - /* set alt to main, and alt to first conf */ - div_ant_conf->main_lna_conf = antcomb->main_conf; - div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; - break; - case 1: - /* set alt to main, and alt to first conf */ - div_ant_conf->main_lna_conf = antcomb->main_conf; - div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; - antcomb->rssi_first = main_rssi_avg; - antcomb->rssi_second = alt_rssi_avg; - - if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { - /* main is LNA1 */ - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_HI, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->first_ratio = true; - else - antcomb->first_ratio = false; - } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_MID, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->first_ratio = true; - else - antcomb->first_ratio = false; - } else { - if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || - (alt_rssi_avg > main_rssi_avg)) && - (antcomb->total_pkt_count > 50)) - antcomb->first_ratio = true; - else - antcomb->first_ratio = false; - } - break; - case 2: - antcomb->alt_good = false; - antcomb->scan_not_start = false; - antcomb->scan = false; - antcomb->rssi_first = main_rssi_avg; - antcomb->rssi_third = alt_rssi_avg; - - if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) - antcomb->rssi_lna1 = alt_rssi_avg; - else if (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2) - antcomb->rssi_lna2 = alt_rssi_avg; - else if (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) { - if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) - antcomb->rssi_lna2 = main_rssi_avg; - else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) - antcomb->rssi_lna1 = main_rssi_avg; - } - - if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) - div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; - else - div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; - - if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_HI, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->second_ratio = true; - else - antcomb->second_ratio = false; - } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { - if (ath_is_alt_ant_ratio_better(alt_ratio, - ATH_ANT_DIV_COMB_LNA1_DELTA_MID, - ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, - main_rssi_avg, alt_rssi_avg, - antcomb->total_pkt_count)) - antcomb->second_ratio = true; - else - antcomb->second_ratio = false; - } else { - if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) && - (alt_rssi_avg > main_rssi_avg + - ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) || - (alt_rssi_avg > main_rssi_avg)) && - (antcomb->total_pkt_count > 50)) - antcomb->second_ratio = true; - else - antcomb->second_ratio = false; - } - - /* set alt to the conf with maximun ratio */ - if (antcomb->first_ratio && antcomb->second_ratio) { - if (antcomb->rssi_second > antcomb->rssi_third) { - /* first alt*/ - if ((antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2*/ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->first_quick_scan_conf; - } else if ((antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) { - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } else { - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->second_quick_scan_conf; - } - } else if (antcomb->first_ratio) { - /* first alt */ - if ((antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->first_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->first_quick_scan_conf; - } else if (antcomb->second_ratio) { - /* second alt */ - if ((antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA1) || - (antcomb->second_quick_scan_conf == - ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = - antcomb->second_quick_scan_conf; - } else { - /* main is largest */ - if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || - (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) - /* Set alt LNA1 or LNA2 */ - if (div_ant_conf->main_lna_conf == - ATH_ANT_DIV_COMB_LNA2) - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else - div_ant_conf->alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - else - /* Set alt to A+B or A-B */ - div_ant_conf->alt_lna_conf = antcomb->main_conf; - } - break; - default: - break; - } -} - -static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, - struct ath_ant_comb *antcomb, int alt_ratio) -{ - if (ant_conf->div_group == 0) { - /* Adjust the fast_div_bias based on main and alt lna conf */ - switch ((ant_conf->main_lna_conf << 4) | - ant_conf->alt_lna_conf) { - case 0x01: /* A-B LNA2 */ - ant_conf->fast_div_bias = 0x3b; - break; - case 0x02: /* A-B LNA1 */ - ant_conf->fast_div_bias = 0x3d; - break; - case 0x03: /* A-B A+B */ - ant_conf->fast_div_bias = 0x1; - break; - case 0x10: /* LNA2 A-B */ - ant_conf->fast_div_bias = 0x7; - break; - case 0x12: /* LNA2 LNA1 */ - ant_conf->fast_div_bias = 0x2; - break; - case 0x13: /* LNA2 A+B */ - ant_conf->fast_div_bias = 0x7; - break; - case 0x20: /* LNA1 A-B */ - ant_conf->fast_div_bias = 0x6; - break; - case 0x21: /* LNA1 LNA2 */ - ant_conf->fast_div_bias = 0x0; - break; - case 0x23: /* LNA1 A+B */ - ant_conf->fast_div_bias = 0x6; - break; - case 0x30: /* A+B A-B */ - ant_conf->fast_div_bias = 0x1; - break; - case 0x31: /* A+B LNA2 */ - ant_conf->fast_div_bias = 0x3b; - break; - case 0x32: /* A+B LNA1 */ - ant_conf->fast_div_bias = 0x3d; - break; - default: - break; - } - } else if (ant_conf->div_group == 1) { - /* Adjust the fast_div_bias based on main and alt_lna_conf */ - switch ((ant_conf->main_lna_conf << 4) | - ant_conf->alt_lna_conf) { - case 0x01: /* A-B LNA2 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x02: /* A-B LNA1 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x03: /* A-B A+B */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x10: /* LNA2 A-B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x3f; - else - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x12: /* LNA2 LNA1 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x13: /* LNA2 A+B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x3f; - else - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x20: /* LNA1 A-B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x3f; - else - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x21: /* LNA1 LNA2 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x23: /* LNA1 A+B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x3f; - else - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x30: /* A+B A-B */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x31: /* A+B LNA2 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x32: /* A+B LNA1 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - default: - break; - } - } else if (ant_conf->div_group == 2) { - /* Adjust the fast_div_bias based on main and alt_lna_conf */ - switch ((ant_conf->main_lna_conf << 4) | - ant_conf->alt_lna_conf) { - case 0x01: /* A-B LNA2 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x02: /* A-B LNA1 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x03: /* A-B A+B */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x10: /* LNA2 A-B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x1; - else - ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x12: /* LNA2 LNA1 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x13: /* LNA2 A+B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x1; - else - ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x20: /* LNA1 A-B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x1; - else - ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x21: /* LNA1 LNA2 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x23: /* LNA1 A+B */ - if (!(antcomb->scan) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) - ant_conf->fast_div_bias = 0x1; - else - ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x30: /* A+B A-B */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x31: /* A+B LNA2 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - case 0x32: /* A+B LNA1 */ - ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; - break; - default: - break; - } - } -} - -/* Antenna diversity and combining */ -static void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) -{ - struct ath_hw_antcomb_conf div_ant_conf; - struct ath_ant_comb *antcomb = &sc->ant_comb; - int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; - int curr_main_set; - int main_rssi = rs->rs_rssi_ctl0; - int alt_rssi = rs->rs_rssi_ctl1; - int rx_ant_conf, main_ant_conf; - bool short_scan = false; - - rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & - ATH_ANT_RX_MASK; - main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & - ATH_ANT_RX_MASK; - - /* Record packet only when both main_rssi and alt_rssi is positive */ - if (main_rssi > 0 && alt_rssi > 0) { - antcomb->total_pkt_count++; - antcomb->main_total_rssi += main_rssi; - antcomb->alt_total_rssi += alt_rssi; - if (main_ant_conf == rx_ant_conf) - antcomb->main_recv_cnt++; - else - antcomb->alt_recv_cnt++; - } - - /* Short scan check */ - if (antcomb->scan && antcomb->alt_good) { - if (time_after(jiffies, antcomb->scan_start_time + - msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) - short_scan = true; - else - if (antcomb->total_pkt_count == - ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { - alt_ratio = ((antcomb->alt_recv_cnt * 100) / - antcomb->total_pkt_count); - if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO) - short_scan = true; - } - } - - if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || - rs->rs_moreaggr) && !short_scan) - return; - - if (antcomb->total_pkt_count) { - alt_ratio = ((antcomb->alt_recv_cnt * 100) / - antcomb->total_pkt_count); - main_rssi_avg = (antcomb->main_total_rssi / - antcomb->total_pkt_count); - alt_rssi_avg = (antcomb->alt_total_rssi / - antcomb->total_pkt_count); - } - - - ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); - curr_alt_set = div_ant_conf.alt_lna_conf; - curr_main_set = div_ant_conf.main_lna_conf; - - antcomb->count++; - - if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { - if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) { - ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, - main_rssi_avg); - antcomb->alt_good = true; - } else { - antcomb->alt_good = false; - } - - antcomb->count = 0; - antcomb->scan = true; - antcomb->scan_not_start = true; - } - - if (!antcomb->scan) { - if (ath_ant_div_comb_alt_check(div_ant_conf.div_group, - alt_ratio, curr_main_set, curr_alt_set, - alt_rssi_avg, main_rssi_avg)) { - if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { - /* Switch main and alt LNA */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - - goto div_comb_done; - } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && - (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { - /* Set alt to another LNA */ - if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - - goto div_comb_done; - } - - if ((alt_rssi_avg < (main_rssi_avg + - div_ant_conf.lna1_lna2_delta))) - goto div_comb_done; - } - - if (!antcomb->scan_not_start) { - switch (curr_alt_set) { - case ATH_ANT_DIV_COMB_LNA2: - antcomb->rssi_lna2 = alt_rssi_avg; - antcomb->rssi_lna1 = main_rssi_avg; - antcomb->scan = true; - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1: - antcomb->rssi_lna1 = alt_rssi_avg; - antcomb->rssi_lna2 = main_rssi_avg; - antcomb->scan = true; - /* set to A+B */ - div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: - antcomb->rssi_add = alt_rssi_avg; - antcomb->scan = true; - /* set to A-B */ - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - break; - case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: - antcomb->rssi_sub = alt_rssi_avg; - antcomb->scan = false; - if (antcomb->rssi_lna2 > - (antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { - /* use LNA2 as main LNA */ - if ((antcomb->rssi_add > antcomb->rssi_lna1) && - (antcomb->rssi_add > antcomb->rssi_sub)) { - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { - /* set to A-B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - } else { - /* set to LNA1 */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } - } else { - /* use LNA1 as main LNA */ - if ((antcomb->rssi_add > antcomb->rssi_lna2) && - (antcomb->rssi_add > antcomb->rssi_sub)) { - /* set to A+B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; - } else if (antcomb->rssi_sub > - antcomb->rssi_lna1) { - /* set to A-B */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; - } else { - /* set to LNA2 */ - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - } - break; - default: - break; - } - } else { - if (!antcomb->alt_good) { - antcomb->scan_not_start = false; - /* Set alt to another LNA */ - if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { - div_ant_conf.main_lna_conf = - ATH_ANT_DIV_COMB_LNA1; - div_ant_conf.alt_lna_conf = - ATH_ANT_DIV_COMB_LNA2; - } - goto div_comb_done; - } - } - - ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, - main_rssi_avg, alt_rssi_avg, - alt_ratio); - - antcomb->quick_scan_cnt++; - -div_comb_done: - ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio); - ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); - - antcomb->scan_start_time = jiffies; - antcomb->total_pkt_count = 0; - antcomb->main_total_rssi = 0; - antcomb->alt_total_rssi = 0; - antcomb->main_recv_cnt = 0; - antcomb->alt_recv_cnt = 0; -} - int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { struct ath_buf *bf; -- cgit v1.2.3 From 781b14a3153a722fec820374271316537881076e Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:23:55 +0530 Subject: ath9k: Use atomic operations The 'sc_flags' variable is being used in a number of places with no locking whatsoever. This patch converts the usage of sc_flags to atomic ops. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ahb.c | 2 +- drivers/net/wireless/ath/ath9k/ath9k.h | 16 +++++++------ drivers/net/wireless/ath/ath9k/beacon.c | 18 +++++++------- drivers/net/wireless/ath/ath9k/debug.c | 6 ++--- drivers/net/wireless/ath/ath9k/link.c | 4 ++-- drivers/net/wireless/ath/ath9k/main.c | 42 ++++++++++++++++----------------- drivers/net/wireless/ath/ath9k/pci.c | 2 +- drivers/net/wireless/ath/ath9k/recv.c | 10 ++++---- drivers/net/wireless/ath/ath9k/xmit.c | 2 +- 9 files changed, 52 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 5e47ca6d16a8..4a4e8a2b9d2c 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -126,7 +126,7 @@ static int ath_ahb_probe(struct platform_device *pdev) sc->irq = irq; /* Will be cleared in ath9k_start() */ - sc->sc_flags |= SC_OP_INVALID; + set_bit(SC_OP_INVALID, &sc->sc_flags); ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4c2ce453af22..ae43de1cbb03 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -604,12 +604,14 @@ void ath_ant_comb_update(struct ath_softc *sc); #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ #define ATH_RATE_DUMMY_MARKER 0 -#define SC_OP_INVALID BIT(0) -#define SC_OP_BEACONS BIT(1) -#define SC_OP_RXFLUSH BIT(2) -#define SC_OP_TSF_RESET BIT(3) -#define SC_OP_ANI_RUN BIT(4) -#define SC_OP_PRIM_STA_VIF BIT(5) +enum sc_op_flags { + SC_OP_INVALID, + SC_OP_BEACONS, + SC_OP_RXFLUSH, + SC_OP_TSF_RESET, + SC_OP_ANI_RUN, + SC_OP_PRIM_STA_VIF, +}; /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) @@ -655,9 +657,9 @@ struct ath_softc { struct completion paprd_complete; unsigned int hw_busy_count; + unsigned long sc_flags; u32 intrstatus; - u32 sc_flags; /* SC_OP_* */ u16 ps_flags; /* PS_* */ u16 curtxpow; bool ps_enabled; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 70b802529123..40775da8941e 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -390,7 +390,7 @@ void ath_beacon_tasklet(unsigned long data) } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); sc->beacon.bmisscnt = 0; - sc->sc_flags |= SC_OP_TSF_RESET; + set_bit(SC_OP_TSF_RESET, &sc->sc_flags); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); } @@ -480,16 +480,16 @@ static void ath9k_beacon_init(struct ath_softc *sc, u32 next_beacon, u32 beacon_period) { - if (sc->sc_flags & SC_OP_TSF_RESET) { + if (test_bit(SC_OP_TSF_RESET, &sc->sc_flags)) { ath9k_ps_wakeup(sc); ath9k_hw_reset_tsf(sc->sc_ah); } ath9k_hw_beaconinit(sc->sc_ah, next_beacon, beacon_period); - if (sc->sc_flags & SC_OP_TSF_RESET) { + if (test_bit(SC_OP_TSF_RESET, &sc->sc_flags)) { ath9k_ps_restore(sc); - sc->sc_flags &= ~SC_OP_TSF_RESET; + clear_bit(SC_OP_TSF_RESET, &sc->sc_flags); } } @@ -519,7 +519,7 @@ static void ath_beacon_config_ap(struct ath_softc *sc, /* Set the computed AP beacon timers */ ath9k_hw_disable_interrupts(ah); - sc->sc_flags |= SC_OP_TSF_RESET; + set_bit(SC_OP_TSF_RESET, &sc->sc_flags); ath9k_beacon_init(sc, nexttbtt, intval); sc->beacon.bmisscnt = 0; ath9k_hw_set_interrupts(ah); @@ -662,7 +662,7 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, u32 tsf, intval, nexttbtt; ath9k_reset_beacon_status(sc); - if (!(sc->sc_flags & SC_OP_BEACONS)) + if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) ath9k_hw_settsf64(ah, sc->beacon.bc_tstamp); intval = TU_TO_USEC(conf->beacon_interval); @@ -727,7 +727,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc, */ if ((sc->sc_ah->opmode == NL80211_IFTYPE_STATION) && (vif->type == NL80211_IFTYPE_STATION) && - (sc->sc_flags & SC_OP_BEACONS) && + test_bit(SC_OP_BEACONS, &sc->sc_flags) && !avp->primary_sta_vif) { ath_dbg(common, CONFIG, "Beacon already configured for a station interface\n"); @@ -813,7 +813,7 @@ void ath_set_beacon(struct ath_softc *sc) return; } - sc->sc_flags |= SC_OP_BEACONS; + set_bit(SC_OP_BEACONS, &sc->sc_flags); } void ath9k_set_beaconing_status(struct ath_softc *sc, bool status) @@ -821,7 +821,7 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status) struct ath_hw *ah = sc->sc_ah; if (!ath_has_valid_bslot(sc)) { - sc->sc_flags &= ~SC_OP_BEACONS; + clear_bit(SC_OP_BEACONS, &sc->sc_flags); return; } diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index c134ddaa10a1..2831258d9507 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -205,10 +205,10 @@ static ssize_t write_file_disable_ani(struct file *file, common->disable_ani = !!disable_ani; if (disable_ani) { - sc->sc_flags &= ~SC_OP_ANI_RUN; + clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); del_timer_sync(&common->ani.timer); } else { - sc->sc_flags |= SC_OP_ANI_RUN; + set_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_start_ani(common); } @@ -1321,7 +1321,7 @@ static int open_file_bb_mac_samps(struct inode *inode, struct file *file) u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; u8 nread; - if (sc->sc_flags & SC_OP_INVALID) + if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return -EAGAIN; buf = vmalloc(size); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 89b38a9ab7c5..0cc4c70f7f0c 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -155,7 +155,7 @@ void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) if (!AR_SREV_9300(sc->sc_ah)) return; - if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) + if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) return; mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies @@ -430,7 +430,7 @@ void ath_start_ani(struct ath_common *common) unsigned long timestamp = jiffies_to_msecs(jiffies); struct ath_softc *sc = (struct ath_softc *) common->priv; - if (!(sc->sc_flags & SC_OP_ANI_RUN)) + if (!test_bit(SC_OP_ANI_RUN, &sc->sc_flags)) return; if (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 66f215ca9969..e4f7d0ea5f20 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -222,7 +222,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_enable_interrupts(ah); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { - if (sc->sc_flags & SC_OP_BEACONS) + if (test_bit(SC_OP_BEACONS, &sc->sc_flags)) ath_set_beacon(sc); ath_restart_work(sc); @@ -293,7 +293,7 @@ static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, { int r; - if (sc->sc_flags & SC_OP_INVALID) + if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return -EIO; r = ath_reset_internal(sc, hchan, false); @@ -435,7 +435,7 @@ irqreturn_t ath_isr(int irq, void *dev) * touch anything. Note this can happen early * on if the IRQ is shared. */ - if (sc->sc_flags & SC_OP_INVALID) + if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return IRQ_NONE; @@ -635,7 +635,7 @@ static int ath9k_start(struct ieee80211_hw *hw) ath_mci_enable(sc); - sc->sc_flags &= ~SC_OP_INVALID; + clear_bit(SC_OP_INVALID, &sc->sc_flags); sc->sc_ah->is_monitoring = false; if (!ath_complete_reset(sc, false)) { @@ -754,7 +754,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath_cancel_work(sc); del_timer_sync(&sc->rx_poll_timer); - if (sc->sc_flags & SC_OP_INVALID) { + if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; @@ -811,7 +811,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_ps_restore(sc); - sc->sc_flags |= SC_OP_INVALID; + set_bit(SC_OP_INVALID, &sc->sc_flags); sc->ps_idle = prev_idle; mutex_unlock(&sc->mutex); @@ -915,11 +915,11 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, /* Set op-mode & TSF */ if (iter_data.naps > 0) { ath9k_hw_set_tsfadjust(ah, 1); - sc->sc_flags |= SC_OP_TSF_RESET; + set_bit(SC_OP_TSF_RESET, &sc->sc_flags); ah->opmode = NL80211_IFTYPE_AP; } else { ath9k_hw_set_tsfadjust(ah, 0); - sc->sc_flags &= ~SC_OP_TSF_RESET; + clear_bit(SC_OP_TSF_RESET, &sc->sc_flags); if (iter_data.nmeshes) ah->opmode = NL80211_IFTYPE_MESH_POINT; @@ -950,12 +950,12 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; if (!common->disable_ani) { - sc->sc_flags |= SC_OP_ANI_RUN; + set_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_start_ani(common); } } else { - sc->sc_flags &= ~SC_OP_ANI_RUN; + clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); del_timer_sync(&common->ani.timer); } } @@ -1479,11 +1479,11 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) * Skip iteration if primary station vif's bss info * was not changed */ - if (sc->sc_flags & SC_OP_PRIM_STA_VIF) + if (test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) return; if (bss_conf->assoc) { - sc->sc_flags |= SC_OP_PRIM_STA_VIF; + set_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags); avp->primary_sta_vif = true; memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); common->curaid = bss_conf->aid; @@ -1504,7 +1504,7 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ath_start_rx_poll(sc, 3); if (!common->disable_ani) { - sc->sc_flags |= SC_OP_ANI_RUN; + set_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_start_ani(common); } @@ -1524,7 +1524,8 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif) if (avp->primary_sta_vif && !bss_conf->assoc) { ath_dbg(common, CONFIG, "Bss Info DISASSOC %d, bssid %pM\n", common->curaid, common->curbssid); - sc->sc_flags &= ~(SC_OP_PRIM_STA_VIF | SC_OP_BEACONS); + clear_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags); + clear_bit(SC_OP_BEACONS, &sc->sc_flags); avp->primary_sta_vif = false; memset(common->curbssid, 0, ETH_ALEN); common->curaid = 0; @@ -1537,10 +1538,9 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif) * None of station vifs are associated. * Clear bssid & aid */ - if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) { + if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { ath9k_hw_write_associd(sc->sc_ah); - /* Stop ANI */ - sc->sc_flags &= ~SC_OP_ANI_RUN; + clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); del_timer_sync(&common->ani.timer); del_timer_sync(&sc->rx_poll_timer); memset(&sc->caldata, 0, sizeof(sc->caldata)); @@ -1578,12 +1578,12 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; if (!common->disable_ani) { - sc->sc_flags |= SC_OP_ANI_RUN; + set_bit(SC_OP_ANI_RUN, &sc->sc_flags); ath_start_ani(common); } } else { - sc->sc_flags &= ~SC_OP_ANI_RUN; + clear_bit(SC_OP_ANI_RUN, &sc->sc_flags); del_timer_sync(&common->ani.timer); del_timer_sync(&sc->rx_poll_timer); } @@ -1595,7 +1595,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, */ if ((changed & BSS_CHANGED_BEACON_INT) && (vif->type == NL80211_IFTYPE_AP)) - sc->sc_flags |= SC_OP_TSF_RESET; + set_bit(SC_OP_TSF_RESET, &sc->sc_flags); /* Configure beaconing (AP, IBSS, MESH) */ if (ath9k_uses_beacons(vif->type) && @@ -1787,7 +1787,7 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) return; } - if (sc->sc_flags & SC_OP_INVALID) { + if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&sc->mutex); return; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 6ec9f88712d0..aa0e83ac51f4 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -251,7 +251,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->mem = mem; /* Will be cleared in ath9k_start() */ - sc->sc_flags |= SC_OP_INVALID; + set_bit(SC_OP_INVALID, &sc->sc_flags); ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index c2f5fd1c8b87..b54e15941ba4 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -285,8 +285,8 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) int error = 0; spin_lock_init(&sc->sc_pcu_lock); - sc->sc_flags &= ~SC_OP_RXFLUSH; spin_lock_init(&sc->rx.rxbuflock); + clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 + sc->sc_ah->caps.rx_status_len; @@ -498,11 +498,11 @@ bool ath_stoprecv(struct ath_softc *sc) void ath_flushrecv(struct ath_softc *sc) { - sc->sc_flags |= SC_OP_RXFLUSH; + set_bit(SC_OP_RXFLUSH, &sc->sc_flags); if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_rx_tasklet(sc, 1, true); ath_rx_tasklet(sc, 1, false); - sc->sc_flags &= ~SC_OP_RXFLUSH; + clear_bit(SC_OP_RXFLUSH, &sc->sc_flags); } static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) @@ -1063,7 +1063,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) do { /* If handling rx interrupt and flush is in progress => exit */ - if ((sc->sc_flags & SC_OP_RXFLUSH) && (flush == 0)) + if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0)) break; memset(&rs, 0, sizeof(rs)); @@ -1108,7 +1108,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) * If we're asked to flush receive queue, directly * chain it back at the queue without processing it. */ - if (sc->sc_flags & SC_OP_RXFLUSH) { + if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) { RX_STAT_INC(rx_drop_rxflush); goto requeue_drop_frag; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 6619a39b8c27..bb74780903d5 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1536,7 +1536,7 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) int i; u32 npend = 0; - if (sc->sc_flags & SC_OP_INVALID) + if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return true; ath9k_hw_abort_tx_dma(ah); -- cgit v1.2.3 From b74713d04effbacd3d126ce94cec18742187b6ce Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:24:01 +0530 Subject: ath9k: Handle fatal interrupts properly When a fatal interrupt is received or it is detected that the baseband has hung, the chip has to be reset immediately. Otherwise, we end up processing spurious interrupts. Ensure that we bail out properly in the ISR when the reset work hasn't completed yet. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index ae43de1cbb03..02fc1c1e5eeb 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -611,6 +611,7 @@ enum sc_op_flags { SC_OP_TSF_RESET, SC_OP_ANI_RUN, SC_OP_PRIM_STA_VIF, + SC_OP_HW_RESET, }; /* Powersave flags */ diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e4f7d0ea5f20..34d6f26c6bc7 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -218,6 +218,8 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_cmn_update_txpow(ah, sc->curtxpow, sc->config.txpowlimit, &sc->curtxpow); + + clear_bit(SC_OP_HW_RESET, &sc->sc_flags); ath9k_hw_set_interrupts(ah); ath9k_hw_enable_interrupts(ah); @@ -362,6 +364,7 @@ void ath9k_tasklet(unsigned long data) RESET_STAT_INC(sc, type); #endif + set_bit(SC_OP_HW_RESET, &sc->sc_flags); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); goto out; } @@ -438,12 +441,14 @@ irqreturn_t ath_isr(int irq, void *dev) if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return IRQ_NONE; - /* shared irq, not for us */ if (!ath9k_hw_intrpend(ah)) return IRQ_NONE; + if(test_bit(SC_OP_HW_RESET, &sc->sc_flags)) + return IRQ_HANDLED; + /* * Figure out the reason(s) for the interrupt. Note * that the hal returns a pseudo-ISR that may include -- cgit v1.2.3 From 07c15a3ffd68ff1a3276daa26885b277f19e4abd Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:24:07 +0530 Subject: ath9k: Fix powersave locking The 'ps_flags' is used/accessed in a variety of contexts and requires proper locking. Use 'sc_pm_lock' appropriately. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 14 ++++++++++++-- drivers/net/wireless/ath/ath9k/recv.c | 13 ++++++------- drivers/net/wireless/ath/ath9k/xmit.c | 3 +++ 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 34d6f26c6bc7..88f7ad106e05 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -345,7 +345,7 @@ void ath9k_tasklet(unsigned long data) struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - + unsigned long flags; u32 status = sc->intrstatus; u32 rxmask; @@ -369,6 +369,7 @@ void ath9k_tasklet(unsigned long data) goto out; } + spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* * TSF sync does not look correct; remain awake to sync with @@ -377,6 +378,7 @@ void ath9k_tasklet(unsigned long data) ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n"); sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC; } + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL | @@ -526,8 +528,10 @@ irqreturn_t ath_isr(int irq, void *dev) /* Clear RxAbort bit so that we can * receive frames */ ath9k_setpower(sc, ATH9K_PM_AWAKE); + spin_lock(&sc->sc_pm_lock); ath9k_hw_setrxabort(sc->sc_ah, 0); sc->ps_flags |= PS_WAIT_FOR_BEACON; + spin_unlock(&sc->sc_pm_lock); } chip_reset: @@ -682,6 +686,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_tx_control txctl; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + unsigned long flags; if (sc->ps_enabled) { /* @@ -704,6 +709,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * completed and if needed, also for RX of buffered frames. */ ath9k_ps_wakeup(sc); + spin_lock_irqsave(&sc->sc_pm_lock, flags); if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) ath9k_hw_setrxabort(sc->sc_ah, 0); if (ieee80211_is_pspoll(hdr->frame_control)) { @@ -719,6 +725,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * the ps_flags bit is cleared. We are just dropping * the ps_usecount here. */ + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_ps_restore(sc); } @@ -1479,7 +1486,7 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; struct ath_vif *avp = (void *)vif->drv_priv; - + unsigned long flags; /* * Skip iteration if primary station vif's bss info * was not changed @@ -1501,7 +1508,10 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) * on the receipt of the first Beacon frame (i.e., * after time sync with the AP). */ + spin_lock_irqsave(&sc->sc_pm_lock, flags); sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + /* Reset rssi stats */ sc->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index b54e15941ba4..fbdcc80437fe 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -587,13 +587,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) /* Process Beacon and CAB receive in PS state */ if (((sc->ps_flags & PS_WAIT_FOR_BEACON) || ath9k_check_auto_sleep(sc)) - && mybeacon) + && mybeacon) { ath_rx_ps_beacon(sc, skb); - else if ((sc->ps_flags & PS_WAIT_FOR_CAB) && - (ieee80211_is_data(hdr->frame_control) || - ieee80211_is_action(hdr->frame_control)) && - is_multicast_ether_addr(hdr->addr1) && - !ieee80211_has_moredata(hdr->frame_control)) { + } else if ((sc->ps_flags & PS_WAIT_FOR_CAB) && + (ieee80211_is_data(hdr->frame_control) || + ieee80211_is_action(hdr->frame_control)) && + is_multicast_ether_addr(hdr->addr1) && + !ieee80211_has_moredata(hdr->frame_control)) { /* * No more broadcast/multicast frames to be received at this * point. @@ -1229,7 +1229,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) skb_trim(skb, skb->len - 8); spin_lock_irqsave(&sc->sc_pm_lock, flags); - if ((sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA)) || diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index bb74780903d5..f777ddcd1172 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1994,6 +1994,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_hdr * hdr = (struct ieee80211_hdr *)skb->data; int q, padpos, padsize; + unsigned long flags; ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb); @@ -2012,6 +2013,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, skb_pull(skb, padsize); } + spin_lock_irqsave(&sc->sc_pm_lock, flags); if ((sc->ps_flags & PS_WAIT_FOR_TX_ACK) && !txq->axq_depth) { sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; ath_dbg(common, PS, @@ -2021,6 +2023,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, PS_WAIT_FOR_PSPOLL_DATA | PS_WAIT_FOR_TX_ACK)); } + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); q = skb_get_queue_mapping(skb); if (txq == sc->tx.txq_map[q]) { -- cgit v1.2.3 From 196fb860ceb6b937cbe608ea3ea0209bd4edf3b2 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 4 Jun 2012 20:24:13 +0530 Subject: ath9k: Resync beacons properly After a chip reset, the beacon timers have to re-programmed correctly for a station in associated state. Use the PS flags to ensure that this is done after a TSF sync happens, otherwise the driver ends up using incorrect values for TBTT/DTIM in powersave mode. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 88f7ad106e05..c0f478b0a9a2 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -210,6 +210,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + unsigned long flags; if (ath_startrecv(sc) != 0) { ath_err(common, "Unable to restart recv logic\n"); @@ -224,9 +225,18 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ath9k_hw_enable_interrupts(ah); if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) { - if (test_bit(SC_OP_BEACONS, &sc->sc_flags)) - ath_set_beacon(sc); + if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) + goto work; + + ath_set_beacon(sc); + if (ah->opmode == NL80211_IFTYPE_STATION && + test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { + spin_lock_irqsave(&sc->sc_pm_lock, flags); + sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON; + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + } + work: ath_restart_work(sc); } -- cgit v1.2.3 From 491b26b40222cc769c163e77177697dd7a63c316 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jun 2012 14:21:14 +0200 Subject: wireless: update wireless URLs The preferred URL is http://wireless.kernel.org/ rather than http://linuxwireless.org/, update all URLs to point there. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/b43legacy/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index cd9c9bc186d9..8b06ca56125e 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -1508,7 +1508,7 @@ static void b43legacy_release_firmware(struct b43legacy_wldev *dev) static void b43legacy_print_fw_helptext(struct b43legacy_wl *wl) { - b43legacyerr(wl, "You must go to http://linuxwireless.org/en/users/" + b43legacyerr(wl, "You must go to http://wireless.kernel.org/en/users/" "Drivers/b43#devicefirmware " "and download the correct firmware (version 3).\n"); } -- cgit v1.2.3 From 33f8100977693fa09c2a32b1ca6dbf4d6eabdd0c Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Tue, 29 May 2012 11:13:15 +0530 Subject: can: c_can: Move overlay structure to array with offset as index c_can uses overlay structure for accessing c_can module registers. With this kind of implementation it is difficult to add one more ip which is similar to c_can in functionality but different register offsets. This patch changes the overlay structure implementation to an array with register offset as index. This way we can overcome the above limitation. Signed-off-by: AnilKumar Ch Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/c_can.c | 120 +++++++++++++++++---------------- drivers/net/can/c_can/c_can.h | 118 +++++++++++++++++++++----------- drivers/net/can/c_can/c_can_platform.c | 23 ++++--- 3 files changed, 154 insertions(+), 107 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 8dc84d66eea1..e2ce508c2753 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -41,6 +41,10 @@ #include "c_can.h" +/* Number of interface registers */ +#define IF_ENUM_REG_LEN 11 +#define C_CAN_IFACE(reg, iface) (C_CAN_IF1_##reg + (iface) * IF_ENUM_REG_LEN) + /* control register */ #define CONTROL_TEST BIT(7) #define CONTROL_CCE BIT(6) @@ -209,10 +213,10 @@ static inline int get_tx_echo_msg_obj(const struct c_can_priv *priv) C_CAN_MSG_OBJ_TX_FIRST; } -static u32 c_can_read_reg32(struct c_can_priv *priv, void *reg) +static u32 c_can_read_reg32(struct c_can_priv *priv, enum reg index) { - u32 val = priv->read_reg(priv, reg); - val |= ((u32) priv->read_reg(priv, reg + 2)) << 16; + u32 val = priv->read_reg(priv, index); + val |= ((u32) priv->read_reg(priv, index + 1)) << 16; return val; } @@ -220,14 +224,14 @@ static void c_can_enable_all_interrupts(struct c_can_priv *priv, int enable) { unsigned int cntrl_save = priv->read_reg(priv, - &priv->regs->control); + C_CAN_CTRL_REG); if (enable) cntrl_save |= (CONTROL_SIE | CONTROL_EIE | CONTROL_IE); else cntrl_save &= ~(CONTROL_EIE | CONTROL_IE | CONTROL_SIE); - priv->write_reg(priv, &priv->regs->control, cntrl_save); + priv->write_reg(priv, C_CAN_CTRL_REG, cntrl_save); } static inline int c_can_msg_obj_is_busy(struct c_can_priv *priv, int iface) @@ -235,7 +239,7 @@ static inline int c_can_msg_obj_is_busy(struct c_can_priv *priv, int iface) int count = MIN_TIMEOUT_VALUE; while (count && priv->read_reg(priv, - &priv->regs->ifregs[iface].com_req) & + C_CAN_IFACE(COMREQ_REG, iface)) & IF_COMR_BUSY) { count--; udelay(1); @@ -258,9 +262,9 @@ static inline void c_can_object_get(struct net_device *dev, * register and message RAM must be complete in 6 CAN-CLK * period. */ - priv->write_reg(priv, &priv->regs->ifregs[iface].com_mask, + priv->write_reg(priv, C_CAN_IFACE(COMMSK_REG, iface), IFX_WRITE_LOW_16BIT(mask)); - priv->write_reg(priv, &priv->regs->ifregs[iface].com_req, + priv->write_reg(priv, C_CAN_IFACE(COMREQ_REG, iface), IFX_WRITE_LOW_16BIT(objno)); if (c_can_msg_obj_is_busy(priv, iface)) @@ -278,9 +282,9 @@ static inline void c_can_object_put(struct net_device *dev, * register and message RAM must be complete in 6 CAN-CLK * period. */ - priv->write_reg(priv, &priv->regs->ifregs[iface].com_mask, + priv->write_reg(priv, C_CAN_IFACE(COMMSK_REG, iface), (IF_COMM_WR | IFX_WRITE_LOW_16BIT(mask))); - priv->write_reg(priv, &priv->regs->ifregs[iface].com_req, + priv->write_reg(priv, C_CAN_IFACE(COMREQ_REG, iface), IFX_WRITE_LOW_16BIT(objno)); if (c_can_msg_obj_is_busy(priv, iface)) @@ -306,18 +310,18 @@ static void c_can_write_msg_object(struct net_device *dev, flags |= IF_ARB_MSGVAL; - priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, + priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), IFX_WRITE_LOW_16BIT(id)); - priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, flags | + priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), flags | IFX_WRITE_HIGH_16BIT(id)); for (i = 0; i < frame->can_dlc; i += 2) { - priv->write_reg(priv, &priv->regs->ifregs[iface].data[i / 2], + priv->write_reg(priv, C_CAN_IFACE(DATA1_REG, iface) + i / 2, frame->data[i] | (frame->data[i + 1] << 8)); } /* enable interrupt for this message object */ - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), IF_MCONT_TXIE | IF_MCONT_TXRQST | IF_MCONT_EOB | frame->can_dlc); c_can_object_put(dev, iface, objno, IF_COMM_ALL); @@ -329,7 +333,7 @@ static inline void c_can_mark_rx_msg_obj(struct net_device *dev, { struct c_can_priv *priv = netdev_priv(dev); - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND)); c_can_object_put(dev, iface, obj, IF_COMM_CONTROL); @@ -343,7 +347,7 @@ static inline void c_can_activate_all_lower_rx_msg_obj(struct net_device *dev, struct c_can_priv *priv = netdev_priv(dev); for (i = C_CAN_MSG_OBJ_RX_FIRST; i <= C_CAN_MSG_RX_LOW_LAST; i++) { - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT)); c_can_object_put(dev, iface, i, IF_COMM_CONTROL); @@ -356,7 +360,7 @@ static inline void c_can_activate_rx_msg_obj(struct net_device *dev, { struct c_can_priv *priv = netdev_priv(dev); - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl_mask & ~(IF_MCONT_MSGLST | IF_MCONT_INTPND | IF_MCONT_NEWDAT)); c_can_object_put(dev, iface, obj, IF_COMM_CONTROL); @@ -374,7 +378,7 @@ static void c_can_handle_lost_msg_obj(struct net_device *dev, c_can_object_get(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST); - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), IF_MCONT_CLR_MSGLST); c_can_object_put(dev, 0, objno, IF_COMM_CONTROL); @@ -410,8 +414,8 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl) frame->can_dlc = get_can_dlc(ctrl & 0x0F); - flags = priv->read_reg(priv, &priv->regs->ifregs[iface].arb2); - val = priv->read_reg(priv, &priv->regs->ifregs[iface].arb1) | + flags = priv->read_reg(priv, C_CAN_IFACE(ARB2_REG, iface)); + val = priv->read_reg(priv, C_CAN_IFACE(ARB1_REG, iface)) | (flags << 16); if (flags & IF_ARB_MSGXTD) @@ -424,7 +428,7 @@ static int c_can_read_msg_object(struct net_device *dev, int iface, int ctrl) else { for (i = 0; i < frame->can_dlc; i += 2) { data = priv->read_reg(priv, - &priv->regs->ifregs[iface].data[i / 2]); + C_CAN_IFACE(DATA1_REG, iface) + i / 2); frame->data[i] = data; frame->data[i + 1] = data >> 8; } @@ -444,40 +448,40 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface, { struct c_can_priv *priv = netdev_priv(dev); - priv->write_reg(priv, &priv->regs->ifregs[iface].mask1, + priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), IFX_WRITE_LOW_16BIT(mask)); - priv->write_reg(priv, &priv->regs->ifregs[iface].mask2, + priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), IFX_WRITE_HIGH_16BIT(mask)); - priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, + priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), IFX_WRITE_LOW_16BIT(id)); - priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, + priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), (IF_ARB_MSGVAL | IFX_WRITE_HIGH_16BIT(id))); - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, mcont); + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), mcont); c_can_object_put(dev, iface, objno, IF_COMM_ALL & ~IF_COMM_TXRQST); netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, - c_can_read_reg32(priv, &priv->regs->msgval1)); + c_can_read_reg32(priv, C_CAN_MSGVAL1_REG)); } static void c_can_inval_msg_object(struct net_device *dev, int iface, int objno) { struct c_can_priv *priv = netdev_priv(dev); - priv->write_reg(priv, &priv->regs->ifregs[iface].arb1, 0); - priv->write_reg(priv, &priv->regs->ifregs[iface].arb2, 0); - priv->write_reg(priv, &priv->regs->ifregs[iface].msg_cntrl, 0); + priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), 0); + priv->write_reg(priv, C_CAN_IFACE(ARB2_REG, iface), 0); + priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), 0); c_can_object_put(dev, iface, objno, IF_COMM_ARB | IF_COMM_CONTROL); netdev_dbg(dev, "obj no:%d, msgval:0x%08x\n", objno, - c_can_read_reg32(priv, &priv->regs->msgval1)); + c_can_read_reg32(priv, C_CAN_MSGVAL1_REG)); } static inline int c_can_is_next_tx_obj_busy(struct c_can_priv *priv, int objno) { - int val = c_can_read_reg32(priv, &priv->regs->txrqst1); + int val = c_can_read_reg32(priv, C_CAN_TXRQST1_REG); /* * as transmission request register's bit n-1 corresponds to @@ -540,12 +544,12 @@ static int c_can_set_bittiming(struct net_device *dev) netdev_info(dev, "setting BTR=%04x BRPE=%04x\n", reg_btr, reg_brpe); - ctrl_save = priv->read_reg(priv, &priv->regs->control); - priv->write_reg(priv, &priv->regs->control, + ctrl_save = priv->read_reg(priv, C_CAN_CTRL_REG); + priv->write_reg(priv, C_CAN_CTRL_REG, ctrl_save | CONTROL_CCE | CONTROL_INIT); - priv->write_reg(priv, &priv->regs->btr, reg_btr); - priv->write_reg(priv, &priv->regs->brp_ext, reg_brpe); - priv->write_reg(priv, &priv->regs->control, ctrl_save); + priv->write_reg(priv, C_CAN_BTR_REG, reg_btr); + priv->write_reg(priv, C_CAN_BRPEXT_REG, reg_brpe); + priv->write_reg(priv, C_CAN_CTRL_REG, ctrl_save); return 0; } @@ -587,36 +591,36 @@ static void c_can_chip_config(struct net_device *dev) struct c_can_priv *priv = netdev_priv(dev); /* enable automatic retransmission */ - priv->write_reg(priv, &priv->regs->control, + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_ENABLE_AR); if (priv->can.ctrlmode & (CAN_CTRLMODE_LISTENONLY & CAN_CTRLMODE_LOOPBACK)) { /* loopback + silent mode : useful for hot self-test */ - priv->write_reg(priv, &priv->regs->control, CONTROL_EIE | + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_EIE | CONTROL_SIE | CONTROL_IE | CONTROL_TEST); - priv->write_reg(priv, &priv->regs->test, + priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK | TEST_SILENT); } else if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { /* loopback mode : useful for self-test function */ - priv->write_reg(priv, &priv->regs->control, CONTROL_EIE | + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_EIE | CONTROL_SIE | CONTROL_IE | CONTROL_TEST); - priv->write_reg(priv, &priv->regs->test, TEST_LBACK); + priv->write_reg(priv, C_CAN_TEST_REG, TEST_LBACK); } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { /* silent mode : bus-monitoring mode */ - priv->write_reg(priv, &priv->regs->control, CONTROL_EIE | + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_EIE | CONTROL_SIE | CONTROL_IE | CONTROL_TEST); - priv->write_reg(priv, &priv->regs->test, TEST_SILENT); + priv->write_reg(priv, C_CAN_TEST_REG, TEST_SILENT); } else /* normal mode*/ - priv->write_reg(priv, &priv->regs->control, + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_EIE | CONTROL_SIE | CONTROL_IE); /* configure message objects */ c_can_configure_msg_objects(dev); /* set a `lec` value so that we can check for updates later */ - priv->write_reg(priv, &priv->regs->status, LEC_UNUSED); + priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED); /* set bittiming params */ c_can_set_bittiming(dev); @@ -669,7 +673,7 @@ static int c_can_get_berr_counter(const struct net_device *dev, unsigned int reg_err_counter; struct c_can_priv *priv = netdev_priv(dev); - reg_err_counter = priv->read_reg(priv, &priv->regs->err_cnt); + reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); bec->rxerr = (reg_err_counter & ERR_CNT_REC_MASK) >> ERR_CNT_REC_SHIFT; bec->txerr = reg_err_counter & ERR_CNT_TEC_MASK; @@ -697,12 +701,12 @@ static void c_can_do_tx(struct net_device *dev) for (/* nix */; (priv->tx_next - priv->tx_echo) > 0; priv->tx_echo++) { msg_obj_no = get_tx_echo_msg_obj(priv); - val = c_can_read_reg32(priv, &priv->regs->txrqst1); + val = c_can_read_reg32(priv, C_CAN_TXRQST1_REG); if (!(val & (1 << (msg_obj_no - 1)))) { can_get_echo_skb(dev, msg_obj_no - C_CAN_MSG_OBJ_TX_FIRST); stats->tx_bytes += priv->read_reg(priv, - &priv->regs->ifregs[0].msg_cntrl) + C_CAN_IFACE(MSGCTRL_REG, 0)) & IF_MCONT_DLC_MASK; stats->tx_packets++; c_can_inval_msg_object(dev, 0, msg_obj_no); @@ -744,11 +748,11 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota) u32 num_rx_pkts = 0; unsigned int msg_obj, msg_ctrl_save; struct c_can_priv *priv = netdev_priv(dev); - u32 val = c_can_read_reg32(priv, &priv->regs->intpnd1); + u32 val = c_can_read_reg32(priv, C_CAN_INTPND1_REG); for (msg_obj = C_CAN_MSG_OBJ_RX_FIRST; msg_obj <= C_CAN_MSG_OBJ_RX_LAST && quota > 0; - val = c_can_read_reg32(priv, &priv->regs->intpnd1), + val = c_can_read_reg32(priv, C_CAN_INTPND1_REG), msg_obj++) { /* * as interrupt pending register's bit n-1 corresponds to @@ -758,7 +762,7 @@ static int c_can_do_rx_poll(struct net_device *dev, int quota) c_can_object_get(dev, 0, msg_obj, IF_COMM_ALL & ~IF_COMM_TXRQST); msg_ctrl_save = priv->read_reg(priv, - &priv->regs->ifregs[0].msg_cntrl); + C_CAN_IFACE(MSGCTRL_REG, 0)); if (msg_ctrl_save & IF_MCONT_EOB) return num_rx_pkts; @@ -819,7 +823,7 @@ static int c_can_handle_state_change(struct net_device *dev, return 0; c_can_get_berr_counter(dev, &bec); - reg_err_counter = priv->read_reg(priv, &priv->regs->err_cnt); + reg_err_counter = priv->read_reg(priv, C_CAN_ERR_CNT_REG); rx_err_passive = (reg_err_counter & ERR_CNT_RP_MASK) >> ERR_CNT_RP_SHIFT; @@ -935,7 +939,7 @@ static int c_can_handle_bus_err(struct net_device *dev, } /* set a `lec` value so that we can check for updates later */ - priv->write_reg(priv, &priv->regs->status, LEC_UNUSED); + priv->write_reg(priv, C_CAN_STS_REG, LEC_UNUSED); netif_receive_skb(skb); stats->rx_packets++; @@ -959,15 +963,15 @@ static int c_can_poll(struct napi_struct *napi, int quota) /* status events have the highest priority */ if (irqstatus == STATUS_INTERRUPT) { priv->current_status = priv->read_reg(priv, - &priv->regs->status); + C_CAN_STS_REG); /* handle Tx/Rx events */ if (priv->current_status & STATUS_TXOK) - priv->write_reg(priv, &priv->regs->status, + priv->write_reg(priv, C_CAN_STS_REG, priv->current_status & ~STATUS_TXOK); if (priv->current_status & STATUS_RXOK) - priv->write_reg(priv, &priv->regs->status, + priv->write_reg(priv, C_CAN_STS_REG, priv->current_status & ~STATUS_RXOK); /* handle state changes */ @@ -1033,7 +1037,7 @@ static irqreturn_t c_can_isr(int irq, void *dev_id) struct net_device *dev = (struct net_device *)dev_id; struct c_can_priv *priv = netdev_priv(dev); - priv->irqstatus = priv->read_reg(priv, &priv->regs->interrupt); + priv->irqstatus = priv->read_reg(priv, C_CAN_INT_REG); if (!priv->irqstatus) return IRQ_NONE; diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index 5f32d34af507..d1e141e3b99c 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -22,43 +22,84 @@ #ifndef C_CAN_H #define C_CAN_H -/* c_can IF registers */ -struct c_can_if_regs { - u16 com_req; - u16 com_mask; - u16 mask1; - u16 mask2; - u16 arb1; - u16 arb2; - u16 msg_cntrl; - u16 data[4]; - u16 _reserved[13]; +enum reg { + C_CAN_CTRL_REG = 0, + C_CAN_STS_REG, + C_CAN_ERR_CNT_REG, + C_CAN_BTR_REG, + C_CAN_INT_REG, + C_CAN_TEST_REG, + C_CAN_BRPEXT_REG, + C_CAN_IF1_COMREQ_REG, + C_CAN_IF1_COMMSK_REG, + C_CAN_IF1_MASK1_REG, + C_CAN_IF1_MASK2_REG, + C_CAN_IF1_ARB1_REG, + C_CAN_IF1_ARB2_REG, + C_CAN_IF1_MSGCTRL_REG, + C_CAN_IF1_DATA1_REG, + C_CAN_IF1_DATA2_REG, + C_CAN_IF1_DATA3_REG, + C_CAN_IF1_DATA4_REG, + C_CAN_IF2_COMREQ_REG, + C_CAN_IF2_COMMSK_REG, + C_CAN_IF2_MASK1_REG, + C_CAN_IF2_MASK2_REG, + C_CAN_IF2_ARB1_REG, + C_CAN_IF2_ARB2_REG, + C_CAN_IF2_MSGCTRL_REG, + C_CAN_IF2_DATA1_REG, + C_CAN_IF2_DATA2_REG, + C_CAN_IF2_DATA3_REG, + C_CAN_IF2_DATA4_REG, + C_CAN_TXRQST1_REG, + C_CAN_TXRQST2_REG, + C_CAN_NEWDAT1_REG, + C_CAN_NEWDAT2_REG, + C_CAN_INTPND1_REG, + C_CAN_INTPND2_REG, + C_CAN_MSGVAL1_REG, + C_CAN_MSGVAL2_REG, }; -/* c_can hardware registers */ -struct c_can_regs { - u16 control; - u16 status; - u16 err_cnt; - u16 btr; - u16 interrupt; - u16 test; - u16 brp_ext; - u16 _reserved1; - struct c_can_if_regs ifregs[2]; /* [0] = IF1 and [1] = IF2 */ - u16 _reserved2[8]; - u16 txrqst1; - u16 txrqst2; - u16 _reserved3[6]; - u16 newdat1; - u16 newdat2; - u16 _reserved4[6]; - u16 intpnd1; - u16 intpnd2; - u16 _reserved5[6]; - u16 msgval1; - u16 msgval2; - u16 _reserved6[6]; +static const u16 reg_map_c_can[] = { + [C_CAN_CTRL_REG] = 0x00, + [C_CAN_STS_REG] = 0x02, + [C_CAN_ERR_CNT_REG] = 0x04, + [C_CAN_BTR_REG] = 0x06, + [C_CAN_INT_REG] = 0x08, + [C_CAN_TEST_REG] = 0x0A, + [C_CAN_BRPEXT_REG] = 0x0C, + [C_CAN_IF1_COMREQ_REG] = 0x10, + [C_CAN_IF1_COMMSK_REG] = 0x12, + [C_CAN_IF1_MASK1_REG] = 0x14, + [C_CAN_IF1_MASK2_REG] = 0x16, + [C_CAN_IF1_ARB1_REG] = 0x18, + [C_CAN_IF1_ARB2_REG] = 0x1A, + [C_CAN_IF1_MSGCTRL_REG] = 0x1C, + [C_CAN_IF1_DATA1_REG] = 0x1E, + [C_CAN_IF1_DATA2_REG] = 0x20, + [C_CAN_IF1_DATA3_REG] = 0x22, + [C_CAN_IF1_DATA4_REG] = 0x24, + [C_CAN_IF2_COMREQ_REG] = 0x40, + [C_CAN_IF2_COMMSK_REG] = 0x42, + [C_CAN_IF2_MASK1_REG] = 0x44, + [C_CAN_IF2_MASK2_REG] = 0x46, + [C_CAN_IF2_ARB1_REG] = 0x48, + [C_CAN_IF2_ARB2_REG] = 0x4A, + [C_CAN_IF2_MSGCTRL_REG] = 0x4C, + [C_CAN_IF2_DATA1_REG] = 0x4E, + [C_CAN_IF2_DATA2_REG] = 0x50, + [C_CAN_IF2_DATA3_REG] = 0x52, + [C_CAN_IF2_DATA4_REG] = 0x54, + [C_CAN_TXRQST1_REG] = 0x80, + [C_CAN_TXRQST2_REG] = 0x82, + [C_CAN_NEWDAT1_REG] = 0x90, + [C_CAN_NEWDAT2_REG] = 0x92, + [C_CAN_INTPND1_REG] = 0xA0, + [C_CAN_INTPND2_REG] = 0xA2, + [C_CAN_MSGVAL1_REG] = 0xB0, + [C_CAN_MSGVAL2_REG] = 0xB2, }; /* c_can private data structure */ @@ -69,9 +110,10 @@ struct c_can_priv { int tx_object; int current_status; int last_status; - u16 (*read_reg) (struct c_can_priv *priv, void *reg); - void (*write_reg) (struct c_can_priv *priv, void *reg, u16 val); - struct c_can_regs __iomem *regs; + u16 (*read_reg) (struct c_can_priv *priv, enum reg index); + void (*write_reg) (struct c_can_priv *priv, enum reg index, u16 val); + void __iomem *base; + const u16 *regs; unsigned long irq_flags; /* for request_irq() */ unsigned int tx_next; unsigned int tx_echo; diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 5e1a5ff6476e..a6e9b937683a 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -42,27 +42,27 @@ * Handle the same by providing a common read/write interface. */ static u16 c_can_plat_read_reg_aligned_to_16bit(struct c_can_priv *priv, - void *reg) + enum reg index) { - return readw(reg); + return readw(priv->base + priv->regs[index]); } static void c_can_plat_write_reg_aligned_to_16bit(struct c_can_priv *priv, - void *reg, u16 val) + enum reg index, u16 val) { - writew(val, reg); + writew(val, priv->base + priv->regs[index]); } static u16 c_can_plat_read_reg_aligned_to_32bit(struct c_can_priv *priv, - void *reg) + enum reg index) { - return readw(reg + (long)reg - (long)priv->regs); + return readw(priv->base + 2 * priv->regs[index]); } static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv, - void *reg, u16 val) + enum reg index, u16 val) { - writew(val, reg + (long)reg - (long)priv->regs); + writew(val, priv->base + 2 * priv->regs[index]); } static int __devinit c_can_plat_probe(struct platform_device *pdev) @@ -115,9 +115,10 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) } priv = netdev_priv(dev); + priv->regs = reg_map_c_can; dev->irq = irq; - priv->regs = addr; + priv->base = addr; #ifdef CONFIG_HAVE_CLK priv->can.clock.freq = clk_get_rate(clk); priv->priv = clk; @@ -146,7 +147,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) } dev_info(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", - KBUILD_MODNAME, priv->regs, dev->irq); + KBUILD_MODNAME, priv->base, dev->irq); return 0; exit_free_device: @@ -176,7 +177,7 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); free_c_can_dev(dev); - iounmap(priv->regs); + iounmap(priv->base); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(mem->start, resource_size(mem)); -- cgit v1.2.3 From 69927fccd96b15bd228bb82d356a7a2a0cfaeefb Mon Sep 17 00:00:00 2001 From: AnilKumar Ch Date: Tue, 29 May 2012 11:13:16 +0530 Subject: can: c_can: Add support for Bosch D_CAN controller This patch adds the support for D_CAN controller driver to the existing C_CAN driver. Bosch D_CAN controller is a full-CAN implementation which is compliant to CAN protocol version 2.0 part A and B. Bosch D_CAN user manual can be obtained from: http://www.semiconductors.bosch.de/media/en/pdf/ ipmodules_1/can/d_can_users_manual_111.pdf A new array is added for accessing the d_can registers, according to d_can controller register space. Current D_CAN implementation has following limitations, this is done to avoid large changes to the C_CAN driver. 1. Message objects are limited to 32, 16 for RX and 16 for TX. C_CAN IP supports upto 32 message objects but in case of D_CAN we can configure upto 128 message objects. 2. Using two 16bit reads/writes for accessing the 32bit D_CAN registers. 3. These patches have been tested on little endian machine, there might be some hidden endian-related issues due to the nature of the accesses (32-bit registers accessed as 2 16-bit registers). However, I do not have a big-endian D_CAN implementation to confirm. Signed-off-by: AnilKumar Ch Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/Kconfig | 13 ++++---- drivers/net/can/c_can/c_can.h | 45 ++++++++++++++++++++++++++++ drivers/net/can/c_can/c_can_platform.c | 55 ++++++++++++++++++++++++++-------- 3 files changed, 94 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig index ffb9773d102d..25d371cf98dd 100644 --- a/drivers/net/can/c_can/Kconfig +++ b/drivers/net/can/c_can/Kconfig @@ -1,15 +1,16 @@ menuconfig CAN_C_CAN - tristate "Bosch C_CAN devices" + tristate "Bosch C_CAN/D_CAN devices" depends on CAN_DEV && HAS_IOMEM if CAN_C_CAN config CAN_C_CAN_PLATFORM - tristate "Generic Platform Bus based C_CAN driver" + tristate "Generic Platform Bus based C_CAN/D_CAN driver" ---help--- - This driver adds support for the C_CAN chips connected to - the "platform bus" (Linux abstraction for directly to the + This driver adds support for the C_CAN/D_CAN chips connected + to the "platform bus" (Linux abstraction for directly to the processor attached devices) which can be found on various - boards from ST Microelectronics (http://www.st.com) - like the SPEAr1310 and SPEAr320 evaluation boards. + boards from ST Microelectronics (http://www.st.com) like the + SPEAr1310 and SPEAr320 evaluation boards & TI (www.ti.com) + boards like am335x, dm814x, dm813x and dm811x. endif diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index d1e141e3b99c..01a7049ab990 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -102,6 +102,51 @@ static const u16 reg_map_c_can[] = { [C_CAN_MSGVAL2_REG] = 0xB2, }; +static const u16 reg_map_d_can[] = { + [C_CAN_CTRL_REG] = 0x00, + [C_CAN_STS_REG] = 0x04, + [C_CAN_ERR_CNT_REG] = 0x08, + [C_CAN_BTR_REG] = 0x0C, + [C_CAN_BRPEXT_REG] = 0x0E, + [C_CAN_INT_REG] = 0x10, + [C_CAN_TEST_REG] = 0x14, + [C_CAN_TXRQST1_REG] = 0x88, + [C_CAN_TXRQST2_REG] = 0x8A, + [C_CAN_NEWDAT1_REG] = 0x9C, + [C_CAN_NEWDAT2_REG] = 0x9E, + [C_CAN_INTPND1_REG] = 0xB0, + [C_CAN_INTPND2_REG] = 0xB2, + [C_CAN_MSGVAL1_REG] = 0xC4, + [C_CAN_MSGVAL2_REG] = 0xC6, + [C_CAN_IF1_COMREQ_REG] = 0x100, + [C_CAN_IF1_COMMSK_REG] = 0x102, + [C_CAN_IF1_MASK1_REG] = 0x104, + [C_CAN_IF1_MASK2_REG] = 0x106, + [C_CAN_IF1_ARB1_REG] = 0x108, + [C_CAN_IF1_ARB2_REG] = 0x10A, + [C_CAN_IF1_MSGCTRL_REG] = 0x10C, + [C_CAN_IF1_DATA1_REG] = 0x110, + [C_CAN_IF1_DATA2_REG] = 0x112, + [C_CAN_IF1_DATA3_REG] = 0x114, + [C_CAN_IF1_DATA4_REG] = 0x116, + [C_CAN_IF2_COMREQ_REG] = 0x120, + [C_CAN_IF2_COMMSK_REG] = 0x122, + [C_CAN_IF2_MASK1_REG] = 0x124, + [C_CAN_IF2_MASK2_REG] = 0x126, + [C_CAN_IF2_ARB1_REG] = 0x128, + [C_CAN_IF2_ARB2_REG] = 0x12A, + [C_CAN_IF2_MSGCTRL_REG] = 0x12C, + [C_CAN_IF2_DATA1_REG] = 0x130, + [C_CAN_IF2_DATA2_REG] = 0x132, + [C_CAN_IF2_DATA3_REG] = 0x134, + [C_CAN_IF2_DATA4_REG] = 0x136, +}; + +enum c_can_dev_id { + C_CAN_DEVTYPE, + D_CAN_DEVTYPE, +}; + /* c_can private data structure */ struct c_can_priv { struct can_priv can; /* must be the first member */ diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index a6e9b937683a..f0921d16f0a9 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -71,6 +71,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) void __iomem *addr; struct net_device *dev; struct c_can_priv *priv; + const struct platform_device_id *id; struct resource *mem; int irq; #ifdef CONFIG_HAVE_CLK @@ -115,7 +116,32 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) } priv = netdev_priv(dev); - priv->regs = reg_map_c_can; + id = platform_get_device_id(pdev); + switch (id->driver_data) { + case C_CAN_DEVTYPE: + priv->regs = reg_map_c_can; + switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) { + case IORESOURCE_MEM_32BIT: + priv->read_reg = c_can_plat_read_reg_aligned_to_32bit; + priv->write_reg = c_can_plat_write_reg_aligned_to_32bit; + break; + case IORESOURCE_MEM_16BIT: + default: + priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; + priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + break; + } + break; + case D_CAN_DEVTYPE: + priv->regs = reg_map_d_can; + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; + priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + break; + default: + ret = -EINVAL; + goto exit_free_device; + } dev->irq = irq; priv->base = addr; @@ -124,18 +150,6 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) priv->priv = clk; #endif - switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) { - case IORESOURCE_MEM_32BIT: - priv->read_reg = c_can_plat_read_reg_aligned_to_32bit; - priv->write_reg = c_can_plat_write_reg_aligned_to_32bit; - break; - case IORESOURCE_MEM_16BIT: - default: - priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; - priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; - break; - } - platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -189,6 +203,20 @@ static int __devexit c_can_plat_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id c_can_id_table[] = { + { + .name = KBUILD_MODNAME, + .driver_data = C_CAN_DEVTYPE, + }, { + .name = "c_can", + .driver_data = C_CAN_DEVTYPE, + }, { + .name = "d_can", + .driver_data = D_CAN_DEVTYPE, + }, { + } +}; + static struct platform_driver c_can_plat_driver = { .driver = { .name = KBUILD_MODNAME, @@ -196,6 +224,7 @@ static struct platform_driver c_can_plat_driver = { }, .probe = c_can_plat_probe, .remove = __devexit_p(c_can_plat_remove), + .id_table = c_can_id_table, }; module_platform_driver(c_can_plat_driver); -- cgit v1.2.3 From f5c55847f7ae31275c3305c997330e5fefa9fa0e Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Wed, 6 Jun 2012 21:22:59 -0400 Subject: dma: enable mxs-dma for imx6q enable the mxs-dma for imx6q. Also remove the unused header file. Signed-off-by: Huang Shijie Acked-by: Shawn Guo Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 2 +- drivers/dma/mxs-dma.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index aadeb5be9dba..c8416d54f917 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -237,7 +237,7 @@ config IMX_DMA config MXS_DMA bool "MXS DMA support" - depends on SOC_IMX23 || SOC_IMX28 + depends on SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q select STMP_DEVICE select DMA_ENGINE help diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index c96ab15319f2..90b980d014aa 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -29,7 +29,6 @@ #include #include -#include #include "dmaengine.h" -- cgit v1.2.3 From f2f12b6fc032c7b1419fd6db84e2868b5f05a878 Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Wed, 6 Jun 2012 10:50:06 -0600 Subject: iommu/amd: Fix missing iommu_shutdown initialization in passthrough mode The iommu_shutdown callback is not initialized when the AMD IOMMU driver runs in passthrough mode. Fix that by moving the callback initialization before the check for passthrough mode. Signed-off-by: Shuah Khan Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu_init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 542024ba6dba..c04ddca7f12f 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -1641,6 +1641,8 @@ static int __init amd_iommu_init(void) amd_iommu_init_api(); + x86_platform.iommu_shutdown = disable_iommus; + if (iommu_pass_through) goto out; @@ -1649,8 +1651,6 @@ static int __init amd_iommu_init(void) else printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n"); - x86_platform.iommu_shutdown = disable_iommus; - out: return ret; -- cgit v1.2.3 From 24214449b00b94328e239d3c35cda3e6fe0f931b Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Fri, 4 May 2012 18:28:21 +0200 Subject: x86, amd_nb: Export model 0x10 and later PCI id Add the F3 PCI id of F15h, model 0x10 to pci_ids.h and to the amd_nb code which generates the list of northbridges on an AMD box. Shorten define name while at it so that it fits into pci_ids.h. Acked-by: Clemens Ladisch Cc: Bjorn Helgaas Acked-by: Andreas Herrmann Signed-off-by: Borislav Petkov --- drivers/hwmon/k10temp.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 7356b5ec8f67..f2fe8078633b 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -33,9 +33,6 @@ static bool force; module_param(force, bool, 0444); MODULE_PARM_DESC(force, "force loading on processors with erratum 319"); -/* PCI-IDs for Northbridge devices not used anywhere else */ -#define PCI_DEVICE_ID_AMD_15H_M10H_NB_F3 0x1403 - /* CPUID function 0x80000001, ebx */ #define CPUID_PKGTYPE_MASK 0xf0000000 #define CPUID_PKGTYPE_F 0x00000000 @@ -213,7 +210,7 @@ static DEFINE_PCI_DEVICE_TABLE(k10temp_id_table) = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, - { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_NB_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); -- cgit v1.2.3 From 4085f641e704b1ff1a60f62756f49281a181f707 Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Sun, 13 May 2012 15:27:17 +0300 Subject: wl18xx: fix PHY_INIT addresses mem size was hardcoded 252, now uses the parameters struct size. Signed-off-by: Ido Reis Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c8d45f011c63..e030b1297a2b 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -540,8 +540,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { .mem3 = { .start = 0x00000000, .size = 0x00000000 }, }, [PART_PHY_INIT] = { - /* TODO: use the phy_conf struct size here */ - .mem = { .start = 0x80926000, .size = 252 }, + .mem = { .start = 0x80926000, + .size = sizeof(struct wl18xx_mac_and_phy_params) }, .reg = { .start = 0x00000000, .size = 0x00000000 }, .mem2 = { .start = 0x00000000, .size = 0x00000000 }, .mem3 = { .start = 0x00000000, .size = 0x00000000 }, -- cgit v1.2.3 From 73395a79df00c3e0101bd7a0229dbbcc065b606a Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Sun, 22 Apr 2012 20:45:52 +0300 Subject: wl18xx: support PG2 version of the chip PG2 has a unique chip id. It supports similar HW quirks. Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 12 +++++++++++- drivers/net/wireless/ti/wl18xx/reg.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index e030b1297a2b..57b4a1089d0d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -588,6 +588,17 @@ static int wl18xx_identify_chip(struct wl1271 *wl) int ret = 0; switch (wl->chip.id) { + case CHIP_ID_185x_PG20: + wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG20)", + wl->chip.id); + wl->sr_fw_name = WL18XX_FW_NAME; + /* wl18xx uses the same firmware for PLT */ + wl->plt_fw_name = WL18XX_FW_NAME; + wl->quirks |= WLCORE_QUIRK_NO_ELP | + WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | + WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; + + break; case CHIP_ID_185x_PG10: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)", wl->chip.id); @@ -602,7 +613,6 @@ static int wl18xx_identify_chip(struct wl1271 *wl) /* PG 1.0 has some problems with MCS_13, so disable it */ wl->ht_cap[IEEE80211_BAND_2GHZ].mcs.rx_mask[1] &= ~BIT(5); - /* TODO: need to blocksize alignment for RX/TX separately? */ break; default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 43863d35feb2..e81f60913e88 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -140,6 +140,7 @@ #define WL18XX_FW_STATUS_ADDR 0x50F8 #define CHIP_ID_185x_PG10 (0x06030101) +#define CHIP_ID_185x_PG20 (0x06030111) /* * Host Command Interrupt. Setting this bit masks -- cgit v1.2.3 From 16ea4733210d741eeb5413acd261e675a12f980e Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Mon, 23 Apr 2012 16:49:19 +0300 Subject: wl18xx: FW/PHY arguments added for PG2 PG2 requires 4 new parameters that to be passed to the PHY. Use the actual PHY initialization struct size for the mem size of the PHY_INIT section, to account for additions in params. [Make sure PG1 still gets the original struct - Arik] Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/conf.h | 4 ++++ drivers/net/wireless/ti/wl18xx/main.c | 19 ++++++++++++++++++- drivers/net/wireless/ti/wl18xx/reg.h | 6 ++++++ 3 files changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index ffad302b6cb7..4e0f189b2539 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -44,6 +44,10 @@ struct wl18xx_conf_phy { u8 clock_valid_on_wake_up; u8 secondary_clock_setting_time; u8 pwr_limit_reference_11_abg; + u8 psat; + s8 low_power_val; + s8 med_power_val; + s8 high_power_val; }; struct wl18xx_priv_conf { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 57b4a1089d0d..bdf4ee12914d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -511,6 +511,10 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .enable_tx_low_pwr_on_siso_rdl = 0x00, .rx_profile = 0x00, .pwr_limit_reference_11_abg = 0xc8, + .psat = 0, + .low_power_val = 0x00, + .med_power_val = 0x0a, + .high_power_val = 0x1e, }, }; @@ -713,6 +717,7 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) struct wl18xx_priv *priv = wl->priv; struct wl18xx_conf_phy *phy = &priv->conf.phy; struct wl18xx_mac_and_phy_params params; + size_t len; memset(¶ms, 0, sizeof(params)); @@ -752,9 +757,21 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) params.board_type = priv->board_type; + /* for PG2 only */ + params.psat = phy->psat; + params.low_power_val = phy->low_power_val; + params.med_power_val = phy->med_power_val; + params.high_power_val = phy->high_power_val; + + /* the parameters struct is smaller for PG1 */ + if (wl->chip.id == CHIP_ID_185x_PG10) + len = offsetof(struct wl18xx_mac_and_phy_params, psat) + 1; + else + len = sizeof(params); + wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)¶ms, - sizeof(params), false); + len, false); } static void wl18xx_enable_interrupts(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index e81f60913e88..a824b26702a8 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -236,6 +236,12 @@ struct wl18xx_mac_and_phy_params { u8 clock_valid_on_wake_up; u8 secondary_clock_setting_time; u8 board_type; + /* enable point saturation */ + u8 psat; + /* low/medium/high Tx power in dBm */ + s8 low_power_val; + s8 med_power_val; + s8 high_power_val; u8 padding[1]; } __packed; -- cgit v1.2.3 From f5755fe96cb010031a50458e6d1391377d94c275 Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Mon, 23 Apr 2012 17:35:25 +0300 Subject: wl18xx: PG2.0 HW Watch dog interrupt support In PG2, the HW watchdog interrupt occupies bit0 of the event vector, and the SW watchdog is relocated to bit9. We perform the relocation globally, as there's only one watchdog bit on previous platforms (bit0). [Only mask in the new bit9 for platforms supporting it. This avoids spurious events on other platforms - Arik] Signed-off-by: Orit Brayer Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/acx.h | 14 ++++++++++++++ drivers/net/wireless/ti/wl12xx/main.c | 4 ++-- drivers/net/wireless/ti/wl18xx/acx.h | 20 ++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/main.c | 14 ++++++++++++-- drivers/net/wireless/ti/wlcore/acx.h | 27 ++++++++++++--------------- drivers/net/wireless/ti/wlcore/main.c | 13 +++++++++++-- 6 files changed, 71 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/acx.h b/drivers/net/wireless/ti/wl12xx/acx.h index 1be0f2d31b19..2a26868b837d 100644 --- a/drivers/net/wireless/ti/wl12xx/acx.h +++ b/drivers/net/wireless/ti/wl12xx/acx.h @@ -26,6 +26,20 @@ #include "../wlcore/wlcore.h" #include "../wlcore/acx.h" +#define WL12XX_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_INIT_COMPLETE | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_CMD_COMPLETE | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA) + +#define WL12XX_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA) + struct wl1271_acx_host_config_bitmap { struct acx_header header; diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 41017baaf253..f74d76c95a7f 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1034,11 +1034,11 @@ static void wl12xx_pre_upload(struct wl1271 *wl) static void wl12xx_enable_interrupts(struct wl1271 *wl) { - wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); + wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL12XX_ACX_ALL_EVENTS_VECTOR); wlcore_enable_interrupts(wl); wlcore_write_reg(wl, REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK)); wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL); } diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index cb6fd85d077f..7d74b031f114 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -28,6 +28,26 @@ /* numbers of bits the length field takes (add 1 for the actual number) */ #define WL18XX_HOST_IF_LEN_SIZE_FIELD 15 +#define WL18XX_ACX_EVENTS_VECTOR_PG1 (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_INIT_COMPLETE | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_CMD_COMPLETE | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA) + +#define WL18XX_ACX_EVENTS_VECTOR_PG2 (WL18XX_ACX_EVENTS_VECTOR_PG1 | \ + WL1271_ACX_SW_INTR_WATCHDOG) + +#define WL18XX_INTR_MASK_PG1 (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA) + +#define WL18XX_INTR_MASK_PG2 (WL18XX_INTR_MASK_PG1 | \ + WL1271_ACX_SW_INTR_WATCHDOG) + struct wl18xx_acx_host_config_bitmap { struct acx_header header; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index bdf4ee12914d..84f8e27c29ab 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -776,11 +776,21 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) static void wl18xx_enable_interrupts(struct wl1271 *wl) { - wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_ALL_EVENTS_VECTOR); + u32 event_mask, intr_mask; + + if (wl->chip.id == CHIP_ID_185x_PG10) { + event_mask = WL18XX_ACX_EVENTS_VECTOR_PG1; + intr_mask = WL18XX_INTR_MASK_PG1; + } else { + event_mask = WL18XX_ACX_EVENTS_VECTOR_PG2; + intr_mask = WL18XX_INTR_MASK_PG2; + } + + wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask); wlcore_enable_interrupts(wl); wlcore_write_reg(wl, REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + WL1271_ACX_INTR_ALL & ~intr_mask); } static int wl18xx_boot(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 46c300d4dea4..c0181258b722 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -51,21 +51,18 @@ #define WL1271_ACX_INTR_TRACE_A BIT(7) /* Trace message on MBOX #B */ #define WL1271_ACX_INTR_TRACE_B BIT(8) - -#define WL1271_ACX_INTR_ALL 0xFFFFFFFF -#define WL1271_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \ - WL1271_ACX_INTR_INIT_COMPLETE | \ - WL1271_ACX_INTR_EVENT_A | \ - WL1271_ACX_INTR_EVENT_B | \ - WL1271_ACX_INTR_CMD_COMPLETE | \ - WL1271_ACX_INTR_HW_AVAILABLE | \ - WL1271_ACX_INTR_DATA) - -#define WL1271_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ - WL1271_ACX_INTR_EVENT_A | \ - WL1271_ACX_INTR_EVENT_B | \ - WL1271_ACX_INTR_HW_AVAILABLE | \ - WL1271_ACX_INTR_DATA) +/* SW FW Initiated interrupt Watchdog timer expiration */ +#define WL1271_ACX_SW_INTR_WATCHDOG BIT(9) + +#define WL1271_ACX_INTR_ALL 0xFFFFFFFF + +/* all possible interrupts - only appropriate ones will be masked in */ +#define WLCORE_ALL_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA | \ + WL1271_ACX_SW_INTR_WATCHDOG) /* Target's information element */ struct acx_header { diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 8800a63539e3..176a3117889b 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -535,14 +535,23 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) wlcore_hw_tx_immediate_compl(wl); intr = le32_to_cpu(wl->fw_status_1->intr); - intr &= WL1271_INTR_MASK; + intr &= WLCORE_ALL_INTR_MASK; if (!intr) { done = true; continue; } if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { - wl1271_error("watchdog interrupt received! " + wl1271_error("HW watchdog interrupt received! starting recovery."); + wl->watchdog_recovery = true; + wl12xx_queue_recovery_work(wl); + + /* restarting the chip. ignore any other interrupt. */ + goto out; + } + + if (unlikely(intr & WL1271_ACX_SW_INTR_WATCHDOG)) { + wl1271_error("SW watchdog interrupt received! " "starting recovery."); wl->watchdog_recovery = true; wl12xx_queue_recovery_work(wl); -- cgit v1.2.3 From 9fccc82e19db0d63741cd6c3d2a8829fc8854406 Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Sun, 13 May 2012 14:53:40 +0300 Subject: wl18xx: pad only last frame in aggregration buffer for PG2 In PG2 only the last frame in the aggregate buffer should be aligned to the sdio block size. This frame's header msb should be set to 0, while in all the previous frames in the aggregation buffer, this bit should be set to 1. [Add a HW op for setting the frame ctrl bit only for 18xx. Other minor cleanups - Arik] [Make the pre_pkt_send operation optional -- Luca] Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/main.c | 30 ++++++++++++++++++++++++++++-- drivers/net/wireless/ti/wl18xx/tx.h | 3 +++ drivers/net/wireless/ti/wlcore/hw_ops.h | 9 +++++++++ drivers/net/wireless/ti/wlcore/tx.c | 16 +++++++++++----- drivers/net/wireless/ti/wlcore/tx.h | 4 ++-- drivers/net/wireless/ti/wlcore/wlcore.h | 4 ++++ 7 files changed, 58 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index f74d76c95a7f..364fb2feffce 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1406,6 +1406,7 @@ static struct wlcore_ops wl12xx_ops = { .debugfs_init = wl12xx_debugfs_add_files, .get_spare_blocks = wl12xx_get_spare_blocks, .set_key = wl12xx_set_key, + .pre_pkt_send = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 84f8e27c29ab..fd02795f830c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -600,7 +600,8 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl->plt_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | - WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN; + WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | + WLCORE_QUIRK_TX_PAD_LAST_FRAME; break; case CHIP_ID_185x_PG10: @@ -847,7 +848,6 @@ wl18xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, u32 blks, u32 spare_blks) { desc->wl18xx_mem.total_mem_blocks = blks; - desc->wl18xx_mem.reserved = 0; } static void @@ -856,6 +856,12 @@ wl18xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, { desc->length = cpu_to_le16(skb->len); + /* if only the last frame is to be padded, we unset this bit on Tx */ + if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) + desc->wl18xx_mem.ctrl = WL18XX_TX_CTRL_NOT_PADDED; + else + desc->wl18xx_mem.ctrl = 0; + wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d " "len: %d life: %d mem: %d", desc->hlid, le16_to_cpu(desc->length), @@ -1152,6 +1158,25 @@ out: return ret; } +static u32 wl18xx_pre_pkt_send(struct wl1271 *wl, + u32 buf_offset, u32 last_len) +{ + if (wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) { + struct wl1271_tx_hw_descr *last_desc; + + /* get the last TX HW descriptor written to the aggr buf */ + last_desc = (struct wl1271_tx_hw_descr *)(wl->aggr_buf + + buf_offset - last_len); + + /* the last frame is padded up to an SDIO block */ + last_desc->wl18xx_mem.ctrl &= ~WL18XX_TX_CTRL_NOT_PADDED; + return ALIGN(buf_offset, WL12XX_BUS_BLOCK_SIZE); + } + + /* no modifications */ + return buf_offset; +} + static struct wlcore_ops wl18xx_ops = { .identify_chip = wl18xx_identify_chip, .boot = wl18xx_boot, @@ -1176,6 +1201,7 @@ static struct wlcore_ops wl18xx_ops = { .handle_static_data = wl18xx_handle_static_data, .get_spare_blocks = wl18xx_get_spare_blocks, .set_key = wl18xx_set_key, + .pre_pkt_send = wl18xx_pre_pkt_send, }; /* HT cap appropriate for wide channels */ diff --git a/drivers/net/wireless/ti/wl18xx/tx.h b/drivers/net/wireless/ti/wl18xx/tx.h index 8aecaf09da9c..ccddc548e44a 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.h +++ b/drivers/net/wireless/ti/wl18xx/tx.h @@ -32,6 +32,9 @@ #define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F #define WL18XX_TX_STATUS_STAT_BIT_IDX 7 +/* Indicates this TX HW frame is not padded to SDIO block size */ +#define WL18XX_TX_CTRL_NOT_PADDED BIT(7) + /* * The FW uses a special bit to indicate a wide channel should be used in * the rate policy. diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 34e0498727fc..9e7787ba9610 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -188,4 +188,13 @@ wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd, return wl->ops->set_key(wl, cmd, vif, sta, key_conf); } +static inline u32 +wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) +{ + if (wl->ops->pre_pkt_send) + return wl->ops->pre_pkt_send(wl, buf_offset, last_len); + + return buf_offset; +} + #endif diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index da9a07d2cf4b..6983e7a829d0 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -178,10 +178,11 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl, unsigned int packet_length) { - if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) - return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE); - else + if ((wl->quirks & WLCORE_QUIRK_TX_PAD_LAST_FRAME) || + !(wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN)) return ALIGN(packet_length, WL1271_TX_ALIGN_TO); + else + return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE); } EXPORT_SYMBOL(wlcore_calc_packet_alignment); @@ -662,7 +663,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) struct wl12xx_vif *wlvif; struct sk_buff *skb; struct wl1271_tx_hw_descr *desc; - u32 buf_offset = 0; + u32 buf_offset = 0, last_len = 0; bool sent_packets = false; unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; int ret; @@ -686,6 +687,9 @@ void wl1271_tx_work_locked(struct wl1271 *wl) * Flush buffer and try again. */ wl1271_skb_queue_head(wl, wlvif, skb); + + buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, + last_len); wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, buf_offset, true); sent_packets = true; @@ -711,7 +715,8 @@ void wl1271_tx_work_locked(struct wl1271 *wl) ieee80211_free_txskb(wl->hw, skb); goto out_ack; } - buf_offset += ret; + last_len = ret; + buf_offset += last_len; wl->tx_packets_count++; if (has_data) { desc = (struct wl1271_tx_hw_descr *) skb->data; @@ -721,6 +726,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) out_ack: if (buf_offset) { + buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, buf_offset, true); sent_packets = true; diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 49e441f34839..fa4be1b91135 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -93,9 +93,9 @@ struct wl18xx_tx_mem { u8 total_mem_blocks; /* - * always zero + * control bits */ - u8 reserved; + u8 ctrl; } __packed; /* diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index e63450072f4d..761a72f4b8d1 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -80,6 +80,7 @@ struct wlcore_ops { struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); + u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); }; enum wlcore_partitions { @@ -420,6 +421,9 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, /* Some firmwares may not support ELP */ #define WLCORE_QUIRK_NO_ELP BIT(6) +/* pad only the last frame in the aggregate buffer */ +#define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7) + /* extra header space is required for TKIP */ #define WLCORE_QUIRK_TKIP_HEADER_SPACE BIT(8) -- cgit v1.2.3 From e16de2c4fc674f5558fd87f57c6a8e5156b22c21 Mon Sep 17 00:00:00 2001 From: Grant Erickson Date: Fri, 1 Jun 2012 09:19:01 -0700 Subject: wl12xx: Add support for an external 26 MHz crystal source Add support for an external 26 MHz crystal source. [Changed wl->ref_clock to priv->ref_clock -- Luca.] Signed-off-by: Grant Erickson Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 364fb2feffce..8338045d226f 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -875,6 +875,7 @@ static int wl127x_boot_clk(struct wl1271 *wl) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; else if (priv->ref_clock == CONF_REF_CLK_26_E || + priv->ref_clock == CONF_REF_CLK_26_M_XTAL || priv->ref_clock == CONF_REF_CLK_52_E) /* ref clk: 26/52 */ clk = 0x5; -- cgit v1.2.3 From c3001b773bb63afd403bae37febf3f09826912cb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 5 Jun 2012 20:31:29 +0000 Subject: net/ethernet: ks8851_mll unregister_netdev() before freeing We added another error condition here, but if we were to hit it then we need to unregister_netdev() before doing the free_netdev(). Otherwise we would hit the BUG_ON() in free_netdev(): BUG_ON(dev->reg_state != NETREG_UNREGISTERED); Signed-off-by: Dan Carpenter Tested-by: Raffaele Recalcati Signed-off-by: David S. Miller --- drivers/net/ethernet/micrel/ks8851_mll.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index 70bd329882c6..875dd5e264eb 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -1606,7 +1606,7 @@ static int __devinit ks8851_probe(struct platform_device *pdev) if (!pdata) { netdev_err(netdev, "No platform data\n"); err = -ENODEV; - goto err_register; + goto err_pdata; } memcpy(ks->mac_addr, pdata->mac_addr, 6); if (!is_valid_ether_addr(ks->mac_addr)) { @@ -1626,6 +1626,8 @@ static int __devinit ks8851_probe(struct platform_device *pdev) (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7); return 0; +err_pdata: + unregister_netdev(netdev); err_register: err_get_irq: iounmap(ks->hw_addr_cmd); -- cgit v1.2.3 From cae82d499979d99fc061f939d451244971024864 Mon Sep 17 00:00:00 2001 From: Rajesh Borundia Date: Wed, 6 Jun 2012 07:35:06 +0000 Subject: qlcnic: Fix estimation of recv MSS in case of LRO o Linux stack estimates MSS from skb->len or skb_shinfo(skb)->gso_size. In case of LRO skb->len is aggregate of len of number of packets hence MSS obtained using skb->len would be incorrect. Incorrect estimation of recv MSS would lead to delayed acks in some traffic patterns (which sends two or three packets and wait for ack and only then send remaining packets). This leads to drop in performance. Hence we need to set gso_size to MSS obtained from firmware. o This is fixed recently in firmware hence the MSS is obtained based on capability. If fw is capable of sending the MSS then only driver sets the gso_size. Signed-off-by: Rajesh Borundia Signed-off-by: Anirban Chakraborty Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 7 +++++++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 3 +++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h | 1 + drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 3 +++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 9 +++++++++ 5 files changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 8680a5dae4a2..520ff031cfaa 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -258,6 +258,8 @@ struct rcv_desc { (((sts_data) >> 52) & 0x1) #define qlcnic_get_lro_sts_seq_number(sts_data) \ ((sts_data) & 0x0FFFFFFFF) +#define qlcnic_get_lro_sts_mss(sts_data1) \ + ((sts_data1 >> 32) & 0x0FFFF) struct status_desc { @@ -623,6 +625,7 @@ struct qlcnic_recv_context { #define QLCNIC_CAP0_JUMBO_CONTIGUOUS (1 << 7) #define QLCNIC_CAP0_LRO_CONTIGUOUS (1 << 8) #define QLCNIC_CAP0_VALIDOFF (1 << 11) +#define QLCNIC_CAP0_LRO_MSS (1 << 21) /* * Context state @@ -829,6 +832,9 @@ struct qlcnic_mac_list_s { #define QLCNIC_FW_CAPABILITY_FVLANTX BIT_9 #define QLCNIC_FW_CAPABILITY_HW_LRO BIT_10 #define QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK BIT_27 +#define QLCNIC_FW_CAPABILITY_MORE_CAPS BIT_31 + +#define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2 /* module types */ #define LINKEVENT_MODULE_NOT_PRESENT 1 @@ -918,6 +924,7 @@ struct qlcnic_ipaddr { #define QLCNIC_NEED_FLR 0x1000 #define QLCNIC_FW_RESET_OWNER 0x2000 #define QLCNIC_FW_HANG 0x4000 +#define QLCNIC_FW_LRO_MSS_CAP 0x8000 #define QLCNIC_IS_MSI_FAMILY(adapter) \ ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED)) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 8db85244e8ad..cfa174d3a3b1 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -237,6 +237,9 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) | QLCNIC_CAP0_VALIDOFF); cap |= (QLCNIC_CAP0_JUMBO_CONTIGUOUS | QLCNIC_CAP0_LRO_CONTIGUOUS); + if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) + cap |= QLCNIC_CAP0_LRO_MSS; + prq->valid_field_offset = offsetof(struct qlcnic_hostrq_rx_ctx, msix_handler); prq->txrx_sds_binding = nsds_rings - 1; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h index 6ced3195aad3..28a6b28192e3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h @@ -588,6 +588,7 @@ enum { #define CRB_DRIVER_VERSION (QLCNIC_REG(0x2a0)) #define CRB_FW_CAPABILITIES_1 (QLCNIC_CAM_RAM(0x128)) +#define CRB_FW_CAPABILITIES_2 (QLCNIC_CAM_RAM(0x12c)) #define CRB_MAC_BLOCK_START (QLCNIC_CAM_RAM(0x1c0)) /* diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index 799fd40ed03a..8620b696aca8 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -1653,6 +1653,9 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter, length = skb->len; + if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) + skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1); + if (vid != 0xffff) __vlan_hwaccel_put_tag(skb, vid); netif_receive_skb(skb); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 46e77a2c5121..707b5ca3ddde 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -1136,6 +1136,8 @@ static int __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev) { int ring; + u32 capab2; + struct qlcnic_host_rds_ring *rds_ring; if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) @@ -1146,6 +1148,12 @@ __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev) if (qlcnic_set_eswitch_port_config(adapter)) return -EIO; + if (adapter->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) { + capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2); + if (capab2 & QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG) + adapter->flags |= QLCNIC_FW_LRO_MSS_CAP; + } + if (qlcnic_fw_create_ctx(adapter)) return -EIO; @@ -1215,6 +1223,7 @@ __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev) qlcnic_napi_disable(adapter); qlcnic_fw_destroy_ctx(adapter); + adapter->flags &= ~QLCNIC_FW_LRO_MSS_CAP; qlcnic_reset_rx_buffers_list(adapter); qlcnic_release_tx_buffers(adapter); -- cgit v1.2.3 From e42ede226c067fef541e4240f919ed2baf25d268 Mon Sep 17 00:00:00 2001 From: Jitendra Kalsaria Date: Wed, 6 Jun 2012 07:35:07 +0000 Subject: qlcnic: fix unsupported CDRP command error message. Add debug messages for FW CDRP command failure. Signed-off-by: Jitendra Kalsaria Signed-off-by: Anirban Chakraborty Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 +++ drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 34 +++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 520ff031cfaa..df4552f15693 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -612,7 +612,11 @@ struct qlcnic_recv_context { #define QLCNIC_CDRP_CMD_GET_MAC_STATS 0x00000037 #define QLCNIC_RCODE_SUCCESS 0 +#define QLCNIC_RCODE_INVALID_ARGS 6 #define QLCNIC_RCODE_NOT_SUPPORTED 9 +#define QLCNIC_RCODE_NOT_PERMITTED 10 +#define QLCNIC_RCODE_NOT_IMPL 15 +#define QLCNIC_RCODE_INVALID 16 #define QLCNIC_RCODE_TIMEOUT 17 #define QLCNIC_DESTROY_CTX_RESET 0 diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index cfa174d3a3b1..b8ead696141e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -53,12 +53,39 @@ qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd) rsp = qlcnic_poll_rsp(adapter); if (rsp == QLCNIC_CDRP_RSP_TIMEOUT) { - dev_err(&pdev->dev, "card response timeout.\n"); + dev_err(&pdev->dev, "CDRP response timeout.\n"); cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT; } else if (rsp == QLCNIC_CDRP_RSP_FAIL) { cmd->rsp.cmd = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); - dev_err(&pdev->dev, "failed card response code:0x%x\n", + switch (cmd->rsp.cmd) { + case QLCNIC_RCODE_INVALID_ARGS: + dev_err(&pdev->dev, "CDRP invalid args: 0x%x.\n", cmd->rsp.cmd); + break; + case QLCNIC_RCODE_NOT_SUPPORTED: + case QLCNIC_RCODE_NOT_IMPL: + dev_err(&pdev->dev, + "CDRP command not supported: 0x%x.\n", + cmd->rsp.cmd); + break; + case QLCNIC_RCODE_NOT_PERMITTED: + dev_err(&pdev->dev, + "CDRP requested action not permitted: 0x%x.\n", + cmd->rsp.cmd); + break; + case QLCNIC_RCODE_INVALID: + dev_err(&pdev->dev, + "CDRP invalid or unknown cmd received: 0x%x.\n", + cmd->rsp.cmd); + break; + case QLCNIC_RCODE_TIMEOUT: + dev_err(&pdev->dev, "CDRP command timeout: 0x%x.\n", + cmd->rsp.cmd); + break; + default: + dev_err(&pdev->dev, "CDRP command failed: 0x%x.\n", + cmd->rsp.cmd); + } } else if (rsp == QLCNIC_CDRP_RSP_OK) { cmd->rsp.cmd = QLCNIC_RCODE_SUCCESS; if (cmd->rsp.arg2) @@ -957,9 +984,6 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter, mac_stats->mac_rx_jabber = le64_to_cpu(stats->mac_rx_jabber); mac_stats->mac_rx_dropped = le64_to_cpu(stats->mac_rx_dropped); mac_stats->mac_rx_crc_error = le64_to_cpu(stats->mac_rx_crc_error); - } else { - dev_info(&adapter->pdev->dev, - "%s: Get mac stats failed =%d.\n", __func__, err); } dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr, -- cgit v1.2.3 From 0bb79565aaa4d917a14c87deae8b98a61833307a Mon Sep 17 00:00:00 2001 From: Rajesh Borundia Date: Wed, 6 Jun 2012 07:35:08 +0000 Subject: qlcnic: Fix protcol type in case of inband vlan. o Use correct l3 (ETH_IP or ETH_IPV6)protcol in case of inband vlan. Because of incorrect protcol type driver was setting incorrect opcode. This resulted in adapter calculating checksum incorrectly. o Updated driver version to 5.0.29 Signed-off-by: Rajesh Borundia Signed-off-by: Anirban Chakraborty Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 4 ++-- drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index df4552f15693..eaa1db9fec32 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -36,8 +36,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 0 -#define _QLCNIC_LINUX_SUBVERSION 28 -#define QLCNIC_LINUX_VERSIONID "5.0.28" +#define _QLCNIC_LINUX_SUBVERSION 29 +#define QLCNIC_LINUX_VERSIONID "5.0.29" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 707b5ca3ddde..33c3e46e59c4 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -2033,6 +2033,7 @@ qlcnic_tx_pkt(struct qlcnic_adapter *adapter, vh = (struct vlan_ethhdr *)skb->data; flags = FLAGS_VLAN_TAGGED; vlan_tci = vh->h_vlan_TCI; + protocol = ntohs(vh->h_vlan_encapsulated_proto); } else if (vlan_tx_tag_present(skb)) { flags = FLAGS_VLAN_OOB; vlan_tci = vlan_tx_tag_get(skb); -- cgit v1.2.3 From c8c60d88c59cbb48737732ba948663a3efe882aa Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 6 Jun 2012 17:13:07 +0000 Subject: bnx2x: Added EEE support This patch adds energy efficient energy support (802.3az) to bnx2x boards with 84833 phys (and sufficiently new BC and external FW). Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h | 61 +++- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 323 +++++++++++++++++++++- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h | 26 ++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 23 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h | 123 ++++++++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c | 4 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h | 2 + 7 files changed, 552 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index a440a8ba85f2..c61aa37298a3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1067,8 +1067,18 @@ struct port_feat_cfg { /* port 0: 0x454 port 1: 0x4c8 */ uses the same defines as link_config */ u32 mfw_wol_link_cfg2; /* 0x480 */ - u32 Reserved2[17]; /* 0x484 */ + /* EEE power saving mode */ + u32 eee_power_mode; /* 0x484 */ + #define PORT_FEAT_CFG_EEE_POWER_MODE_MASK 0x000000FF + #define PORT_FEAT_CFG_EEE_POWER_MODE_SHIFT 0 + #define PORT_FEAT_CFG_EEE_POWER_MODE_DISABLED 0x00000000 + #define PORT_FEAT_CFG_EEE_POWER_MODE_BALANCED 0x00000001 + #define PORT_FEAT_CFG_EEE_POWER_MODE_AGGRESSIVE 0x00000002 + #define PORT_FEAT_CFG_EEE_POWER_MODE_LOW_LATENCY 0x00000003 + + + u32 Reserved2[16]; /* 0x488 */ }; @@ -1255,6 +1265,8 @@ struct drv_func_mb { #define DRV_MSG_CODE_DRV_INFO_ACK 0xd8000000 #define DRV_MSG_CODE_DRV_INFO_NACK 0xd9000000 + #define DRV_MSG_CODE_EEE_RESULTS_ACK 0xda000000 + #define DRV_MSG_CODE_SET_MF_BW 0xe0000000 #define REQ_BC_VER_4_SET_MF_BW 0x00060202 #define DRV_MSG_CODE_SET_MF_BW_ACK 0xe1000000 @@ -1320,6 +1332,8 @@ struct drv_func_mb { #define FW_MSG_CODE_DRV_INFO_ACK 0xd8100000 #define FW_MSG_CODE_DRV_INFO_NACK 0xd9100000 + #define FW_MSG_CODE_EEE_RESULS_ACK 0xda100000 + #define FW_MSG_CODE_SET_MF_BW_SENT 0xe0000000 #define FW_MSG_CODE_SET_MF_BW_DONE 0xe1000000 @@ -1383,6 +1397,8 @@ struct drv_func_mb { #define DRV_STATUS_DRV_INFO_REQ 0x04000000 + #define DRV_STATUS_EEE_NEGOTIATION_RESULTS 0x08000000 + u32 virt_mac_upper; #define VIRT_MAC_SIGN_MASK 0xffff0000 #define VIRT_MAC_SIGNATURE 0x564d0000 @@ -1613,6 +1629,11 @@ struct fw_flr_mb { struct fw_flr_ack ack; }; +struct eee_remote_vals { + u32 tx_tw; + u32 rx_tw; +}; + /**** SUPPORT FOR SHMEM ARRRAYS *** * The SHMEM HSI is aligned on 32 bit boundaries which makes it difficult to * define arrays with storage types smaller then unsigned dwords. @@ -2053,6 +2074,41 @@ struct shmem2_region { #define DRV_INFO_CONTROL_OP_CODE_MASK 0x0000ff00 #define DRV_INFO_CONTROL_OP_CODE_SHIFT 8 u32 ibft_host_addr; /* initialized by option ROM */ + struct eee_remote_vals eee_remote_vals[PORT_MAX]; + u32 reserved[E2_FUNC_MAX]; + + + /* the status of EEE auto-negotiation + * bits 15:0 the configured tx-lpi entry timer value. Depends on bit 31. + * bits 19:16 the supported modes for EEE. + * bits 23:20 the speeds advertised for EEE. + * bits 27:24 the speeds the Link partner advertised for EEE. + * The supported/adv. modes in bits 27:19 originate from the + * SHMEM_EEE_XXX_ADV definitions (where XXX is replaced by speed). + * bit 28 when 1'b1 EEE was requested. + * bit 29 when 1'b1 tx lpi was requested. + * bit 30 when 1'b1 EEE was negotiated. Tx lpi will be asserted iff + * 30:29 are 2'b11. + * bit 31 when 1'b0 bits 15:0 contain a PORT_FEAT_CFG_EEE_ define as + * value. When 1'b1 those bits contains a value times 16 microseconds. + */ + u32 eee_status[PORT_MAX]; + #define SHMEM_EEE_TIMER_MASK 0x0000ffff + #define SHMEM_EEE_SUPPORTED_MASK 0x000f0000 + #define SHMEM_EEE_SUPPORTED_SHIFT 16 + #define SHMEM_EEE_ADV_STATUS_MASK 0x00f00000 + #define SHMEM_EEE_100M_ADV (1<<0) + #define SHMEM_EEE_1G_ADV (1<<1) + #define SHMEM_EEE_10G_ADV (1<<2) + #define SHMEM_EEE_ADV_STATUS_SHIFT 20 + #define SHMEM_EEE_LP_ADV_STATUS_MASK 0x0f000000 + #define SHMEM_EEE_LP_ADV_STATUS_SHIFT 24 + #define SHMEM_EEE_REQUESTED_BIT 0x10000000 + #define SHMEM_EEE_LPI_REQUESTED_BIT 0x20000000 + #define SHMEM_EEE_ACTIVE_BIT 0x40000000 + #define SHMEM_EEE_TIME_OUTPUT_BIT 0x80000000 + + u32 sizeof_port_stats; }; @@ -2599,6 +2655,9 @@ struct host_port_stats { u32 pfc_frames_tx_lo; u32 pfc_frames_rx_hi; u32 pfc_frames_rx_lo; + + u32 eee_lpi_count_hi; + u32 eee_lpi_count_lo; }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index a3fb7215cd89..c7c814db027d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -1305,6 +1305,94 @@ int bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos) return 0; } + +/******************************************************************/ +/* EEE section */ +/******************************************************************/ +static u8 bnx2x_eee_has_cap(struct link_params *params) +{ + struct bnx2x *bp = params->bp; + + if (REG_RD(bp, params->shmem2_base) <= + offsetof(struct shmem2_region, eee_status[params->port])) + return 0; + + return 1; +} + +static int bnx2x_eee_nvram_to_time(u32 nvram_mode, u32 *idle_timer) +{ + switch (nvram_mode) { + case PORT_FEAT_CFG_EEE_POWER_MODE_BALANCED: + *idle_timer = EEE_MODE_NVRAM_BALANCED_TIME; + break; + case PORT_FEAT_CFG_EEE_POWER_MODE_AGGRESSIVE: + *idle_timer = EEE_MODE_NVRAM_AGGRESSIVE_TIME; + break; + case PORT_FEAT_CFG_EEE_POWER_MODE_LOW_LATENCY: + *idle_timer = EEE_MODE_NVRAM_LATENCY_TIME; + break; + default: + *idle_timer = 0; + break; + } + + return 0; +} + +static int bnx2x_eee_time_to_nvram(u32 idle_timer, u32 *nvram_mode) +{ + switch (idle_timer) { + case EEE_MODE_NVRAM_BALANCED_TIME: + *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_BALANCED; + break; + case EEE_MODE_NVRAM_AGGRESSIVE_TIME: + *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_AGGRESSIVE; + break; + case EEE_MODE_NVRAM_LATENCY_TIME: + *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_LOW_LATENCY; + break; + default: + *nvram_mode = PORT_FEAT_CFG_EEE_POWER_MODE_DISABLED; + break; + } + + return 0; +} + +static u32 bnx2x_eee_calc_timer(struct link_params *params) +{ + u32 eee_mode, eee_idle; + struct bnx2x *bp = params->bp; + + if (params->eee_mode & EEE_MODE_OVERRIDE_NVRAM) { + if (params->eee_mode & EEE_MODE_OUTPUT_TIME) { + /* time value in eee_mode --> used directly*/ + eee_idle = params->eee_mode & EEE_MODE_TIMER_MASK; + } else { + /* hsi value in eee_mode --> time */ + if (bnx2x_eee_nvram_to_time(params->eee_mode & + EEE_MODE_NVRAM_MASK, + &eee_idle)) + return 0; + } + } else { + /* hsi values in nvram --> time*/ + eee_mode = ((REG_RD(bp, params->shmem_base + + offsetof(struct shmem_region, dev_info. + port_feature_config[params->port]. + eee_power_mode)) & + PORT_FEAT_CFG_EEE_POWER_MODE_MASK) >> + PORT_FEAT_CFG_EEE_POWER_MODE_SHIFT); + + if (bnx2x_eee_nvram_to_time(eee_mode, &eee_idle)) + return 0; + } + + return eee_idle; +} + + /******************************************************************/ /* PFC section */ /******************************************************************/ @@ -1729,6 +1817,14 @@ static int bnx2x_xmac_enable(struct link_params *params, /* update PFC */ bnx2x_update_pfc_xmac(params, vars, 0); + if (vars->eee_status & SHMEM_EEE_ADV_STATUS_MASK) { + DP(NETIF_MSG_LINK, "Setting XMAC for EEE\n"); + REG_WR(bp, xmac_base + XMAC_REG_EEE_TIMERS_HI, 0x1380008); + REG_WR(bp, xmac_base + XMAC_REG_EEE_CTRL, 0x1); + } else { + REG_WR(bp, xmac_base + XMAC_REG_EEE_CTRL, 0x0); + } + /* Enable TX and RX */ val = XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN; @@ -2439,6 +2535,16 @@ static void bnx2x_update_mng(struct link_params *params, u32 link_status) port_mb[params->port].link_status), link_status); } +static void bnx2x_update_mng_eee(struct link_params *params, u32 eee_status) +{ + struct bnx2x *bp = params->bp; + + if (bnx2x_eee_has_cap(params)) + REG_WR(bp, params->shmem2_base + + offsetof(struct shmem2_region, + eee_status[params->port]), eee_status); +} + static void bnx2x_update_pfc_nig(struct link_params *params, struct link_vars *vars, struct bnx2x_nig_brb_pfc_port_params *nig_params) @@ -3950,6 +4056,20 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL4_MISC3, val | 0x8080); + /* Enable LPI pass through */ + if ((params->eee_mode & EEE_MODE_ADV_LPI) && + (phy->flags & FLAGS_EEE_10GBT) && + (!(params->eee_mode & EEE_MODE_ENABLE_LPI) || + bnx2x_eee_calc_timer(params)) && + (params->req_duplex[bnx2x_phy_selection(params)] == DUPLEX_FULL)) { + DP(NETIF_MSG_LINK, "Configure WC for LPI pass through\n"); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_EEE_COMBO_CONTROL0, + 0x7c); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL4_MISC5, 0xc000); + } + /* 10G XFI Full Duplex */ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x100); @@ -6462,6 +6582,15 @@ static int bnx2x_update_link_down(struct link_params *params, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); } if (CHIP_IS_E3(bp)) { + REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 + (params->port << 2), + 0); + REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 0); + REG_WR(bp, MISC_REG_CPMU_LP_MASK_ENT_P0 + (params->port << 2), + 0); + vars->eee_status &= ~(SHMEM_EEE_LP_ADV_STATUS_MASK | + SHMEM_EEE_ACTIVE_BIT); + + bnx2x_update_mng_eee(params, vars->eee_status); bnx2x_xmac_disable(params); bnx2x_umac_disable(params); } @@ -6501,6 +6630,16 @@ static int bnx2x_update_link_up(struct link_params *params, bnx2x_umac_enable(params, vars, 0); bnx2x_set_led(params, vars, LED_MODE_OPER, vars->line_speed); + + if ((vars->eee_status & SHMEM_EEE_ACTIVE_BIT) && + (vars->eee_status & SHMEM_EEE_LPI_REQUESTED_BIT)) { + DP(NETIF_MSG_LINK, "Enabling LPI assertion\n"); + REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 + + (params->port << 2), 1); + REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 1); + REG_WR(bp, MISC_REG_CPMU_LP_MASK_ENT_P0 + + (params->port << 2), 0xfc20); + } } if ((CHIP_IS_E1x(bp) || CHIP_IS_E2(bp))) { @@ -6538,7 +6677,7 @@ static int bnx2x_update_link_up(struct link_params *params, /* update shared memory */ bnx2x_update_mng(params, vars->link_status); - + bnx2x_update_mng_eee(params, vars->eee_status); /* Check remote fault */ for (phy_idx = INT_PHY; phy_idx < MAX_PHYS; phy_idx++) { if (params->phy[phy_idx].flags & FLAGS_TX_ERROR_CHECK) { @@ -6582,6 +6721,8 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) phy_vars[phy_index].phy_link_up = 0; phy_vars[phy_index].link_up = 0; phy_vars[phy_index].fault_detected = 0; + /* different consideration, since vars holds inner state */ + phy_vars[phy_index].eee_status = vars->eee_status; } if (USES_WARPCORE(bp)) @@ -6711,6 +6852,9 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) vars->link_status |= LINK_STATUS_SERDES_LINK; else vars->link_status &= ~LINK_STATUS_SERDES_LINK; + + vars->eee_status = phy_vars[active_external_phy].eee_status; + DP(NETIF_MSG_LINK, "Active external phy selected: %x\n", active_external_phy); } @@ -9579,9 +9723,9 @@ static int bnx2x_8481_config_init(struct bnx2x_phy *phy, static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, struct link_params *params, u16 fw_cmd, - u16 cmd_args[]) + u16 cmd_args[], int argc) { - u32 idx; + int idx; u16 val; struct bnx2x *bp = params->bp; /* Write CMD_OPEN_OVERRIDE to STATUS reg */ @@ -9601,7 +9745,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, } /* Prepare argument(s) and issue command */ - for (idx = 0; idx < PHY84833_CMDHDLR_MAX_ARGS; idx++) { + for (idx = 0; idx < argc; idx++) { bnx2x_cl45_write(bp, phy, MDIO_CTL_DEVAD, MDIO_84833_CMD_HDLR_DATA1 + idx, cmd_args[idx]); @@ -9622,7 +9766,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, return -EINVAL; } /* Gather returning data */ - for (idx = 0; idx < PHY84833_CMDHDLR_MAX_ARGS; idx++) { + for (idx = 0; idx < argc; idx++) { bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD, MDIO_84833_CMD_HDLR_DATA1 + idx, &cmd_args[idx]); @@ -9656,7 +9800,7 @@ static int bnx2x_84833_pair_swap_cfg(struct bnx2x_phy *phy, data[1] = (u16)pair_swap; status = bnx2x_84833_cmd_hdlr(phy, params, - PHY84833_CMD_SET_PAIR_SWAP, data); + PHY84833_CMD_SET_PAIR_SWAP, data, PHY84833_CMDHDLR_MAX_ARGS); if (status == 0) DP(NETIF_MSG_LINK, "Pairswap OK, val=0x%x\n", data[1]); @@ -9734,6 +9878,95 @@ static int bnx2x_84833_hw_reset_phy(struct bnx2x_phy *phy, return 0; } +static int bnx2x_8483x_eee_timers(struct link_params *params, + struct link_vars *vars) +{ + u32 eee_idle = 0, eee_mode; + struct bnx2x *bp = params->bp; + + eee_idle = bnx2x_eee_calc_timer(params); + + if (eee_idle) { + REG_WR(bp, MISC_REG_CPMU_LP_IDLE_THR_P0 + (params->port << 2), + eee_idle); + } else if ((params->eee_mode & EEE_MODE_ENABLE_LPI) && + (params->eee_mode & EEE_MODE_OVERRIDE_NVRAM) && + (params->eee_mode & EEE_MODE_OUTPUT_TIME)) { + DP(NETIF_MSG_LINK, "Error: Tx LPI is enabled with timer 0\n"); + return -EINVAL; + } + + vars->eee_status &= ~(SHMEM_EEE_TIMER_MASK | SHMEM_EEE_TIME_OUTPUT_BIT); + if (params->eee_mode & EEE_MODE_OUTPUT_TIME) { + /* eee_idle in 1u --> eee_status in 16u */ + eee_idle >>= 4; + vars->eee_status |= (eee_idle & SHMEM_EEE_TIMER_MASK) | + SHMEM_EEE_TIME_OUTPUT_BIT; + } else { + if (bnx2x_eee_time_to_nvram(eee_idle, &eee_mode)) + return -EINVAL; + vars->eee_status |= eee_mode; + } + + return 0; +} + +static int bnx2x_8483x_disable_eee(struct bnx2x_phy *phy, + struct link_params *params, + struct link_vars *vars) +{ + int rc; + struct bnx2x *bp = params->bp; + u16 cmd_args = 0; + + DP(NETIF_MSG_LINK, "Don't Advertise 10GBase-T EEE\n"); + + /* Make Certain LPI is disabled */ + REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 + (params->port << 2), 0); + REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 0); + + /* Prevent Phy from working in EEE and advertising it */ + rc = bnx2x_84833_cmd_hdlr(phy, params, + PHY84833_CMD_SET_EEE_MODE, &cmd_args, 1); + if (rc != 0) { + DP(NETIF_MSG_LINK, "EEE disable failed.\n"); + return rc; + } + + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_EEE_ADV, 0); + vars->eee_status &= ~SHMEM_EEE_ADV_STATUS_MASK; + + return 0; +} + +static int bnx2x_8483x_enable_eee(struct bnx2x_phy *phy, + struct link_params *params, + struct link_vars *vars) +{ + int rc; + struct bnx2x *bp = params->bp; + u16 cmd_args = 1; + + DP(NETIF_MSG_LINK, "Advertise 10GBase-T EEE\n"); + + rc = bnx2x_84833_cmd_hdlr(phy, params, + PHY84833_CMD_SET_EEE_MODE, &cmd_args, 1); + if (rc != 0) { + DP(NETIF_MSG_LINK, "EEE enable failed.\n"); + return rc; + } + + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_EEE_ADV, 0x8); + + /* Mask events preventing LPI generation */ + REG_WR(bp, MISC_REG_CPMU_LP_MASK_EXT_P0 + (params->port << 2), 0xfc20); + + vars->eee_status &= ~SHMEM_EEE_ADV_STATUS_MASK; + vars->eee_status |= (SHMEM_EEE_10G_ADV << SHMEM_EEE_ADV_STATUS_SHIFT); + + return 0; +} + #define PHY84833_CONSTANT_LATENCY 1193 static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, struct link_params *params, @@ -9833,7 +10066,8 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, cmd_args[2] = PHY84833_CONSTANT_LATENCY + 1; cmd_args[3] = PHY84833_CONSTANT_LATENCY; rc = bnx2x_84833_cmd_hdlr(phy, params, - PHY84833_CMD_SET_EEE_MODE, cmd_args); + PHY84833_CMD_SET_EEE_MODE, cmd_args, + PHY84833_CMDHDLR_MAX_ARGS); if (rc != 0) DP(NETIF_MSG_LINK, "Cfg AutogrEEEn failed.\n"); } @@ -9858,6 +10092,48 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, MDIO_CTL_REG_84823_USER_CTRL_REG, val); } + bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_FW_REV, &val); + + /* Configure EEE support */ + if ((val >= MDIO_84833_TOP_CFG_FW_EEE) && bnx2x_eee_has_cap(params)) { + phy->flags |= FLAGS_EEE_10GBT; + vars->eee_status |= SHMEM_EEE_10G_ADV << + SHMEM_EEE_SUPPORTED_SHIFT; + /* Propogate params' bits --> vars (for migration exposure) */ + if (params->eee_mode & EEE_MODE_ENABLE_LPI) + vars->eee_status |= SHMEM_EEE_LPI_REQUESTED_BIT; + else + vars->eee_status &= ~SHMEM_EEE_LPI_REQUESTED_BIT; + + if (params->eee_mode & EEE_MODE_ADV_LPI) + vars->eee_status |= SHMEM_EEE_REQUESTED_BIT; + else + vars->eee_status &= ~SHMEM_EEE_REQUESTED_BIT; + + rc = bnx2x_8483x_eee_timers(params, vars); + if (rc != 0) { + DP(NETIF_MSG_LINK, "Failed to configure EEE timers\n"); + bnx2x_8483x_disable_eee(phy, params, vars); + return rc; + } + + if ((params->req_duplex[actual_phy_selection] == DUPLEX_FULL) && + (params->eee_mode & EEE_MODE_ADV_LPI) && + (bnx2x_eee_calc_timer(params) || + !(params->eee_mode & EEE_MODE_ENABLE_LPI))) + rc = bnx2x_8483x_enable_eee(phy, params, vars); + else + rc = bnx2x_8483x_disable_eee(phy, params, vars); + if (rc != 0) { + DP(NETIF_MSG_LINK, "Failed to set EEE advertisment\n"); + return rc; + } + } else { + phy->flags &= ~FLAGS_EEE_10GBT; + vars->eee_status &= ~SHMEM_EEE_SUPPORTED_MASK; + } + if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { /* Bring PHY out of super isolate mode as the final step. */ bnx2x_cl45_read(bp, phy, @@ -9989,6 +10265,31 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy, if (val & (1<<11)) vars->link_status |= LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE; + + /* Determine if EEE was negotiated */ + if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + u32 eee_shmem = 0; + + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_EEE_ADV, &val1); + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_LP_EEE_ADV, &val2); + if ((val1 & val2) & 0x8) { + DP(NETIF_MSG_LINK, "EEE negotiated\n"); + vars->eee_status |= SHMEM_EEE_ACTIVE_BIT; + } + + if (val2 & 0x12) + eee_shmem |= SHMEM_EEE_100M_ADV; + if (val2 & 0x4) + eee_shmem |= SHMEM_EEE_1G_ADV; + if (val2 & 0x68) + eee_shmem |= SHMEM_EEE_10G_ADV; + + vars->eee_status &= ~SHMEM_EEE_LP_ADV_STATUS_MASK; + vars->eee_status |= (eee_shmem << + SHMEM_EEE_LP_ADV_STATUS_SHIFT); + } } return link_up; @@ -11243,7 +11544,8 @@ static struct bnx2x_phy phy_84833 = { .def_md_devad = 0, .flags = (FLAGS_FAN_FAILURE_DET_REQ | FLAGS_REARM_LATCH_SIGNAL | - FLAGS_TX_ERROR_CHECK), + FLAGS_TX_ERROR_CHECK | + FLAGS_EEE_10GBT), .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .mdio_ctrl = 0, @@ -12011,6 +12313,8 @@ int bnx2x_phy_init(struct link_params *params, struct link_vars *vars) break; } bnx2x_update_mng(params, vars->link_status); + + bnx2x_update_mng_eee(params, vars->eee_status); return 0; } @@ -12023,6 +12327,9 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, /* disable attentions */ vars->link_status = 0; bnx2x_update_mng(params, vars->link_status); + vars->eee_status &= ~(SHMEM_EEE_LP_ADV_STATUS_MASK | + SHMEM_EEE_ACTIVE_BIT); + bnx2x_update_mng_eee(params, vars->eee_status); bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index ea4371f4335f..e920800a7bc5 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -149,6 +149,7 @@ struct bnx2x_phy { #define FLAGS_DUMMY_READ (1<<9) #define FLAGS_MDC_MDIO_WA_B0 (1<<10) #define FLAGS_TX_ERROR_CHECK (1<<12) +#define FLAGS_EEE_10GBT (1<<13) /* preemphasis values for the rx side */ u16 rx_preemphasis[4]; @@ -265,6 +266,30 @@ struct link_params { u8 num_phys; u8 rsrv; + + /* Used to configure the EEE Tx LPI timer, has several modes of + * operation, according to bits 29:28 - + * 2'b00: Timer will be configured by nvram, output will be the value + * from nvram. + * 2'b01: Timer will be configured by nvram, output will be in + * microseconds. + * 2'b10: bits 1:0 contain an nvram value which will be used instead + * of the one located in the nvram. Output will be that value. + * 2'b11: bits 19:0 contain the idle timer in microseconds; output + * will be in microseconds. + * Bits 31:30 should be 2'b11 in order for EEE to be enabled. + */ + u32 eee_mode; +#define EEE_MODE_NVRAM_BALANCED_TIME (0xa00) +#define EEE_MODE_NVRAM_AGGRESSIVE_TIME (0x100) +#define EEE_MODE_NVRAM_LATENCY_TIME (0x6000) +#define EEE_MODE_NVRAM_MASK (0x3) +#define EEE_MODE_TIMER_MASK (0xfffff) +#define EEE_MODE_OUTPUT_TIME (1<<28) +#define EEE_MODE_OVERRIDE_NVRAM (1<<29) +#define EEE_MODE_ENABLE_LPI (1<<30) +#define EEE_MODE_ADV_LPI (1<<31) + u16 hw_led_mode; /* part of the hw_config read from the shmem */ u32 multi_phy_config; @@ -301,6 +326,7 @@ struct link_vars { /* The same definitions as the shmem parameter */ u32 link_status; + u32 eee_status; u8 fault_detected; u8 rsrv1; u16 periodic_flags; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index f755a665dab3..a622bb7bf21d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -3176,6 +3176,12 @@ static void bnx2x_set_mf_bw(struct bnx2x *bp) bnx2x_fw_command(bp, DRV_MSG_CODE_SET_MF_BW_ACK, 0); } +static void bnx2x_handle_eee_event(struct bnx2x *bp) +{ + DP(BNX2X_MSG_MCP, "EEE - LLDP event\n"); + bnx2x_fw_command(bp, DRV_MSG_CODE_EEE_RESULTS_ACK, 0); +} + static void bnx2x_handle_drv_info_req(struct bnx2x *bp) { enum drv_info_opcode op_code; @@ -3742,6 +3748,8 @@ static void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn) if (val & DRV_STATUS_AFEX_EVENT_MASK) bnx2x_handle_afex_cmd(bp, val & DRV_STATUS_AFEX_EVENT_MASK); + if (val & DRV_STATUS_EEE_NEGOTIATION_RESULTS) + bnx2x_handle_eee_event(bp); if (bp->link_vars.periodic_flags & PERIODIC_FLAGS_LINK_EVENT) { /* sync with link */ @@ -10082,7 +10090,7 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) { int port = BP_PORT(bp); u32 config; - u32 ext_phy_type, ext_phy_config; + u32 ext_phy_type, ext_phy_config, eee_mode; bp->link_params.bp = bp; bp->link_params.port = port; @@ -10149,6 +10157,19 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) bp->port.need_hw_lock = bnx2x_hw_lock_required(bp, bp->common.shmem_base, bp->common.shmem2_base); + + /* Configure link feature according to nvram value */ + eee_mode = (((SHMEM_RD(bp, dev_info. + port_feature_config[port].eee_power_mode)) & + PORT_FEAT_CFG_EEE_POWER_MODE_MASK) >> + PORT_FEAT_CFG_EEE_POWER_MODE_SHIFT); + if (eee_mode != PORT_FEAT_CFG_EEE_POWER_MODE_DISABLED) { + bp->link_params.eee_mode = EEE_MODE_ADV_LPI | + EEE_MODE_ENABLE_LPI | + EEE_MODE_OUTPUT_TIME; + } else { + bp->link_params.eee_mode = 0; + } } void bnx2x_get_iscsi_info(struct bnx2x *bp) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index bbd387492a80..bfef98f666c9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -1488,6 +1488,121 @@ * 2:1 - otp_misc_do[51:50]; 0 - otp_misc_do[1]. */ #define MISC_REG_CHIP_TYPE 0xac60 #define MISC_REG_CHIP_TYPE_57811_MASK (1<<1) +#define MISC_REG_CPMU_LP_DR_ENABLE 0xa858 +/* [RW 1] FW EEE LPI Enable. When 1 indicates that EEE LPI mode is enabled + * by FW. When 0 indicates that the EEE LPI mode is disabled by FW. Clk + * 25MHz. Reset on hard reset. */ +#define MISC_REG_CPMU_LP_FW_ENABLE_P0 0xa84c +/* [RW 32] EEE LPI Idle Threshold. The threshold value for the idle EEE LPI + * counter. Timer tick is 1 us. Clock 25MHz. Reset on hard reset. */ +#define MISC_REG_CPMU_LP_IDLE_THR_P0 0xa8a0 +/* [RW 18] LPI entry events mask. [0] - Vmain SM Mask. When 1 indicates that + * the Vmain SM end state is disabled. When 0 indicates that the Vmain SM + * end state is enabled. [1] - FW Queues Empty Mask. When 1 indicates that + * the FW command that all Queues are empty is disabled. When 0 indicates + * that the FW command that all Queues are empty is enabled. [2] - FW Early + * Exit Mask / Reserved (Entry mask). When 1 indicates that the FW Early + * Exit command is disabled. When 0 indicates that the FW Early Exit command + * is enabled. This bit applicable only in the EXIT Events Mask registers. + * [3] - PBF Request Mask. When 1 indicates that the PBF Request indication + * is disabled. When 0 indicates that the PBF Request indication is enabled. + * [4] - Tx Request Mask. When =1 indicates that the Tx other Than PBF + * Request indication is disabled. When 0 indicates that the Tx Other Than + * PBF Request indication is enabled. [5] - Rx EEE LPI Status Mask. When 1 + * indicates that the RX EEE LPI Status indication is disabled. When 0 + * indicates that the RX EEE LPI Status indication is enabled. In the EXIT + * Events Masks registers; this bit masks the falling edge detect of the LPI + * Status (Rx LPI is on - off). [6] - Tx Pause Mask. When 1 indicates that + * the Tx Pause indication is disabled. When 0 indicates that the Tx Pause + * indication is enabled. [7] - BRB1 Empty Mask. When 1 indicates that the + * BRB1 EMPTY indication is disabled. When 0 indicates that the BRB1 EMPTY + * indication is enabled. [8] - QM Idle Mask. When 1 indicates that the QM + * IDLE indication is disabled. When 0 indicates that the QM IDLE indication + * is enabled. (One bit for both VOQ0 and VOQ1). [9] - QM LB Idle Mask. When + * 1 indicates that the QM IDLE indication for LOOPBACK is disabled. When 0 + * indicates that the QM IDLE indication for LOOPBACK is enabled. [10] - L1 + * Status Mask. When 1 indicates that the L1 Status indication from the PCIE + * CORE is disabled. When 0 indicates that the RX EEE LPI Status indication + * from the PCIE CORE is enabled. In the EXIT Events Masks registers; this + * bit masks the falling edge detect of the L1 status (L1 is on - off). [11] + * - P0 E0 EEE EEE LPI REQ Mask. When =1 indicates that the P0 E0 EEE EEE + * LPI REQ indication is disabled. When =0 indicates that the P0 E0 EEE LPI + * REQ indication is enabled. [12] - P1 E0 EEE LPI REQ Mask. When =1 + * indicates that the P0 EEE LPI REQ indication is disabled. When =0 + * indicates that the P0 EEE LPI REQ indication is enabled. [13] - P0 E1 EEE + * LPI REQ Mask. When =1 indicates that the P0 EEE LPI REQ indication is + * disabled. When =0 indicates that the P0 EEE LPI REQ indication is + * enabled. [14] - P1 E1 EEE LPI REQ Mask. When =1 indicates that the P0 EEE + * LPI REQ indication is disabled. When =0 indicates that the P0 EEE LPI REQ + * indication is enabled. [15] - L1 REQ Mask. When =1 indicates that the L1 + * REQ indication is disabled. When =0 indicates that the L1 indication is + * enabled. [16] - Rx EEE LPI Status Edge Detect Mask. When =1 indicates + * that the RX EEE LPI Status Falling Edge Detect indication is disabled (Rx + * EEE LPI is on - off). When =0 indicates that the RX EEE LPI Status + * Falling Edge Detec indication is enabled (Rx EEE LPI is on - off). This + * bit is applicable only in the EXIT Events Masks registers. [17] - L1 + * Status Edge Detect Mask. When =1 indicates that the L1 Status Falling + * Edge Detect indication from the PCIE CORE is disabled (L1 is on - off). + * When =0 indicates that the L1 Status Falling Edge Detect indication from + * the PCIE CORE is enabled (L1 is on - off). This bit is applicable only in + * the EXIT Events Masks registers. Clock 25MHz. Reset on hard reset. */ +#define MISC_REG_CPMU_LP_MASK_ENT_P0 0xa880 +/* [RW 18] EEE LPI exit events mask. [0] - Vmain SM Mask. When 1 indicates + * that the Vmain SM end state is disabled. When 0 indicates that the Vmain + * SM end state is enabled. [1] - FW Queues Empty Mask. When 1 indicates + * that the FW command that all Queues are empty is disabled. When 0 + * indicates that the FW command that all Queues are empty is enabled. [2] - + * FW Early Exit Mask / Reserved (Entry mask). When 1 indicates that the FW + * Early Exit command is disabled. When 0 indicates that the FW Early Exit + * command is enabled. This bit applicable only in the EXIT Events Mask + * registers. [3] - PBF Request Mask. When 1 indicates that the PBF Request + * indication is disabled. When 0 indicates that the PBF Request indication + * is enabled. [4] - Tx Request Mask. When =1 indicates that the Tx other + * Than PBF Request indication is disabled. When 0 indicates that the Tx + * Other Than PBF Request indication is enabled. [5] - Rx EEE LPI Status + * Mask. When 1 indicates that the RX EEE LPI Status indication is disabled. + * When 0 indicates that the RX LPI Status indication is enabled. In the + * EXIT Events Masks registers; this bit masks the falling edge detect of + * the EEE LPI Status (Rx EEE LPI is on - off). [6] - Tx Pause Mask. When 1 + * indicates that the Tx Pause indication is disabled. When 0 indicates that + * the Tx Pause indication is enabled. [7] - BRB1 Empty Mask. When 1 + * indicates that the BRB1 EMPTY indication is disabled. When 0 indicates + * that the BRB1 EMPTY indication is enabled. [8] - QM Idle Mask. When 1 + * indicates that the QM IDLE indication is disabled. When 0 indicates that + * the QM IDLE indication is enabled. (One bit for both VOQ0 and VOQ1). [9] + * - QM LB Idle Mask. When 1 indicates that the QM IDLE indication for + * LOOPBACK is disabled. When 0 indicates that the QM IDLE indication for + * LOOPBACK is enabled. [10] - L1 Status Mask. When 1 indicates that the L1 + * Status indication from the PCIE CORE is disabled. When 0 indicates that + * the RX EEE LPI Status indication from the PCIE CORE is enabled. In the + * EXIT Events Masks registers; this bit masks the falling edge detect of + * the L1 status (L1 is on - off). [11] - P0 E0 EEE EEE LPI REQ Mask. When + * =1 indicates that the P0 E0 EEE EEE LPI REQ indication is disabled. When + * =0 indicates that the P0 E0 EEE LPI REQ indication is enabled. [12] - P1 + * E0 EEE LPI REQ Mask. When =1 indicates that the P0 EEE LPI REQ indication + * is disabled. When =0 indicates that the P0 EEE LPI REQ indication is + * enabled. [13] - P0 E1 EEE LPI REQ Mask. When =1 indicates that the P0 EEE + * LPI REQ indication is disabled. When =0 indicates that the P0 EEE LPI REQ + * indication is enabled. [14] - P1 E1 EEE LPI REQ Mask. When =1 indicates + * that the P0 EEE LPI REQ indication is disabled. When =0 indicates that + * the P0 EEE LPI REQ indication is enabled. [15] - L1 REQ Mask. When =1 + * indicates that the L1 REQ indication is disabled. When =0 indicates that + * the L1 indication is enabled. [16] - Rx EEE LPI Status Edge Detect Mask. + * When =1 indicates that the RX EEE LPI Status Falling Edge Detect + * indication is disabled (Rx EEE LPI is on - off). When =0 indicates that + * the RX EEE LPI Status Falling Edge Detec indication is enabled (Rx EEE + * LPI is on - off). This bit is applicable only in the EXIT Events Masks + * registers. [17] - L1 Status Edge Detect Mask. When =1 indicates that the + * L1 Status Falling Edge Detect indication from the PCIE CORE is disabled + * (L1 is on - off). When =0 indicates that the L1 Status Falling Edge + * Detect indication from the PCIE CORE is enabled (L1 is on - off). This + * bit is applicable only in the EXIT Events Masks registers.Clock 25MHz. + * Reset on hard reset. */ +#define MISC_REG_CPMU_LP_MASK_EXT_P0 0xa888 +/* [RW 16] EEE LPI Entry Events Counter. A statistic counter with the number + * of counts that the SM entered the EEE LPI state. Clock 25MHz. Read only + * register. Reset on hard reset. */ +#define MISC_REG_CPMU_LP_SM_ENT_CNT_P0 0xa8b8 /* [RW 32] The following driver registers(1...16) represent 16 drivers and 32 clients. Each client can be controlled by one driver only. One in each bit represent that this driver control the appropriate client (Ex: bit 5 @@ -5372,6 +5487,8 @@ /* [RW 32] Lower 48 bits of ctrl_sa register. Used as the SA in PAUSE/PFC * packets transmitted by the MAC */ #define XMAC_REG_CTRL_SA_LO 0x28 +#define XMAC_REG_EEE_CTRL 0xd8 +#define XMAC_REG_EEE_TIMERS_HI 0xe4 #define XMAC_REG_PAUSE_CTRL 0x68 #define XMAC_REG_PFC_CTRL 0x70 #define XMAC_REG_PFC_CTRL_HI 0x74 @@ -6813,6 +6930,8 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_AN_REG_LP_AUTO_NEG 0x0013 #define MDIO_AN_REG_LP_AUTO_NEG2 0x0014 #define MDIO_AN_REG_MASTER_STATUS 0x0021 +#define MDIO_AN_REG_EEE_ADV 0x003c +#define MDIO_AN_REG_LP_EEE_ADV 0x003d /*bcm*/ #define MDIO_AN_REG_LINK_STATUS 0x8304 #define MDIO_AN_REG_CL37_CL73 0x8370 @@ -6866,6 +6985,8 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_PMA_REG_84823_LED3_STRETCH_EN 0x0080 /* BCM84833 only */ +#define MDIO_84833_TOP_CFG_FW_REV 0x400f +#define MDIO_84833_TOP_CFG_FW_EEE 0x10b1 #define MDIO_84833_TOP_CFG_XGPHY_STRAP1 0x401a #define MDIO_84833_SUPER_ISOLATE 0x8000 /* These are mailbox register set used by 84833. */ @@ -6993,11 +7114,13 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_DIGITAL3_UP1 0x8329 #define MDIO_WC_REG_DIGITAL3_LP_UP1 0x832c #define MDIO_WC_REG_DIGITAL4_MISC3 0x833c +#define MDIO_WC_REG_DIGITAL4_MISC5 0x833e #define MDIO_WC_REG_DIGITAL5_MISC6 0x8345 #define MDIO_WC_REG_DIGITAL5_MISC7 0x8349 #define MDIO_WC_REG_DIGITAL5_ACTUAL_SPEED 0x834e #define MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL 0x8350 #define MDIO_WC_REG_CL49_USERB0_CTRL 0x8368 +#define MDIO_WC_REG_EEE_COMBO_CONTROL0 0x8390 #define MDIO_WC_REG_TX66_CONTROL 0x83b0 #define MDIO_WC_REG_RX66_CONTROL 0x83c0 #define MDIO_WC_REG_RX66_SCW0 0x83c2 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 1e2785cd11d0..0e8bdcb9c748 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -785,6 +785,10 @@ static int bnx2x_hw_stats_update(struct bnx2x *bp) pstats->host_port_stats_counter++; + if (CHIP_IS_E3(bp)) + estats->eee_tx_lpi += REG_RD(bp, + MISC_REG_CPMU_LP_SM_ENT_CNT_P0); + if (!BP_NOMCP(bp)) { u32 nig_timer_max = SHMEM_RD(bp, port_mb[BP_PORT(bp)].stat_nig_timer); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h index 93e689fdfeda..24b8e505b60c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.h @@ -203,6 +203,8 @@ struct bnx2x_eth_stats { /* Recovery */ u32 recoverable_error; u32 unrecoverable_error; + /* src: Clear-on-Read register; Will not survive PMF Migration */ + u32 eee_tx_lpi; }; -- cgit v1.2.3 From e9939c80e9cb51f0da7d6e70838fd6c90248439f Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 6 Jun 2012 17:13:08 +0000 Subject: bnx2x: Added EEE Ethtool support. This patch extends the bnx2x's ethtool interface to enable control in the eee feature, as well as report statistic information about it. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 134 +++++++++++++++++++++ 1 file changed, 134 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index ddc18ee5c5ae..bf30e2829285 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -177,6 +177,8 @@ static const struct { 4, STATS_FLAGS_FUNC, "recoverable_errors" }, { STATS_OFFSET32(unrecoverable_error), 4, STATS_FLAGS_FUNC, "unrecoverable_errors" }, + { STATS_OFFSET32(eee_tx_lpi), + 4, STATS_FLAGS_PORT, "Tx LPI entry count"} }; #define BNX2X_NUM_STATS ARRAY_SIZE(bnx2x_stats_arr) @@ -1543,6 +1545,136 @@ static const struct { { "idle check (online)" } }; +static u32 bnx2x_eee_to_adv(u32 eee_adv) +{ + u32 modes = 0; + + if (eee_adv & SHMEM_EEE_100M_ADV) + modes |= ADVERTISED_100baseT_Full; + if (eee_adv & SHMEM_EEE_1G_ADV) + modes |= ADVERTISED_1000baseT_Full; + if (eee_adv & SHMEM_EEE_10G_ADV) + modes |= ADVERTISED_10000baseT_Full; + + return modes; +} + +static u32 bnx2x_adv_to_eee(u32 modes, u32 shift) +{ + u32 eee_adv = 0; + if (modes & ADVERTISED_100baseT_Full) + eee_adv |= SHMEM_EEE_100M_ADV; + if (modes & ADVERTISED_1000baseT_Full) + eee_adv |= SHMEM_EEE_1G_ADV; + if (modes & ADVERTISED_10000baseT_Full) + eee_adv |= SHMEM_EEE_10G_ADV; + + return eee_adv << shift; +} + +static int bnx2x_get_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct bnx2x *bp = netdev_priv(dev); + u32 eee_cfg; + + if (!SHMEM2_HAS(bp, eee_status[BP_PORT(bp)])) { + DP(BNX2X_MSG_ETHTOOL, "BC Version does not support EEE\n"); + return -EOPNOTSUPP; + } + + eee_cfg = SHMEM2_RD(bp, eee_status[BP_PORT(bp)]); + + edata->supported = + bnx2x_eee_to_adv((eee_cfg & SHMEM_EEE_SUPPORTED_MASK) >> + SHMEM_EEE_SUPPORTED_SHIFT); + + edata->advertised = + bnx2x_eee_to_adv((eee_cfg & SHMEM_EEE_ADV_STATUS_MASK) >> + SHMEM_EEE_ADV_STATUS_SHIFT); + edata->lp_advertised = + bnx2x_eee_to_adv((eee_cfg & SHMEM_EEE_LP_ADV_STATUS_MASK) >> + SHMEM_EEE_LP_ADV_STATUS_SHIFT); + + /* SHMEM value is in 16u units --> Convert to 1u units. */ + edata->tx_lpi_timer = (eee_cfg & SHMEM_EEE_TIMER_MASK) << 4; + + edata->eee_enabled = (eee_cfg & SHMEM_EEE_REQUESTED_BIT) ? 1 : 0; + edata->eee_active = (eee_cfg & SHMEM_EEE_ACTIVE_BIT) ? 1 : 0; + edata->tx_lpi_enabled = (eee_cfg & SHMEM_EEE_LPI_REQUESTED_BIT) ? 1 : 0; + + return 0; +} + +static int bnx2x_set_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct bnx2x *bp = netdev_priv(dev); + u32 eee_cfg; + u32 advertised; + + if (IS_MF(bp)) + return 0; + + if (!SHMEM2_HAS(bp, eee_status[BP_PORT(bp)])) { + DP(BNX2X_MSG_ETHTOOL, "BC Version does not support EEE\n"); + return -EOPNOTSUPP; + } + + eee_cfg = SHMEM2_RD(bp, eee_status[BP_PORT(bp)]); + + if (!(eee_cfg & SHMEM_EEE_SUPPORTED_MASK)) { + DP(BNX2X_MSG_ETHTOOL, "Board does not support EEE!\n"); + return -EOPNOTSUPP; + } + + advertised = bnx2x_adv_to_eee(edata->advertised, + SHMEM_EEE_ADV_STATUS_SHIFT); + if ((advertised != (eee_cfg & SHMEM_EEE_ADV_STATUS_MASK))) { + DP(BNX2X_MSG_ETHTOOL, + "Direct manipulation of EEE advertisment is not supported\n"); + return -EINVAL; + } + + if (edata->tx_lpi_timer > EEE_MODE_TIMER_MASK) { + DP(BNX2X_MSG_ETHTOOL, + "Maximal Tx Lpi timer supported is %x(u)\n", + EEE_MODE_TIMER_MASK); + return -EINVAL; + } + if (edata->tx_lpi_enabled && + (edata->tx_lpi_timer < EEE_MODE_NVRAM_AGGRESSIVE_TIME)) { + DP(BNX2X_MSG_ETHTOOL, + "Minimal Tx Lpi timer supported is %d(u)\n", + EEE_MODE_NVRAM_AGGRESSIVE_TIME); + return -EINVAL; + } + + /* All is well; Apply changes*/ + if (edata->eee_enabled) + bp->link_params.eee_mode |= EEE_MODE_ADV_LPI; + else + bp->link_params.eee_mode &= ~EEE_MODE_ADV_LPI; + + if (edata->tx_lpi_enabled) + bp->link_params.eee_mode |= EEE_MODE_ENABLE_LPI; + else + bp->link_params.eee_mode &= ~EEE_MODE_ENABLE_LPI; + + bp->link_params.eee_mode &= ~EEE_MODE_TIMER_MASK; + bp->link_params.eee_mode |= (edata->tx_lpi_timer & + EEE_MODE_TIMER_MASK) | + EEE_MODE_OVERRIDE_NVRAM | + EEE_MODE_OUTPUT_TIME; + + /* Restart link to propogate changes */ + if (netif_running(dev)) { + bnx2x_stats_handle(bp, STATS_EVENT_STOP); + bnx2x_link_set(bp); + } + + return 0; +} + + enum { BNX2X_CHIP_E1_OFST = 0, BNX2X_CHIP_E1H_OFST, @@ -2472,6 +2604,8 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .get_rxfh_indir_size = bnx2x_get_rxfh_indir_size, .get_rxfh_indir = bnx2x_get_rxfh_indir, .set_rxfh_indir = bnx2x_set_rxfh_indir, + .get_eee = bnx2x_get_eee, + .set_eee = bnx2x_set_eee, }; void bnx2x_set_ethtool_ops(struct net_device *netdev) -- cgit v1.2.3 From ccf7e72b54253f926a35fdfa0366e0cb9ba72ffd Mon Sep 17 00:00:00 2001 From: Hong zhi guo Date: Wed, 6 Jun 2012 22:36:27 +0000 Subject: macvtap: use prepare_to_wait/finish_wait to ensure mb instead of raw assignment to current->state Signed-off-by: Hong Zhiguo Signed-off-by: David S. Miller --- drivers/net/macvtap.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 2ee56de7b0ca..0737bd4d1669 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -847,13 +847,12 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, const struct iovec *iv, unsigned long len, int noblock) { - DECLARE_WAITQUEUE(wait, current); + DEFINE_WAIT(wait); struct sk_buff *skb; ssize_t ret = 0; - add_wait_queue(sk_sleep(&q->sk), &wait); while (len) { - current->state = TASK_INTERRUPTIBLE; + prepare_to_wait(sk_sleep(&q->sk), &wait, TASK_INTERRUPTIBLE); /* Read frames from the queue */ skb = skb_dequeue(&q->sk.sk_receive_queue); @@ -875,8 +874,7 @@ static ssize_t macvtap_do_read(struct macvtap_queue *q, struct kiocb *iocb, break; } - current->state = TASK_RUNNING; - remove_wait_queue(sk_sleep(&q->sk), &wait); + finish_wait(sk_sleep(&q->sk), &wait); return ret; } -- cgit v1.2.3 From 1578e7778fa04eb7e32da561effee6cd38139b0f Mon Sep 17 00:00:00 2001 From: Padmanabh Ratnakar Date: Thu, 7 Jun 2012 04:37:08 +0000 Subject: be2net: Fix driver load for VFs for Lancer Permanent MAC is wrongly supplied in create iface command. Call the command with no MAC address and then MAC address should be later queried and applied. Signed-off-by: Padmanabh Ratnakar Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_cmds.c | 21 +++---- drivers/net/ethernet/emulex/benet/be_cmds.h | 8 +-- drivers/net/ethernet/emulex/benet/be_main.c | 98 ++++++++++++++++------------- 3 files changed, 66 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index 8d06ea381741..f8997521b147 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -1132,7 +1132,7 @@ err: * Uses MCCQ */ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags, - u8 *mac, u32 *if_handle, u32 *pmac_id, u32 domain) + u32 *if_handle, u32 domain) { struct be_mcc_wrb *wrb; struct be_cmd_req_if_create *req; @@ -1152,17 +1152,13 @@ int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags, req->hdr.domain = domain; req->capability_flags = cpu_to_le32(cap_flags); req->enable_flags = cpu_to_le32(en_flags); - if (mac) - memcpy(req->mac_addr, mac, ETH_ALEN); - else - req->pmac_invalid = true; + + req->pmac_invalid = true; status = be_mcc_notify_wait(adapter); if (!status) { struct be_cmd_resp_if_create *resp = embedded_payload(wrb); *if_handle = le32_to_cpu(resp->interface_id); - if (mac) - *pmac_id = le32_to_cpu(resp->pmac_id); } err: @@ -2330,8 +2326,8 @@ err: } /* Uses synchronous MCCQ */ -int be_cmd_get_mac_from_list(struct be_adapter *adapter, u32 domain, - bool *pmac_id_active, u32 *pmac_id, u8 *mac) +int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac, + bool *pmac_id_active, u32 *pmac_id, u8 domain) { struct be_mcc_wrb *wrb; struct be_cmd_req_get_mac_list *req; @@ -2376,8 +2372,9 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u32 domain, get_mac_list_cmd.va; mac_count = resp->true_mac_count + resp->pseudo_mac_count; /* Mac list returned could contain one or more active mac_ids - * or one or more pseudo permanant mac addresses. If an active - * mac_id is present, return first active mac_id found + * or one or more true or pseudo permanant mac addresses. + * If an active mac_id is present, return first active mac_id + * found. */ for (i = 0; i < mac_count; i++) { struct get_list_macaddr *mac_entry; @@ -2396,7 +2393,7 @@ int be_cmd_get_mac_from_list(struct be_adapter *adapter, u32 domain, goto out; } } - /* If no active mac_id found, return first pseudo mac addr */ + /* If no active mac_id found, return first mac addr */ *pmac_id_active = false; memcpy(mac, resp->macaddr_list[0].mac_addr_id.macaddr, ETH_ALEN); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 9625bf420c16..2f6bb06df9c6 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -1664,8 +1664,7 @@ extern int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, extern int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, int pmac_id, u32 domain); extern int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, - u32 en_flags, u8 *mac, u32 *if_handle, u32 *pmac_id, - u32 domain); + u32 en_flags, u32 *if_handle, u32 domain); extern int be_cmd_if_destroy(struct be_adapter *adapter, int if_handle, u32 domain); extern int be_cmd_eq_create(struct be_adapter *adapter, @@ -1751,8 +1750,9 @@ extern int be_cmd_get_cntl_attributes(struct be_adapter *adapter); extern int be_cmd_req_native_mode(struct be_adapter *adapter); extern int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size); extern void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf); -extern int be_cmd_get_mac_from_list(struct be_adapter *adapter, u32 domain, - bool *pmac_id_active, u32 *pmac_id, u8 *mac); +extern int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac, + bool *pmac_id_active, u32 *pmac_id, + u8 domain); extern int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array, u8 mac_count, u32 domain); extern int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid, diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index f29827f657eb..896f283967d4 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2601,8 +2601,8 @@ static int be_vf_setup(struct be_adapter *adapter) cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_MULTICAST; for_all_vfs(adapter, vf_cfg, vf) { - status = be_cmd_if_create(adapter, cap_flags, en_flags, NULL, - &vf_cfg->if_handle, NULL, vf + 1); + status = be_cmd_if_create(adapter, cap_flags, en_flags, + &vf_cfg->if_handle, vf + 1); if (status) goto err; } @@ -2642,29 +2642,43 @@ static void be_setup_init(struct be_adapter *adapter) adapter->phy.forced_port_speed = -1; } -static int be_add_mac_from_list(struct be_adapter *adapter, u8 *mac) +static int be_get_mac_addr(struct be_adapter *adapter, u8 *mac, u32 if_handle, + bool *active_mac, u32 *pmac_id) { - u32 pmac_id; - int status; - bool pmac_id_active; + int status = 0; - status = be_cmd_get_mac_from_list(adapter, 0, &pmac_id_active, - &pmac_id, mac); - if (status != 0) - goto do_none; + if (!is_zero_ether_addr(adapter->netdev->perm_addr)) { + memcpy(mac, adapter->netdev->dev_addr, ETH_ALEN); + if (!lancer_chip(adapter) && !be_physfn(adapter)) + *active_mac = true; + else + *active_mac = false; - if (pmac_id_active) { - status = be_cmd_mac_addr_query(adapter, mac, - MAC_ADDRESS_TYPE_NETWORK, - false, adapter->if_handle, pmac_id); + return status; + } - if (!status) - adapter->pmac_id[0] = pmac_id; + if (lancer_chip(adapter)) { + status = be_cmd_get_mac_from_list(adapter, mac, + active_mac, pmac_id, 0); + if (*active_mac) { + status = be_cmd_mac_addr_query(adapter, mac, + MAC_ADDRESS_TYPE_NETWORK, + false, if_handle, + *pmac_id); + } + } else if (be_physfn(adapter)) { + /* For BE3, for PF get permanent MAC */ + status = be_cmd_mac_addr_query(adapter, mac, + MAC_ADDRESS_TYPE_NETWORK, true, + 0, 0); + *active_mac = false; } else { - status = be_cmd_pmac_add(adapter, mac, - adapter->if_handle, &adapter->pmac_id[0], 0); + /* For BE3, for VF get soft MAC assigned by PF*/ + status = be_cmd_mac_addr_query(adapter, mac, + MAC_ADDRESS_TYPE_NETWORK, false, + if_handle, 0); + *active_mac = true; } -do_none: return status; } @@ -2685,12 +2699,12 @@ static int be_get_config(struct be_adapter *adapter) static int be_setup(struct be_adapter *adapter) { - struct net_device *netdev = adapter->netdev; struct device *dev = &adapter->pdev->dev; u32 cap_flags, en_flags; u32 tx_fc, rx_fc; int status; u8 mac[ETH_ALEN]; + bool active_mac; be_setup_init(adapter); @@ -2716,14 +2730,6 @@ static int be_setup(struct be_adapter *adapter) if (status) goto err; - memset(mac, 0, ETH_ALEN); - status = be_cmd_mac_addr_query(adapter, mac, MAC_ADDRESS_TYPE_NETWORK, - true /*permanent */, 0, 0); - if (status) - return status; - memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN); - memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); - en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS; cap_flags = en_flags | BE_IF_FLAGS_MCAST_PROMISCUOUS | @@ -2733,27 +2739,29 @@ static int be_setup(struct be_adapter *adapter) cap_flags |= BE_IF_FLAGS_RSS; en_flags |= BE_IF_FLAGS_RSS; } + status = be_cmd_if_create(adapter, cap_flags, en_flags, - netdev->dev_addr, &adapter->if_handle, - &adapter->pmac_id[0], 0); + &adapter->if_handle, 0); if (status != 0) goto err; - /* The VF's permanent mac queried from card is incorrect. - * For BEx: Query the mac configued by the PF using if_handle - * For Lancer: Get and use mac_list to obtain mac address. - */ - if (!be_physfn(adapter)) { - if (lancer_chip(adapter)) - status = be_add_mac_from_list(adapter, mac); - else - status = be_cmd_mac_addr_query(adapter, mac, - MAC_ADDRESS_TYPE_NETWORK, false, - adapter->if_handle, 0); - if (!status) { - memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN); - memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); - } + memset(mac, 0, ETH_ALEN); + active_mac = false; + status = be_get_mac_addr(adapter, mac, adapter->if_handle, + &active_mac, &adapter->pmac_id[0]); + if (status != 0) + goto err; + + if (!active_mac) { + status = be_cmd_pmac_add(adapter, mac, adapter->if_handle, + &adapter->pmac_id[0], 0); + if (status != 0) + goto err; + } + + if (is_zero_ether_addr(adapter->netdev->dev_addr)) { + memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN); + memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); } status = be_tx_qs_create(adapter); -- cgit v1.2.3 From 5a6881e8e134ea636bbc8423049e84638dbb7106 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 Jun 2012 10:05:14 +0800 Subject: regulator: core: Handle fixed voltage in map_voltage_linear Fixed voltage is a kind of linear mapping where n_voltages is 1. This change allows [list|map]_voltage_linear to be used for fixed voltage. For fixed voltage, n_voltages is 1 and the only valid selector is 0. Thus we actually don't care the uV_step setting. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 8521e0d6b3bc..5c6aedaa934d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2070,6 +2070,14 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev, { int ret, voltage; + /* Allow uV_step to be 0 for fixed voltage */ + if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) { + if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV) + return 0; + else + return -EINVAL; + } + if (!rdev->desc->uV_step) { BUG_ON(!rdev->desc->uV_step); return -EINVAL; -- cgit v1.2.3 From 985f769c89aedcae2981080051fc85b4ed035a9b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 Jun 2012 10:06:40 +0800 Subject: regulator: isl6271a: Use regulator_[list|map]_voltage_linear for isl_fixed_ops Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/isl6271a-regulator.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index 56d273f25603..4a11b82a6d57 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -75,19 +75,13 @@ static struct regulator_ops isl_core_ops = { static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) { - int id = rdev_get_id(dev); - return (id == 1) ? 1100000 : 1300000; -} - -static int isl6271a_list_fixed_voltage(struct regulator_dev *dev, unsigned selector) -{ - int id = rdev_get_id(dev); - return (id == 1) ? 1100000 : 1300000; + return dev->desc->min_uV; } static struct regulator_ops isl_fixed_ops = { .get_voltage = isl6271a_get_fixed_voltage, - .list_voltage = isl6271a_list_fixed_voltage, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, }; static const struct regulator_desc isl_rd[] = { @@ -107,6 +101,7 @@ static const struct regulator_desc isl_rd[] = { .ops = &isl_fixed_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .min_uV = 1100000, }, { .name = "LDO2", .id = 2, @@ -114,6 +109,7 @@ static const struct regulator_desc isl_rd[] = { .ops = &isl_fixed_ops, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .min_uV = 1300000, }, }; -- cgit v1.2.3 From ee3ed6effd8cc4c0cbfa35307cfc5fcec75b5e12 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 Jun 2012 10:38:30 +0800 Subject: regulator: da903x: Don't read/write to DA9030_INVAL/DA9034_INVAL address For fixed voltage, DA9030_LDO13 and DA9034_LDO5, the info->vol_reg is DA9030_INVAL/DA9034_INVAL. It does not make sense to read/write to DA9030_INVAL/DA9034_INVAL address. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/da903x.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 1005f5f7e603..36c5b92fe0af 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -107,6 +107,9 @@ static int da903x_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) struct device *da9034_dev = to_da903x_dev(rdev); uint8_t val, mask; + if (rdev->desc->n_voltages == 1) + return -EINVAL; + val = selector << info->vol_shift; mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; @@ -120,6 +123,9 @@ static int da903x_get_voltage_sel(struct regulator_dev *rdev) uint8_t val, mask; int ret; + if (rdev->desc->n_voltages == 1) + return 0; + ret = da903x_read(da9034_dev, info->vol_reg, &val); if (ret) return ret; -- cgit v1.2.3 From 1acb645ebfe3a008e3c3350918d7d29974b1eaec Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 Jun 2012 16:38:09 +0800 Subject: regulator: rc5t583: Simplify rc5t583_set_voltage_time_sel implementation For linear mappings, we can use below equation to get the voltage difference between new_selector and old_selector: abs(new_selector - old_selector) * rdev->desc->uV_step Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/rc5t583-regulator.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c index 1d34e64a1307..332eae897dab 100644 --- a/drivers/regulator/rc5t583-regulator.c +++ b/drivers/regulator/rc5t583-regulator.c @@ -70,18 +70,10 @@ static int rc5t583_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); - int old_uV, new_uV; - old_uV = regulator_list_voltage_linear(rdev, old_selector); - if (old_uV < 0) - return old_uV; - - new_uV = regulator_list_voltage_linear(rdev, new_selector); - if (new_uV < 0) - return new_uV; - - return DIV_ROUND_UP(abs(old_uV - new_uV), - reg->reg_info->change_uv_per_us); + return DIV_ROUND_UP(abs(new_selector - old_selector) * + rdev->desc->uV_step, + reg->reg_info->change_uv_per_us); } -- cgit v1.2.3 From 999f0c7cb8e7c378bd1f3fcd56cf8dc5a30c83e4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 7 Jun 2012 17:08:21 +0800 Subject: regulator: palmas: Clear PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK bits in palmas_set_mode_smps Current code actually clears more bits than PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK bits. Signed-off-by: Axel Lin Acked-by: Graeme Gregory Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index b4e10b0b36d4..1a0924d81b2a 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -257,8 +257,7 @@ static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode) unsigned int reg; palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®); - reg &= ~PALMAS_SMPS12_CTRL_STATUS_MASK; - reg >>= PALMAS_SMPS12_CTRL_STATUS_SHIFT; + reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK; switch (mode) { case REGULATOR_MODE_NORMAL: -- cgit v1.2.3 From fedd89b1aafd6b2bf7c0b4009fd616a02484ae80 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 6 Jun 2012 20:01:38 +0800 Subject: regulator: palmas: Slightly code change for better readability It's a little bit hard to read that the "else" case means id == PALMAS_REG_SMPS10. if (id != PALMAS_REG_SMPS10){ do something for the cases id != PALMAS_REG_SMPS10; } else { do something for the case id == PALMAS_REG_SMPS10; } This patch changes above syntax to switch statement. Signed-off-by: Axel Lin Acked-by: Graeme Gregory Signed-off-by: Mark Brown --- drivers/regulator/palmas-regulator.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c index 1a0924d81b2a..55267bbfcd9c 100644 --- a/drivers/regulator/palmas-regulator.c +++ b/drivers/regulator/palmas-regulator.c @@ -516,7 +516,15 @@ static int palmas_smps_init(struct palmas *palmas, int id, if (ret) return ret; - if (id != PALMAS_REG_SMPS10) { + switch (id) { + case PALMAS_REG_SMPS10: + if (reg_init->mode_sleep) { + reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK; + reg |= reg_init->mode_sleep << + PALMAS_SMPS10_CTRL_MODE_SLEEP_SHIFT; + } + break; + default: if (reg_init->warm_reset) reg |= PALMAS_SMPS12_CTRL_WR_S; @@ -528,14 +536,8 @@ static int palmas_smps_init(struct palmas *palmas, int id, reg |= reg_init->mode_sleep << PALMAS_SMPS12_CTRL_MODE_SLEEP_SHIFT; } - } else { - if (reg_init->mode_sleep) { - reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK; - reg |= reg_init->mode_sleep << - PALMAS_SMPS10_CTRL_MODE_SLEEP_SHIFT; - } - } + ret = palmas_smps_write(palmas, addr, reg); if (ret) return ret; @@ -659,10 +661,8 @@ static __devinit int palmas_probe(struct platform_device *pdev) pmic->desc[id].name = palmas_regs_info[id].name; pmic->desc[id].id = id; - if (id != PALMAS_REG_SMPS10) { - pmic->desc[id].ops = &palmas_ops_smps; - pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES; - } else { + switch (id) { + case PALMAS_REG_SMPS10: pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES; pmic->desc[id].ops = &palmas_ops_smps10; pmic->desc[id].vsel_reg = PALMAS_SMPS10_CTRL; @@ -671,6 +671,10 @@ static __devinit int palmas_probe(struct platform_device *pdev) pmic->desc[id].enable_mask = SMPS10_BOOST_EN; pmic->desc[id].min_uV = 3750000; pmic->desc[id].uV_step = 1250000; + break; + default: + pmic->desc[id].ops = &palmas_ops_smps; + pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES; } pmic->desc[id].type = REGULATOR_VOLTAGE; -- cgit v1.2.3 From c49af95631a46e8a2664ae2067c900905c39f2b2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 6 Jun 2012 08:36:37 +0800 Subject: regulator: pcap: Convert to regulator_list_voltage_table Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/pcap-regulator.c | 95 +++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 53 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index 8211101121f0..68777acc099f 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -18,80 +18,80 @@ #include #include -static const u16 V1_table[] = { - 2775, 1275, 1600, 1725, 1825, 1925, 2075, 2275, +static const unsigned int V1_table[] = { + 2775000, 1275000, 1600000, 1725000, 1825000, 1925000, 2075000, 2275000, }; -static const u16 V2_table[] = { - 2500, 2775, +static const unsigned int V2_table[] = { + 2500000, 2775000, }; -static const u16 V3_table[] = { - 1075, 1275, 1550, 1725, 1876, 1950, 2075, 2275, +static const unsigned int V3_table[] = { + 1075000, 1275000, 1550000, 1725000, 1876000, 1950000, 2075000, 2275000, }; -static const u16 V4_table[] = { - 1275, 1550, 1725, 1875, 1950, 2075, 2275, 2775, +static const unsigned int V4_table[] = { + 1275000, 1550000, 1725000, 1875000, 1950000, 2075000, 2275000, 2775000, }; -static const u16 V5_table[] = { - 1875, 2275, 2475, 2775, +static const unsigned int V5_table[] = { + 1875000, 2275000, 2475000, 2775000, }; -static const u16 V6_table[] = { - 2475, 2775, +static const unsigned int V6_table[] = { + 2475000, 2775000, }; -static const u16 V7_table[] = { - 1875, 2775, +static const unsigned int V7_table[] = { + 1875000, 2775000, }; #define V8_table V4_table -static const u16 V9_table[] = { - 1575, 1875, 2475, 2775, +static const unsigned int V9_table[] = { + 1575000, 1875000, 2475000, 2775000, }; -static const u16 V10_table[] = { - 5000, +static const unsigned int V10_table[] = { + 5000000, }; -static const u16 VAUX1_table[] = { - 1875, 2475, 2775, 3000, +static const unsigned int VAUX1_table[] = { + 1875000, 2475000, 2775000, 3000000, }; #define VAUX2_table VAUX1_table -static const u16 VAUX3_table[] = { - 1200, 1200, 1200, 1200, 1400, 1600, 1800, 2000, - 2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600, +static const unsigned int VAUX3_table[] = { + 1200000, 1200000, 1200000, 1200000, 1400000, 1600000, 1800000, 2000000, + 2200000, 2400000, 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, }; -static const u16 VAUX4_table[] = { - 1800, 1800, 3000, 5000, +static const unsigned int VAUX4_table[] = { + 1800000, 1800000, 3000000, 5000000, }; -static const u16 VSIM_table[] = { - 1875, 3000, +static const unsigned int VSIM_table[] = { + 1875000, 3000000, }; -static const u16 VSIM2_table[] = { - 1875, +static const unsigned int VSIM2_table[] = { + 1875000, }; -static const u16 VVIB_table[] = { - 1300, 1800, 2000, 3000, +static const unsigned int VVIB_table[] = { + 1300000, 1800000, 2000000, 3000000, }; -static const u16 SW1_table[] = { - 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, - 1300, 1350, 1400, 1450, 1500, 1600, 1875, 2250, +static const unsigned int SW1_table[] = { + 900000, 950000, 1000000, 1050000, 1100000, 1150000, 1200000, 1250000, + 1300000, 1350000, 1400000, 1450000, 1500000, 1600000, 1875000, 2250000, }; #define SW2_table SW1_table -static const u16 SW3_table[] = { - 4000, 4500, 5000, 5500, +static const unsigned int SW3_table[] = { + 4000000, 4500000, 5000000, 5500000, }; struct pcap_regulator { @@ -100,8 +100,6 @@ struct pcap_regulator { const u8 index; const u8 stby; const u8 lowpwr; - const u8 n_voltages; - const u16 *voltage_table; }; #define NA 0xff @@ -113,8 +111,6 @@ struct pcap_regulator { .index = _index, \ .stby = _stby, \ .lowpwr = _lowpwr, \ - .n_voltages = ARRAY_SIZE(_vreg##_table), \ - .voltage_table = _vreg##_table, \ } static struct pcap_regulator vreg_table[] = { @@ -157,11 +153,11 @@ static int pcap_regulator_set_voltage_sel(struct regulator_dev *rdev, void *pcap = rdev_get_drvdata(rdev); /* the regulator doesn't support voltage switching */ - if (vreg->n_voltages == 1) + if (rdev->desc->n_voltages == 1) return -EINVAL; return ezx_pcap_set_bits(pcap, vreg->reg, - (vreg->n_voltages - 1) << vreg->index, + (rdev->desc->n_voltages - 1) << vreg->index, selector << vreg->index); } @@ -171,11 +167,11 @@ static int pcap_regulator_get_voltage_sel(struct regulator_dev *rdev) void *pcap = rdev_get_drvdata(rdev); u32 tmp; - if (vreg->n_voltages == 1) + if (rdev->desc->n_voltages == 1) return 0; ezx_pcap_read(pcap, vreg->reg, &tmp); - tmp = ((tmp >> vreg->index) & (vreg->n_voltages - 1)); + tmp = ((tmp >> vreg->index) & (rdev->desc->n_voltages - 1)); return tmp; } @@ -214,16 +210,8 @@ static int pcap_regulator_is_enabled(struct regulator_dev *rdev) return (tmp >> vreg->en) & 1; } -static int pcap_regulator_list_voltage(struct regulator_dev *rdev, - unsigned int index) -{ - struct pcap_regulator *vreg = &vreg_table[rdev_get_id(rdev)]; - - return vreg->voltage_table[index] * 1000; -} - static struct regulator_ops pcap_regulator_ops = { - .list_voltage = pcap_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .set_voltage_sel = pcap_regulator_set_voltage_sel, .get_voltage_sel = pcap_regulator_get_voltage_sel, .enable = pcap_regulator_enable, @@ -236,6 +224,7 @@ static struct regulator_ops pcap_regulator_ops = { .name = #_vreg, \ .id = _vreg, \ .n_voltages = ARRAY_SIZE(_vreg##_table), \ + .volt_table = _vreg##_table, \ .ops = &pcap_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ -- cgit v1.2.3 From f46470379342033bdd9f740ce483c96f5c20eab2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 5 Jun 2012 22:52:06 +0800 Subject: regulator: tps6586x: Convert to regulator_list_voltage_table Also adds tps6586x_ldo0_voltages mapping table for LDO0, so we can remove the hack in tps6586x_list_voltage() and convert it to regulator_list_voltage_table. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6586x-regulator.c | 61 ++++++++++++++-------------------- 1 file changed, 25 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index c0a214575380..c0ac2418d6ab 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -63,8 +63,6 @@ struct tps6586x_regulator { int enable_bit[2]; int enable_reg[2]; - int *voltages; - /* for DVM regulators */ int go_reg; int go_bit; @@ -75,19 +73,6 @@ static inline struct device *to_tps6586x_dev(struct regulator_dev *rdev) return rdev_get_dev(rdev)->parent->parent; } -static int tps6586x_list_voltage(struct regulator_dev *rdev, unsigned selector) -{ - struct tps6586x_regulator *info = rdev_get_drvdata(rdev); - int rid = rdev_get_id(rdev); - - /* LDO0 has minimal voltage 1.2V rather than 1.25V */ - if ((rid == TPS6586X_ID_LDO_0) && (selector == 0)) - return (info->voltages[0] - 50) * 1000; - - return info->voltages[selector] * 1000; -} - - static int tps6586x_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { @@ -168,7 +153,7 @@ static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev) } static struct regulator_ops tps6586x_regulator_ops = { - .list_voltage = tps6586x_list_voltage, + .list_voltage = regulator_list_voltage_table, .get_voltage_sel = tps6586x_get_voltage_sel, .set_voltage_sel = tps6586x_set_voltage_sel, @@ -177,29 +162,33 @@ static struct regulator_ops tps6586x_regulator_ops = { .disable = tps6586x_regulator_disable, }; -static int tps6586x_ldo_voltages[] = { - 1250, 1500, 1800, 2500, 2700, 2850, 3100, 3300, +static const unsigned int tps6586x_ldo0_voltages[] = { + 1200000, 1500000, 1800000, 2500000, 2700000, 2850000, 3100000, 3300000, +}; + +static const unsigned int tps6586x_ldo4_voltages[] = { + 1700000, 1725000, 1750000, 1775000, 1800000, 1825000, 1850000, 1875000, + 1900000, 1925000, 1950000, 1975000, 2000000, 2025000, 2050000, 2075000, + 2100000, 2125000, 2150000, 2175000, 2200000, 2225000, 2250000, 2275000, + 2300000, 2325000, 2350000, 2375000, 2400000, 2425000, 2450000, 2475000, }; -static int tps6586x_ldo4_voltages[] = { - 1700, 1725, 1750, 1775, 1800, 1825, 1850, 1875, - 1900, 1925, 1950, 1975, 2000, 2025, 2050, 2075, - 2100, 2125, 2150, 2175, 2200, 2225, 2250, 2275, - 2300, 2325, 2350, 2375, 2400, 2425, 2450, 2475, +static const unsigned int tps6586x_ldo_voltages[] = { + 1250000, 1500000, 1800000, 2500000, 2700000, 2850000, 3100000, 3300000, }; -static int tps6586x_sm2_voltages[] = { - 3000, 3050, 3100, 3150, 3200, 3250, 3300, 3350, - 3400, 3450, 3500, 3550, 3600, 3650, 3700, 3750, - 3800, 3850, 3900, 3950, 4000, 4050, 4100, 4150, - 4200, 4250, 4300, 4350, 4400, 4450, 4500, 4550, +static const unsigned int tps6586x_sm2_voltages[] = { + 3000000, 3050000, 3100000, 3150000, 3200000, 3250000, 3300000, 3350000, + 3400000, 3450000, 3500000, 3550000, 3600000, 3650000, 3700000, 3750000, + 3800000, 3850000, 3900000, 3950000, 4000000, 4050000, 4100000, 4150000, + 4200000, 4250000, 4300000, 4350000, 4400000, 4450000, 4500000, 4550000, }; -static int tps6586x_dvm_voltages[] = { - 725, 750, 775, 800, 825, 850, 875, 900, - 925, 950, 975, 1000, 1025, 1050, 1075, 1100, - 1125, 1150, 1175, 1200, 1225, 1250, 1275, 1300, - 1325, 1350, 1375, 1400, 1425, 1450, 1475, 1500, +static const unsigned int tps6586x_dvm_voltages[] = { + 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, + 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, 1100000, + 1125000, 1150000, 1175000, 1200000, 1225000, 1250000, 1275000, 1300000, + 1325000, 1350000, 1375000, 1400000, 1425000, 1450000, 1475000, 1500000, }; #define TPS6586X_REGULATOR(_id, vdata, vreg, shift, nbits, \ @@ -210,6 +199,7 @@ static int tps6586x_dvm_voltages[] = { .type = REGULATOR_VOLTAGE, \ .id = TPS6586X_ID_##_id, \ .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \ + .volt_table = tps6586x_##vdata##_voltages, \ .owner = THIS_MODULE, \ }, \ .volt_reg = TPS6586X_##vreg, \ @@ -218,8 +208,7 @@ static int tps6586x_dvm_voltages[] = { .enable_reg[0] = TPS6586X_SUPPLY##ereg0, \ .enable_bit[0] = (ebit0), \ .enable_reg[1] = TPS6586X_SUPPLY##ereg1, \ - .enable_bit[1] = (ebit1), \ - .voltages = tps6586x_##vdata##_voltages, + .enable_bit[1] = (ebit1), #define TPS6586X_REGULATOR_DVM_GOREG(goreg, gobit) \ .go_reg = TPS6586X_##goreg, \ @@ -241,7 +230,7 @@ static int tps6586x_dvm_voltages[] = { } static struct tps6586x_regulator tps6586x_regulator[] = { - TPS6586X_LDO(LDO_0, ldo, SUPPLYV1, 5, 3, ENC, 0, END, 0), + TPS6586X_LDO(LDO_0, ldo0, SUPPLYV1, 5, 3, ENC, 0, END, 0), TPS6586X_LDO(LDO_3, ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2), TPS6586X_LDO(LDO_5, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6), TPS6586X_LDO(LDO_6, ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4), -- cgit v1.2.3 From 93f5de5c58a863b48a8c44d70c4f7e3d0c7af50e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 08:25:41 +0800 Subject: regulator: max1586: Convert max1586_v3_ops to regulator_list_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max1586.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 126c7d8f31c6..a54771e4d960 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -63,14 +63,6 @@ static int v6_voltages_uv[] = { 1, 1800000, 2500000, 3000000 }; * R24 and R25=100kOhm as described in the data sheet. * The gain is approximately: 1 + R24/R25 + R24/185.5kOhm */ -static int max1586_v3_calc_voltage(struct max1586_data *max1586, - unsigned selector) -{ - unsigned range_uV = max1586->max_uV - max1586->min_uV; - - return max1586->min_uV + (selector * range_uV / MAX1586_V3_MAX_VSEL); -} - static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { @@ -86,25 +78,16 @@ static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV, *selector = DIV_ROUND_UP((min_uV - max1586->min_uV) * MAX1586_V3_MAX_VSEL, range_uV); - if (max1586_v3_calc_voltage(max1586, *selector) > max_uV) + if (regulator_list_voltage_linear(rdev, *selector) > max_uV) return -EINVAL; dev_dbg(&client->dev, "changing voltage v3 to %dmv\n", - max1586_v3_calc_voltage(max1586, *selector) / 1000); + regulator_list_voltage_linear(rdev, *selector) / 1000); v3_prog = I2C_V3_SELECT | (u8) *selector; return i2c_smbus_write_byte(client, v3_prog); } -static int max1586_v3_list(struct regulator_dev *rdev, unsigned selector) -{ - struct max1586_data *max1586 = rdev_get_drvdata(rdev); - - if (selector > MAX1586_V3_MAX_VSEL) - return -EINVAL; - return max1586_v3_calc_voltage(max1586, selector); -} - static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) { @@ -124,7 +107,7 @@ static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev, */ static struct regulator_ops max1586_v3_ops = { .set_voltage = max1586_v3_set, - .list_voltage = max1586_v3_list, + .list_voltage = regulator_list_voltage_linear, }; static struct regulator_ops max1586_v6_ops = { @@ -132,7 +115,7 @@ static struct regulator_ops max1586_v6_ops = { .list_voltage = regulator_list_voltage_table, }; -static const struct regulator_desc max1586_reg[] = { +static struct regulator_desc max1586_reg[] = { { .name = "Output_V3", .id = MAX1586_V3, @@ -185,6 +168,13 @@ static int __devinit max1586_pmic_probe(struct i2c_client *client, goto err; } + if (id == MAX1586_V3) { + max1586_reg[id].min_uV = max1586->min_uV; + max1586_reg[id].uV_step = + (max1586->max_uV - max1586->min_uV) / + MAX1586_V3_MAX_VSEL; + } + config.dev = &client->dev; config.init_data = pdata->subdevs[i].platform_data; config.driver_data = max1586; -- cgit v1.2.3 From 9b558950bb652a85861fef28172e134c367dab1a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 08:26:44 +0800 Subject: regulator: max1586: Convert max1586_v3_ops to set_voltage_sel and map_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max1586.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index a54771e4d960..f67af3c1b963 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -63,28 +63,17 @@ static int v6_voltages_uv[] = { 1, 1800000, 2500000, 3000000 }; * R24 and R25=100kOhm as described in the data sheet. * The gain is approximately: 1 + R24/R25 + R24/185.5kOhm */ -static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV, - unsigned *selector) +static int max1586_v3_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) { struct max1586_data *max1586 = rdev_get_drvdata(rdev); struct i2c_client *client = max1586->client; - unsigned range_uV = max1586->max_uV - max1586->min_uV; u8 v3_prog; - if (min_uV > max1586->max_uV || max_uV < max1586->min_uV) - return -EINVAL; - if (min_uV < max1586->min_uV) - min_uV = max1586->min_uV; - - *selector = DIV_ROUND_UP((min_uV - max1586->min_uV) * - MAX1586_V3_MAX_VSEL, range_uV); - if (regulator_list_voltage_linear(rdev, *selector) > max_uV) - return -EINVAL; - dev_dbg(&client->dev, "changing voltage v3 to %dmv\n", - regulator_list_voltage_linear(rdev, *selector) / 1000); + regulator_list_voltage_linear(rdev, selector) / 1000); - v3_prog = I2C_V3_SELECT | (u8) *selector; + v3_prog = I2C_V3_SELECT | (u8) selector; return i2c_smbus_write_byte(client, v3_prog); } @@ -106,8 +95,9 @@ static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev, * the set up value. */ static struct regulator_ops max1586_v3_ops = { - .set_voltage = max1586_v3_set, + .set_voltage_sel = max1586_v3_set_voltage_sel, .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, }; static struct regulator_ops max1586_v6_ops = { -- cgit v1.2.3 From ec8a1586780cbb437aeb2006957c5bbe113c7046 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 6 Jun 2012 10:55:27 +0530 Subject: dma: tegra: add dmaengine based dma driver Add dmaengine based NVIDIA's Tegra APB DMA driver. This driver support the slave mode of data transfer from peripheral to memory and vice versa. The driver supports for the cyclic and non-cyclic mode of data transfer. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 14 + drivers/dma/Makefile | 1 + drivers/dma/tegra20-apb-dma.c | 1410 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1425 insertions(+) create mode 100644 drivers/dma/tegra20-apb-dma.c (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index c8416d54f917..b3761153649d 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -148,6 +148,20 @@ config TXX9_DMAC Support the TXx9 SoC internal DMA controller. This can be integrated in chips such as the Toshiba TX4927/38/39. +config TEGRA20_APB_DMA + bool "NVIDIA Tegra20 APB DMA support" + depends on ARCH_TEGRA + select DMA_ENGINE + help + Support for the NVIDIA Tegra20 APB DMA controller driver. The + DMA controller is having multiple DMA channel which can be + configured for different peripherals like audio, UART, SPI, + I2C etc which is in APB bus. + This DMA controller transfers data from memory to peripheral fifo + or vice versa. It does not support memory to memory data transfer. + + + config SH_DMAE tristate "Renesas SuperH DMAC support" depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE) diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 86b795baba98..d679ea3f6ab0 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MXS_DMA) += mxs-dma.o obj-$(CONFIG_TIMB_DMA) += timb_dma.o obj-$(CONFIG_SIRF_DMA) += sirf-dma.o obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o +obj-$(CONFIG_TEGRA20_APB_DMA) += tegra20-apb-dma.o obj-$(CONFIG_PL330_DMA) += pl330.o obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c new file mode 100644 index 000000000000..134ea7cfc795 --- /dev/null +++ b/drivers/dma/tegra20-apb-dma.c @@ -0,0 +1,1410 @@ +/* + * DMA driver for Nvidia's Tegra20 APB DMA controller. + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "dmaengine.h" + +#define TEGRA_APBDMA_GENERAL 0x0 +#define TEGRA_APBDMA_GENERAL_ENABLE BIT(31) + +#define TEGRA_APBDMA_CONTROL 0x010 +#define TEGRA_APBDMA_IRQ_MASK 0x01c +#define TEGRA_APBDMA_IRQ_MASK_SET 0x020 + +/* CSR register */ +#define TEGRA_APBDMA_CHAN_CSR 0x00 +#define TEGRA_APBDMA_CSR_ENB BIT(31) +#define TEGRA_APBDMA_CSR_IE_EOC BIT(30) +#define TEGRA_APBDMA_CSR_HOLD BIT(29) +#define TEGRA_APBDMA_CSR_DIR BIT(28) +#define TEGRA_APBDMA_CSR_ONCE BIT(27) +#define TEGRA_APBDMA_CSR_FLOW BIT(21) +#define TEGRA_APBDMA_CSR_REQ_SEL_SHIFT 16 +#define TEGRA_APBDMA_CSR_WCOUNT_MASK 0xFFFC + +/* STATUS register */ +#define TEGRA_APBDMA_CHAN_STATUS 0x004 +#define TEGRA_APBDMA_STATUS_BUSY BIT(31) +#define TEGRA_APBDMA_STATUS_ISE_EOC BIT(30) +#define TEGRA_APBDMA_STATUS_HALT BIT(29) +#define TEGRA_APBDMA_STATUS_PING_PONG BIT(28) +#define TEGRA_APBDMA_STATUS_COUNT_SHIFT 2 +#define TEGRA_APBDMA_STATUS_COUNT_MASK 0xFFFC + +/* AHB memory address */ +#define TEGRA_APBDMA_CHAN_AHBPTR 0x010 + +/* AHB sequence register */ +#define TEGRA_APBDMA_CHAN_AHBSEQ 0x14 +#define TEGRA_APBDMA_AHBSEQ_INTR_ENB BIT(31) +#define TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_8 (0 << 28) +#define TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_16 (1 << 28) +#define TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32 (2 << 28) +#define TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_64 (3 << 28) +#define TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_128 (4 << 28) +#define TEGRA_APBDMA_AHBSEQ_DATA_SWAP BIT(27) +#define TEGRA_APBDMA_AHBSEQ_BURST_1 (4 << 24) +#define TEGRA_APBDMA_AHBSEQ_BURST_4 (5 << 24) +#define TEGRA_APBDMA_AHBSEQ_BURST_8 (6 << 24) +#define TEGRA_APBDMA_AHBSEQ_DBL_BUF BIT(19) +#define TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT 16 +#define TEGRA_APBDMA_AHBSEQ_WRAP_NONE 0 + +/* APB address */ +#define TEGRA_APBDMA_CHAN_APBPTR 0x018 + +/* APB sequence register */ +#define TEGRA_APBDMA_CHAN_APBSEQ 0x01c +#define TEGRA_APBDMA_APBSEQ_BUS_WIDTH_8 (0 << 28) +#define TEGRA_APBDMA_APBSEQ_BUS_WIDTH_16 (1 << 28) +#define TEGRA_APBDMA_APBSEQ_BUS_WIDTH_32 (2 << 28) +#define TEGRA_APBDMA_APBSEQ_BUS_WIDTH_64 (3 << 28) +#define TEGRA_APBDMA_APBSEQ_BUS_WIDTH_128 (4 << 28) +#define TEGRA_APBDMA_APBSEQ_DATA_SWAP BIT(27) +#define TEGRA_APBDMA_APBSEQ_WRAP_WORD_1 (1 << 16) + +/* + * If any burst is in flight and DMA paused then this is the time to complete + * on-flight burst and update DMA status register. + */ +#define TEGRA_APBDMA_BURST_COMPLETE_TIME 20 + +/* Channel base address offset from APBDMA base address */ +#define TEGRA_APBDMA_CHANNEL_BASE_ADD_OFFSET 0x1000 + +/* DMA channel register space size */ +#define TEGRA_APBDMA_CHANNEL_REGISTER_SIZE 0x20 + +struct tegra_dma; + +/* + * tegra_dma_chip_data Tegra chip specific DMA data + * @nr_channels: Number of channels available in the controller. + * @max_dma_count: Maximum DMA transfer count supported by DMA controller. + */ +struct tegra_dma_chip_data { + int nr_channels; + int max_dma_count; +}; + +/* DMA channel registers */ +struct tegra_dma_channel_regs { + unsigned long csr; + unsigned long ahb_ptr; + unsigned long apb_ptr; + unsigned long ahb_seq; + unsigned long apb_seq; +}; + +/* + * tegra_dma_sg_req: Dma request details to configure hardware. This + * contains the details for one transfer to configure DMA hw. + * The client's request for data transfer can be broken into multiple + * sub-transfer as per requester details and hw support. + * This sub transfer get added in the list of transfer and point to Tegra + * DMA descriptor which manages the transfer details. + */ +struct tegra_dma_sg_req { + struct tegra_dma_channel_regs ch_regs; + int req_len; + bool configured; + bool last_sg; + bool half_done; + struct list_head node; + struct tegra_dma_desc *dma_desc; +}; + +/* + * tegra_dma_desc: Tegra DMA descriptors which manages the client requests. + * This descriptor keep track of transfer status, callbacks and request + * counts etc. + */ +struct tegra_dma_desc { + struct dma_async_tx_descriptor txd; + int bytes_requested; + int bytes_transferred; + enum dma_status dma_status; + struct list_head node; + struct list_head tx_list; + struct list_head cb_node; + int cb_count; +}; + +struct tegra_dma_channel; + +typedef void (*dma_isr_handler)(struct tegra_dma_channel *tdc, + bool to_terminate); + +/* tegra_dma_channel: Channel specific information */ +struct tegra_dma_channel { + struct dma_chan dma_chan; + bool config_init; + int id; + int irq; + unsigned long chan_base_offset; + spinlock_t lock; + bool busy; + struct tegra_dma *tdma; + bool cyclic; + + /* Different lists for managing the requests */ + struct list_head free_sg_req; + struct list_head pending_sg_req; + struct list_head free_dma_desc; + struct list_head cb_desc; + + /* ISR handler and tasklet for bottom half of isr handling */ + dma_isr_handler isr_handler; + struct tasklet_struct tasklet; + dma_async_tx_callback callback; + void *callback_param; + + /* Channel-slave specific configuration */ + struct dma_slave_config dma_sconfig; +}; + +/* tegra_dma: Tegra DMA specific information */ +struct tegra_dma { + struct dma_device dma_dev; + struct device *dev; + struct clk *dma_clk; + spinlock_t global_lock; + void __iomem *base_addr; + struct tegra_dma_chip_data *chip_data; + + /* Some register need to be cache before suspend */ + u32 reg_gen; + + /* Last member of the structure */ + struct tegra_dma_channel channels[0]; +}; + +static inline void tdma_write(struct tegra_dma *tdma, u32 reg, u32 val) +{ + writel(val, tdma->base_addr + reg); +} + +static inline u32 tdma_read(struct tegra_dma *tdma, u32 reg) +{ + return readl(tdma->base_addr + reg); +} + +static inline void tdc_write(struct tegra_dma_channel *tdc, + u32 reg, u32 val) +{ + writel(val, tdc->tdma->base_addr + tdc->chan_base_offset + reg); +} + +static inline u32 tdc_read(struct tegra_dma_channel *tdc, u32 reg) +{ + return readl(tdc->tdma->base_addr + tdc->chan_base_offset + reg); +} + +static inline struct tegra_dma_channel *to_tegra_dma_chan(struct dma_chan *dc) +{ + return container_of(dc, struct tegra_dma_channel, dma_chan); +} + +static inline struct tegra_dma_desc *txd_to_tegra_dma_desc( + struct dma_async_tx_descriptor *td) +{ + return container_of(td, struct tegra_dma_desc, txd); +} + +static inline struct device *tdc2dev(struct tegra_dma_channel *tdc) +{ + return &tdc->dma_chan.dev->device; +} + +static dma_cookie_t tegra_dma_tx_submit(struct dma_async_tx_descriptor *tx); +static int tegra_dma_runtime_suspend(struct device *dev); +static int tegra_dma_runtime_resume(struct device *dev); + +/* Get DMA desc from free list, if not there then allocate it. */ +static struct tegra_dma_desc *tegra_dma_desc_get( + struct tegra_dma_channel *tdc) +{ + struct tegra_dma_desc *dma_desc; + unsigned long flags; + + spin_lock_irqsave(&tdc->lock, flags); + + /* Do not allocate if desc are waiting for ack */ + list_for_each_entry(dma_desc, &tdc->free_dma_desc, node) { + if (async_tx_test_ack(&dma_desc->txd)) { + list_del(&dma_desc->node); + spin_unlock_irqrestore(&tdc->lock, flags); + return dma_desc; + } + } + + spin_unlock_irqrestore(&tdc->lock, flags); + + /* Allocate DMA desc */ + dma_desc = kzalloc(sizeof(*dma_desc), GFP_ATOMIC); + if (!dma_desc) { + dev_err(tdc2dev(tdc), "dma_desc alloc failed\n"); + return NULL; + } + + dma_async_tx_descriptor_init(&dma_desc->txd, &tdc->dma_chan); + dma_desc->txd.tx_submit = tegra_dma_tx_submit; + dma_desc->txd.flags = 0; + return dma_desc; +} + +static void tegra_dma_desc_put(struct tegra_dma_channel *tdc, + struct tegra_dma_desc *dma_desc) +{ + unsigned long flags; + + spin_lock_irqsave(&tdc->lock, flags); + if (!list_empty(&dma_desc->tx_list)) + list_splice_init(&dma_desc->tx_list, &tdc->free_sg_req); + list_add_tail(&dma_desc->node, &tdc->free_dma_desc); + spin_unlock_irqrestore(&tdc->lock, flags); +} + +static struct tegra_dma_sg_req *tegra_dma_sg_req_get( + struct tegra_dma_channel *tdc) +{ + struct tegra_dma_sg_req *sg_req = NULL; + unsigned long flags; + + spin_lock_irqsave(&tdc->lock, flags); + if (!list_empty(&tdc->free_sg_req)) { + sg_req = list_first_entry(&tdc->free_sg_req, + typeof(*sg_req), node); + list_del(&sg_req->node); + spin_unlock_irqrestore(&tdc->lock, flags); + return sg_req; + } + spin_unlock_irqrestore(&tdc->lock, flags); + + sg_req = kzalloc(sizeof(struct tegra_dma_sg_req), GFP_ATOMIC); + if (!sg_req) + dev_err(tdc2dev(tdc), "sg_req alloc failed\n"); + return sg_req; +} + +static int tegra_dma_slave_config(struct dma_chan *dc, + struct dma_slave_config *sconfig) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + + if (!list_empty(&tdc->pending_sg_req)) { + dev_err(tdc2dev(tdc), "Configuration not allowed\n"); + return -EBUSY; + } + + memcpy(&tdc->dma_sconfig, sconfig, sizeof(*sconfig)); + tdc->config_init = true; + return 0; +} + +static void tegra_dma_global_pause(struct tegra_dma_channel *tdc, + bool wait_for_burst_complete) +{ + struct tegra_dma *tdma = tdc->tdma; + + spin_lock(&tdma->global_lock); + tdma_write(tdma, TEGRA_APBDMA_GENERAL, 0); + if (wait_for_burst_complete) + udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME); +} + +static void tegra_dma_global_resume(struct tegra_dma_channel *tdc) +{ + struct tegra_dma *tdma = tdc->tdma; + + tdma_write(tdma, TEGRA_APBDMA_GENERAL, TEGRA_APBDMA_GENERAL_ENABLE); + spin_unlock(&tdma->global_lock); +} + +static void tegra_dma_stop(struct tegra_dma_channel *tdc) +{ + u32 csr; + u32 status; + + /* Disable interrupts */ + csr = tdc_read(tdc, TEGRA_APBDMA_CHAN_CSR); + csr &= ~TEGRA_APBDMA_CSR_IE_EOC; + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, csr); + + /* Disable DMA */ + csr &= ~TEGRA_APBDMA_CSR_ENB; + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, csr); + + /* Clear interrupt status if it is there */ + status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); + if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { + dev_dbg(tdc2dev(tdc), "%s():clearing interrupt\n", __func__); + tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status); + } + tdc->busy = false; +} + +static void tegra_dma_start(struct tegra_dma_channel *tdc, + struct tegra_dma_sg_req *sg_req) +{ + struct tegra_dma_channel_regs *ch_regs = &sg_req->ch_regs; + + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, ch_regs->csr); + tdc_write(tdc, TEGRA_APBDMA_CHAN_APBSEQ, ch_regs->apb_seq); + tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, ch_regs->apb_ptr); + tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBSEQ, ch_regs->ahb_seq); + tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBPTR, ch_regs->ahb_ptr); + + /* Start DMA */ + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, + ch_regs->csr | TEGRA_APBDMA_CSR_ENB); +} + +static void tegra_dma_configure_for_next(struct tegra_dma_channel *tdc, + struct tegra_dma_sg_req *nsg_req) +{ + unsigned long status; + + /* + * The DMA controller reloads the new configuration for next transfer + * after last burst of current transfer completes. + * If there is no IEC status then this makes sure that last burst + * has not be completed. There may be case that last burst is on + * flight and so it can complete but because DMA is paused, it + * will not generates interrupt as well as not reload the new + * configuration. + * If there is already IEC status then interrupt handler need to + * load new configuration. + */ + tegra_dma_global_pause(tdc, false); + status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); + + /* + * If interrupt is pending then do nothing as the ISR will handle + * the programing for new request. + */ + if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { + dev_err(tdc2dev(tdc), + "Skipping new configuration as interrupt is pending\n"); + tegra_dma_global_resume(tdc); + return; + } + + /* Safe to program new configuration */ + tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, nsg_req->ch_regs.apb_ptr); + tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBPTR, nsg_req->ch_regs.ahb_ptr); + tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, + nsg_req->ch_regs.csr | TEGRA_APBDMA_CSR_ENB); + nsg_req->configured = true; + + tegra_dma_global_resume(tdc); +} + +static void tdc_start_head_req(struct tegra_dma_channel *tdc) +{ + struct tegra_dma_sg_req *sg_req; + + if (list_empty(&tdc->pending_sg_req)) + return; + + sg_req = list_first_entry(&tdc->pending_sg_req, + typeof(*sg_req), node); + tegra_dma_start(tdc, sg_req); + sg_req->configured = true; + tdc->busy = true; +} + +static void tdc_configure_next_head_desc(struct tegra_dma_channel *tdc) +{ + struct tegra_dma_sg_req *hsgreq; + struct tegra_dma_sg_req *hnsgreq; + + if (list_empty(&tdc->pending_sg_req)) + return; + + hsgreq = list_first_entry(&tdc->pending_sg_req, typeof(*hsgreq), node); + if (!list_is_last(&hsgreq->node, &tdc->pending_sg_req)) { + hnsgreq = list_first_entry(&hsgreq->node, + typeof(*hnsgreq), node); + tegra_dma_configure_for_next(tdc, hnsgreq); + } +} + +static inline int get_current_xferred_count(struct tegra_dma_channel *tdc, + struct tegra_dma_sg_req *sg_req, unsigned long status) +{ + return sg_req->req_len - (status & TEGRA_APBDMA_STATUS_COUNT_MASK) - 4; +} + +static void tegra_dma_abort_all(struct tegra_dma_channel *tdc) +{ + struct tegra_dma_sg_req *sgreq; + struct tegra_dma_desc *dma_desc; + + while (!list_empty(&tdc->pending_sg_req)) { + sgreq = list_first_entry(&tdc->pending_sg_req, + typeof(*sgreq), node); + list_del(&sgreq->node); + list_add_tail(&sgreq->node, &tdc->free_sg_req); + if (sgreq->last_sg) { + dma_desc = sgreq->dma_desc; + dma_desc->dma_status = DMA_ERROR; + list_add_tail(&dma_desc->node, &tdc->free_dma_desc); + + /* Add in cb list if it is not there. */ + if (!dma_desc->cb_count) + list_add_tail(&dma_desc->cb_node, + &tdc->cb_desc); + dma_desc->cb_count++; + } + } + tdc->isr_handler = NULL; +} + +static bool handle_continuous_head_request(struct tegra_dma_channel *tdc, + struct tegra_dma_sg_req *last_sg_req, bool to_terminate) +{ + struct tegra_dma_sg_req *hsgreq = NULL; + + if (list_empty(&tdc->pending_sg_req)) { + dev_err(tdc2dev(tdc), "Dma is running without req\n"); + tegra_dma_stop(tdc); + return false; + } + + /* + * Check that head req on list should be in flight. + * If it is not in flight then abort transfer as + * looping of transfer can not continue. + */ + hsgreq = list_first_entry(&tdc->pending_sg_req, typeof(*hsgreq), node); + if (!hsgreq->configured) { + tegra_dma_stop(tdc); + dev_err(tdc2dev(tdc), "Error in dma transfer, aborting dma\n"); + tegra_dma_abort_all(tdc); + return false; + } + + /* Configure next request */ + if (!to_terminate) + tdc_configure_next_head_desc(tdc); + return true; +} + +static void handle_once_dma_done(struct tegra_dma_channel *tdc, + bool to_terminate) +{ + struct tegra_dma_sg_req *sgreq; + struct tegra_dma_desc *dma_desc; + + tdc->busy = false; + sgreq = list_first_entry(&tdc->pending_sg_req, typeof(*sgreq), node); + dma_desc = sgreq->dma_desc; + dma_desc->bytes_transferred += sgreq->req_len; + + list_del(&sgreq->node); + if (sgreq->last_sg) { + dma_desc->dma_status = DMA_SUCCESS; + dma_cookie_complete(&dma_desc->txd); + if (!dma_desc->cb_count) + list_add_tail(&dma_desc->cb_node, &tdc->cb_desc); + dma_desc->cb_count++; + list_add_tail(&dma_desc->node, &tdc->free_dma_desc); + } + list_add_tail(&sgreq->node, &tdc->free_sg_req); + + /* Do not start DMA if it is going to be terminate */ + if (to_terminate || list_empty(&tdc->pending_sg_req)) + return; + + tdc_start_head_req(tdc); + return; +} + +static void handle_cont_sngl_cycle_dma_done(struct tegra_dma_channel *tdc, + bool to_terminate) +{ + struct tegra_dma_sg_req *sgreq; + struct tegra_dma_desc *dma_desc; + bool st; + + sgreq = list_first_entry(&tdc->pending_sg_req, typeof(*sgreq), node); + dma_desc = sgreq->dma_desc; + dma_desc->bytes_transferred += sgreq->req_len; + + /* Callback need to be call */ + if (!dma_desc->cb_count) + list_add_tail(&dma_desc->cb_node, &tdc->cb_desc); + dma_desc->cb_count++; + + /* If not last req then put at end of pending list */ + if (!list_is_last(&sgreq->node, &tdc->pending_sg_req)) { + list_del(&sgreq->node); + list_add_tail(&sgreq->node, &tdc->pending_sg_req); + sgreq->configured = false; + st = handle_continuous_head_request(tdc, sgreq, to_terminate); + if (!st) + dma_desc->dma_status = DMA_ERROR; + } + return; +} + +static void tegra_dma_tasklet(unsigned long data) +{ + struct tegra_dma_channel *tdc = (struct tegra_dma_channel *)data; + dma_async_tx_callback callback = NULL; + void *callback_param = NULL; + struct tegra_dma_desc *dma_desc; + unsigned long flags; + int cb_count; + + spin_lock_irqsave(&tdc->lock, flags); + while (!list_empty(&tdc->cb_desc)) { + dma_desc = list_first_entry(&tdc->cb_desc, + typeof(*dma_desc), cb_node); + list_del(&dma_desc->cb_node); + callback = dma_desc->txd.callback; + callback_param = dma_desc->txd.callback_param; + cb_count = dma_desc->cb_count; + dma_desc->cb_count = 0; + spin_unlock_irqrestore(&tdc->lock, flags); + while (cb_count-- && callback) + callback(callback_param); + spin_lock_irqsave(&tdc->lock, flags); + } + spin_unlock_irqrestore(&tdc->lock, flags); +} + +static irqreturn_t tegra_dma_isr(int irq, void *dev_id) +{ + struct tegra_dma_channel *tdc = dev_id; + unsigned long status; + unsigned long flags; + + spin_lock_irqsave(&tdc->lock, flags); + + status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); + if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { + tdc_write(tdc, TEGRA_APBDMA_CHAN_STATUS, status); + tdc->isr_handler(tdc, false); + tasklet_schedule(&tdc->tasklet); + spin_unlock_irqrestore(&tdc->lock, flags); + return IRQ_HANDLED; + } + + spin_unlock_irqrestore(&tdc->lock, flags); + dev_info(tdc2dev(tdc), + "Interrupt already served status 0x%08lx\n", status); + return IRQ_NONE; +} + +static dma_cookie_t tegra_dma_tx_submit(struct dma_async_tx_descriptor *txd) +{ + struct tegra_dma_desc *dma_desc = txd_to_tegra_dma_desc(txd); + struct tegra_dma_channel *tdc = to_tegra_dma_chan(txd->chan); + unsigned long flags; + dma_cookie_t cookie; + + spin_lock_irqsave(&tdc->lock, flags); + dma_desc->dma_status = DMA_IN_PROGRESS; + cookie = dma_cookie_assign(&dma_desc->txd); + list_splice_tail_init(&dma_desc->tx_list, &tdc->pending_sg_req); + spin_unlock_irqrestore(&tdc->lock, flags); + return cookie; +} + +static void tegra_dma_issue_pending(struct dma_chan *dc) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + unsigned long flags; + + spin_lock_irqsave(&tdc->lock, flags); + if (list_empty(&tdc->pending_sg_req)) { + dev_err(tdc2dev(tdc), "No DMA request\n"); + goto end; + } + if (!tdc->busy) { + tdc_start_head_req(tdc); + + /* Continuous single mode: Configure next req */ + if (tdc->cyclic) { + /* + * Wait for 1 burst time for configure DMA for + * next transfer. + */ + udelay(TEGRA_APBDMA_BURST_COMPLETE_TIME); + tdc_configure_next_head_desc(tdc); + } + } +end: + spin_unlock_irqrestore(&tdc->lock, flags); + return; +} + +static void tegra_dma_terminate_all(struct dma_chan *dc) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + struct tegra_dma_sg_req *sgreq; + struct tegra_dma_desc *dma_desc; + unsigned long flags; + unsigned long status; + bool was_busy; + + spin_lock_irqsave(&tdc->lock, flags); + if (list_empty(&tdc->pending_sg_req)) { + spin_unlock_irqrestore(&tdc->lock, flags); + return; + } + + if (!tdc->busy) + goto skip_dma_stop; + + /* Pause DMA before checking the queue status */ + tegra_dma_global_pause(tdc, true); + + status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); + if (status & TEGRA_APBDMA_STATUS_ISE_EOC) { + dev_dbg(tdc2dev(tdc), "%s():handling isr\n", __func__); + tdc->isr_handler(tdc, true); + status = tdc_read(tdc, TEGRA_APBDMA_CHAN_STATUS); + } + + was_busy = tdc->busy; + tegra_dma_stop(tdc); + + if (!list_empty(&tdc->pending_sg_req) && was_busy) { + sgreq = list_first_entry(&tdc->pending_sg_req, + typeof(*sgreq), node); + sgreq->dma_desc->bytes_transferred += + get_current_xferred_count(tdc, sgreq, status); + } + tegra_dma_global_resume(tdc); + +skip_dma_stop: + tegra_dma_abort_all(tdc); + + while (!list_empty(&tdc->cb_desc)) { + dma_desc = list_first_entry(&tdc->cb_desc, + typeof(*dma_desc), cb_node); + list_del(&dma_desc->cb_node); + dma_desc->cb_count = 0; + } + spin_unlock_irqrestore(&tdc->lock, flags); +} + +static enum dma_status tegra_dma_tx_status(struct dma_chan *dc, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + struct tegra_dma_desc *dma_desc; + struct tegra_dma_sg_req *sg_req; + enum dma_status ret; + unsigned long flags; + + spin_lock_irqsave(&tdc->lock, flags); + + ret = dma_cookie_status(dc, cookie, txstate); + if (ret == DMA_SUCCESS) { + dma_set_residue(txstate, 0); + spin_unlock_irqrestore(&tdc->lock, flags); + return ret; + } + + /* Check on wait_ack desc status */ + list_for_each_entry(dma_desc, &tdc->free_dma_desc, node) { + if (dma_desc->txd.cookie == cookie) { + dma_set_residue(txstate, + dma_desc->bytes_requested - + dma_desc->bytes_transferred); + ret = dma_desc->dma_status; + spin_unlock_irqrestore(&tdc->lock, flags); + return ret; + } + } + + /* Check in pending list */ + list_for_each_entry(sg_req, &tdc->pending_sg_req, node) { + dma_desc = sg_req->dma_desc; + if (dma_desc->txd.cookie == cookie) { + dma_set_residue(txstate, + dma_desc->bytes_requested - + dma_desc->bytes_transferred); + ret = dma_desc->dma_status; + spin_unlock_irqrestore(&tdc->lock, flags); + return ret; + } + } + + dev_dbg(tdc2dev(tdc), "cookie %d does not found\n", cookie); + spin_unlock_irqrestore(&tdc->lock, flags); + return ret; +} + +static int tegra_dma_device_control(struct dma_chan *dc, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + switch (cmd) { + case DMA_SLAVE_CONFIG: + return tegra_dma_slave_config(dc, + (struct dma_slave_config *)arg); + + case DMA_TERMINATE_ALL: + tegra_dma_terminate_all(dc); + return 0; + + default: + break; + } + + return -ENXIO; +} + +static inline int get_bus_width(struct tegra_dma_channel *tdc, + enum dma_slave_buswidth slave_bw) +{ + switch (slave_bw) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + return TEGRA_APBDMA_APBSEQ_BUS_WIDTH_8; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + return TEGRA_APBDMA_APBSEQ_BUS_WIDTH_16; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return TEGRA_APBDMA_APBSEQ_BUS_WIDTH_32; + case DMA_SLAVE_BUSWIDTH_8_BYTES: + return TEGRA_APBDMA_APBSEQ_BUS_WIDTH_64; + default: + dev_warn(tdc2dev(tdc), + "slave bw is not supported, using 32bits\n"); + return TEGRA_APBDMA_APBSEQ_BUS_WIDTH_32; + } +} + +static inline int get_burst_size(struct tegra_dma_channel *tdc, + u32 burst_size, enum dma_slave_buswidth slave_bw, int len) +{ + int burst_byte; + int burst_ahb_width; + + /* + * burst_size from client is in terms of the bus_width. + * convert them into AHB memory width which is 4 byte. + */ + burst_byte = burst_size * slave_bw; + burst_ahb_width = burst_byte / 4; + + /* If burst size is 0 then calculate the burst size based on length */ + if (!burst_ahb_width) { + if (len & 0xF) + return TEGRA_APBDMA_AHBSEQ_BURST_1; + else if ((len >> 4) & 0x1) + return TEGRA_APBDMA_AHBSEQ_BURST_4; + else + return TEGRA_APBDMA_AHBSEQ_BURST_8; + } + if (burst_ahb_width < 4) + return TEGRA_APBDMA_AHBSEQ_BURST_1; + else if (burst_ahb_width < 8) + return TEGRA_APBDMA_AHBSEQ_BURST_4; + else + return TEGRA_APBDMA_AHBSEQ_BURST_8; +} + +static int get_transfer_param(struct tegra_dma_channel *tdc, + enum dma_transfer_direction direction, unsigned long *apb_addr, + unsigned long *apb_seq, unsigned long *csr, unsigned int *burst_size, + enum dma_slave_buswidth *slave_bw) +{ + + switch (direction) { + case DMA_MEM_TO_DEV: + *apb_addr = tdc->dma_sconfig.dst_addr; + *apb_seq = get_bus_width(tdc, tdc->dma_sconfig.dst_addr_width); + *burst_size = tdc->dma_sconfig.dst_maxburst; + *slave_bw = tdc->dma_sconfig.dst_addr_width; + *csr = TEGRA_APBDMA_CSR_DIR; + return 0; + + case DMA_DEV_TO_MEM: + *apb_addr = tdc->dma_sconfig.src_addr; + *apb_seq = get_bus_width(tdc, tdc->dma_sconfig.src_addr_width); + *burst_size = tdc->dma_sconfig.src_maxburst; + *slave_bw = tdc->dma_sconfig.src_addr_width; + *csr = 0; + return 0; + + default: + dev_err(tdc2dev(tdc), "Dma direction is not supported\n"); + return -EINVAL; + } + return -EINVAL; +} + +static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( + struct dma_chan *dc, struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags, + void *context) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + struct tegra_dma_desc *dma_desc; + unsigned int i; + struct scatterlist *sg; + unsigned long csr, ahb_seq, apb_ptr, apb_seq; + struct list_head req_list; + struct tegra_dma_sg_req *sg_req = NULL; + u32 burst_size; + enum dma_slave_buswidth slave_bw; + int ret; + + if (!tdc->config_init) { + dev_err(tdc2dev(tdc), "dma channel is not configured\n"); + return NULL; + } + if (sg_len < 1) { + dev_err(tdc2dev(tdc), "Invalid segment length %d\n", sg_len); + return NULL; + } + + ret = get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr, + &burst_size, &slave_bw); + if (ret < 0) + return NULL; + + INIT_LIST_HEAD(&req_list); + + ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB; + ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE << + TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT; + ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32; + + csr |= TEGRA_APBDMA_CSR_ONCE | TEGRA_APBDMA_CSR_FLOW; + csr |= tdc->dma_sconfig.slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT; + if (flags & DMA_PREP_INTERRUPT) + csr |= TEGRA_APBDMA_CSR_IE_EOC; + + apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1; + + dma_desc = tegra_dma_desc_get(tdc); + if (!dma_desc) { + dev_err(tdc2dev(tdc), "Dma descriptors not available\n"); + return NULL; + } + INIT_LIST_HEAD(&dma_desc->tx_list); + INIT_LIST_HEAD(&dma_desc->cb_node); + dma_desc->cb_count = 0; + dma_desc->bytes_requested = 0; + dma_desc->bytes_transferred = 0; + dma_desc->dma_status = DMA_IN_PROGRESS; + + /* Make transfer requests */ + for_each_sg(sgl, sg, sg_len, i) { + u32 len, mem; + + mem = sg_phys(sg); + len = sg_dma_len(sg); + + if ((len & 3) || (mem & 3) || + (len > tdc->tdma->chip_data->max_dma_count)) { + dev_err(tdc2dev(tdc), + "Dma length/memory address is not supported\n"); + tegra_dma_desc_put(tdc, dma_desc); + return NULL; + } + + sg_req = tegra_dma_sg_req_get(tdc); + if (!sg_req) { + dev_err(tdc2dev(tdc), "Dma sg-req not available\n"); + tegra_dma_desc_put(tdc, dma_desc); + return NULL; + } + + ahb_seq |= get_burst_size(tdc, burst_size, slave_bw, len); + dma_desc->bytes_requested += len; + + sg_req->ch_regs.apb_ptr = apb_ptr; + sg_req->ch_regs.ahb_ptr = mem; + sg_req->ch_regs.csr = csr | ((len - 4) & 0xFFFC); + sg_req->ch_regs.apb_seq = apb_seq; + sg_req->ch_regs.ahb_seq = ahb_seq; + sg_req->configured = false; + sg_req->last_sg = false; + sg_req->dma_desc = dma_desc; + sg_req->req_len = len; + + list_add_tail(&sg_req->node, &dma_desc->tx_list); + } + sg_req->last_sg = true; + if (flags & DMA_CTRL_ACK) + dma_desc->txd.flags = DMA_CTRL_ACK; + + /* + * Make sure that mode should not be conflicting with currently + * configured mode. + */ + if (!tdc->isr_handler) { + tdc->isr_handler = handle_once_dma_done; + tdc->cyclic = false; + } else { + if (tdc->cyclic) { + dev_err(tdc2dev(tdc), "DMA configured in cyclic mode\n"); + tegra_dma_desc_put(tdc, dma_desc); + return NULL; + } + } + + return &dma_desc->txd; +} + +struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( + struct dma_chan *dc, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + void *context) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + struct tegra_dma_desc *dma_desc = NULL; + struct tegra_dma_sg_req *sg_req = NULL; + unsigned long csr, ahb_seq, apb_ptr, apb_seq; + int len; + size_t remain_len; + dma_addr_t mem = buf_addr; + u32 burst_size; + enum dma_slave_buswidth slave_bw; + int ret; + + if (!buf_len || !period_len) { + dev_err(tdc2dev(tdc), "Invalid buffer/period len\n"); + return NULL; + } + + if (!tdc->config_init) { + dev_err(tdc2dev(tdc), "DMA slave is not configured\n"); + return NULL; + } + + /* + * We allow to take more number of requests till DMA is + * not started. The driver will loop over all requests. + * Once DMA is started then new requests can be queued only after + * terminating the DMA. + */ + if (tdc->busy) { + dev_err(tdc2dev(tdc), "Request not allowed when dma running\n"); + return NULL; + } + + /* + * We only support cycle transfer when buf_len is multiple of + * period_len. + */ + if (buf_len % period_len) { + dev_err(tdc2dev(tdc), "buf_len is not multiple of period_len\n"); + return NULL; + } + + len = period_len; + if ((len & 3) || (buf_addr & 3) || + (len > tdc->tdma->chip_data->max_dma_count)) { + dev_err(tdc2dev(tdc), "Req len/mem address is not correct\n"); + return NULL; + } + + ret = get_transfer_param(tdc, direction, &apb_ptr, &apb_seq, &csr, + &burst_size, &slave_bw); + if (ret < 0) + return NULL; + + + ahb_seq = TEGRA_APBDMA_AHBSEQ_INTR_ENB; + ahb_seq |= TEGRA_APBDMA_AHBSEQ_WRAP_NONE << + TEGRA_APBDMA_AHBSEQ_WRAP_SHIFT; + ahb_seq |= TEGRA_APBDMA_AHBSEQ_BUS_WIDTH_32; + + csr |= TEGRA_APBDMA_CSR_FLOW | TEGRA_APBDMA_CSR_IE_EOC; + csr |= tdc->dma_sconfig.slave_id << TEGRA_APBDMA_CSR_REQ_SEL_SHIFT; + + apb_seq |= TEGRA_APBDMA_APBSEQ_WRAP_WORD_1; + + dma_desc = tegra_dma_desc_get(tdc); + if (!dma_desc) { + dev_err(tdc2dev(tdc), "not enough descriptors available\n"); + return NULL; + } + + INIT_LIST_HEAD(&dma_desc->tx_list); + INIT_LIST_HEAD(&dma_desc->cb_node); + dma_desc->cb_count = 0; + + dma_desc->bytes_transferred = 0; + dma_desc->bytes_requested = buf_len; + remain_len = buf_len; + + /* Split transfer equal to period size */ + while (remain_len) { + sg_req = tegra_dma_sg_req_get(tdc); + if (!sg_req) { + dev_err(tdc2dev(tdc), "Dma sg-req not available\n"); + tegra_dma_desc_put(tdc, dma_desc); + return NULL; + } + + ahb_seq |= get_burst_size(tdc, burst_size, slave_bw, len); + sg_req->ch_regs.apb_ptr = apb_ptr; + sg_req->ch_regs.ahb_ptr = mem; + sg_req->ch_regs.csr = csr | ((len - 4) & 0xFFFC); + sg_req->ch_regs.apb_seq = apb_seq; + sg_req->ch_regs.ahb_seq = ahb_seq; + sg_req->configured = false; + sg_req->half_done = false; + sg_req->last_sg = false; + sg_req->dma_desc = dma_desc; + sg_req->req_len = len; + + list_add_tail(&sg_req->node, &dma_desc->tx_list); + remain_len -= len; + mem += len; + } + sg_req->last_sg = true; + dma_desc->txd.flags = DMA_CTRL_ACK; + + /* + * Make sure that mode should not be conflicting with currently + * configured mode. + */ + if (!tdc->isr_handler) { + tdc->isr_handler = handle_cont_sngl_cycle_dma_done; + tdc->cyclic = true; + } else { + if (!tdc->cyclic) { + dev_err(tdc2dev(tdc), "DMA configuration conflict\n"); + tegra_dma_desc_put(tdc, dma_desc); + return NULL; + } + } + + return &dma_desc->txd; +} + +static int tegra_dma_alloc_chan_resources(struct dma_chan *dc) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + + dma_cookie_init(&tdc->dma_chan); + tdc->config_init = false; + return 0; +} + +static void tegra_dma_free_chan_resources(struct dma_chan *dc) +{ + struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); + + struct tegra_dma_desc *dma_desc; + struct tegra_dma_sg_req *sg_req; + struct list_head dma_desc_list; + struct list_head sg_req_list; + unsigned long flags; + + INIT_LIST_HEAD(&dma_desc_list); + INIT_LIST_HEAD(&sg_req_list); + + dev_dbg(tdc2dev(tdc), "Freeing channel %d\n", tdc->id); + + if (tdc->busy) + tegra_dma_terminate_all(dc); + + spin_lock_irqsave(&tdc->lock, flags); + list_splice_init(&tdc->pending_sg_req, &sg_req_list); + list_splice_init(&tdc->free_sg_req, &sg_req_list); + list_splice_init(&tdc->free_dma_desc, &dma_desc_list); + INIT_LIST_HEAD(&tdc->cb_desc); + tdc->config_init = false; + spin_unlock_irqrestore(&tdc->lock, flags); + + while (!list_empty(&dma_desc_list)) { + dma_desc = list_first_entry(&dma_desc_list, + typeof(*dma_desc), node); + list_del(&dma_desc->node); + kfree(dma_desc); + } + + while (!list_empty(&sg_req_list)) { + sg_req = list_first_entry(&sg_req_list, typeof(*sg_req), node); + list_del(&sg_req->node); + kfree(sg_req); + } +} + +/* Tegra20 specific DMA controller information */ +static struct tegra_dma_chip_data tegra20_dma_chip_data = { + .nr_channels = 16, + .max_dma_count = 1024UL * 64, +}; + +#if defined(CONFIG_OF) +/* Tegra30 specific DMA controller information */ +static struct tegra_dma_chip_data tegra30_dma_chip_data = { + .nr_channels = 32, + .max_dma_count = 1024UL * 64, +}; + +static const struct of_device_id tegra_dma_of_match[] __devinitconst = { + { + .compatible = "nvidia,tegra30-apbdma-new", + .data = &tegra30_dma_chip_data, + }, { + .compatible = "nvidia,tegra20-apbdma-new", + .data = &tegra20_dma_chip_data, + }, { + }, +}; +MODULE_DEVICE_TABLE(of, tegra_dma_of_match); +#endif + +static int __devinit tegra_dma_probe(struct platform_device *pdev) +{ + struct resource *res; + struct tegra_dma *tdma; + int ret; + int i; + struct tegra_dma_chip_data *cdata = NULL; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_device(of_match_ptr(tegra_dma_of_match), + &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + } else { + /* If no device tree then fallback to tegra20 */ + cdata = &tegra20_dma_chip_data; + } + + tdma = devm_kzalloc(&pdev->dev, sizeof(*tdma) + cdata->nr_channels * + sizeof(struct tegra_dma_channel), GFP_KERNEL); + if (!tdma) { + dev_err(&pdev->dev, "Error: memory allocation failed\n"); + return -ENOMEM; + } + + tdma->dev = &pdev->dev; + tdma->chip_data = cdata; + platform_set_drvdata(pdev, tdma); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No mem resource for DMA\n"); + return -EINVAL; + } + + tdma->base_addr = devm_request_and_ioremap(&pdev->dev, res); + if (!tdma->base_addr) { + dev_err(&pdev->dev, + "Cannot request memregion/iomap dma address\n"); + return -EADDRNOTAVAIL; + } + + tdma->dma_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(tdma->dma_clk)) { + dev_err(&pdev->dev, "Error: Missing controller clock\n"); + return PTR_ERR(tdma->dma_clk); + } + + spin_lock_init(&tdma->global_lock); + + pm_runtime_enable(&pdev->dev); + if (!pm_runtime_enabled(&pdev->dev)) { + ret = tegra_dma_runtime_resume(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "dma_runtime_resume failed %d\n", + ret); + goto err_pm_disable; + } + } + + /* Reset DMA controller */ + tegra_periph_reset_assert(tdma->dma_clk); + udelay(2); + tegra_periph_reset_deassert(tdma->dma_clk); + + /* Enable global DMA registers */ + tdma_write(tdma, TEGRA_APBDMA_GENERAL, TEGRA_APBDMA_GENERAL_ENABLE); + tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0); + tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul); + + INIT_LIST_HEAD(&tdma->dma_dev.channels); + for (i = 0; i < cdata->nr_channels; i++) { + struct tegra_dma_channel *tdc = &tdma->channels[i]; + char irq_name[30]; + + tdc->chan_base_offset = TEGRA_APBDMA_CHANNEL_BASE_ADD_OFFSET + + i * TEGRA_APBDMA_CHANNEL_REGISTER_SIZE; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, i); + if (!res) { + ret = -EINVAL; + dev_err(&pdev->dev, "No irq resource for chan %d\n", i); + goto err_irq; + } + tdc->irq = res->start; + snprintf(irq_name, sizeof(irq_name), "apbdma.%d", i); + ret = devm_request_irq(&pdev->dev, tdc->irq, + tegra_dma_isr, 0, irq_name, tdc); + if (ret) { + dev_err(&pdev->dev, + "request_irq failed with err %d channel %d\n", + i, ret); + goto err_irq; + } + + tdc->dma_chan.device = &tdma->dma_dev; + dma_cookie_init(&tdc->dma_chan); + list_add_tail(&tdc->dma_chan.device_node, + &tdma->dma_dev.channels); + tdc->tdma = tdma; + tdc->id = i; + + tasklet_init(&tdc->tasklet, tegra_dma_tasklet, + (unsigned long)tdc); + spin_lock_init(&tdc->lock); + + INIT_LIST_HEAD(&tdc->pending_sg_req); + INIT_LIST_HEAD(&tdc->free_sg_req); + INIT_LIST_HEAD(&tdc->free_dma_desc); + INIT_LIST_HEAD(&tdc->cb_desc); + } + + dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); + dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); + tdma->dma_dev.dev = &pdev->dev; + tdma->dma_dev.device_alloc_chan_resources = + tegra_dma_alloc_chan_resources; + tdma->dma_dev.device_free_chan_resources = + tegra_dma_free_chan_resources; + tdma->dma_dev.device_prep_slave_sg = tegra_dma_prep_slave_sg; + tdma->dma_dev.device_prep_dma_cyclic = tegra_dma_prep_dma_cyclic; + tdma->dma_dev.device_control = tegra_dma_device_control; + tdma->dma_dev.device_tx_status = tegra_dma_tx_status; + tdma->dma_dev.device_issue_pending = tegra_dma_issue_pending; + + ret = dma_async_device_register(&tdma->dma_dev); + if (ret < 0) { + dev_err(&pdev->dev, + "Tegra20 APB DMA driver registration failed %d\n", ret); + goto err_irq; + } + + dev_info(&pdev->dev, "Tegra20 APB DMA driver register %d channels\n", + cdata->nr_channels); + return 0; + +err_irq: + while (--i >= 0) { + struct tegra_dma_channel *tdc = &tdma->channels[i]; + tasklet_kill(&tdc->tasklet); + } + +err_pm_disable: + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_dma_runtime_suspend(&pdev->dev); + return ret; +} + +static int __devexit tegra_dma_remove(struct platform_device *pdev) +{ + struct tegra_dma *tdma = platform_get_drvdata(pdev); + int i; + struct tegra_dma_channel *tdc; + + dma_async_device_unregister(&tdma->dma_dev); + + for (i = 0; i < tdma->chip_data->nr_channels; ++i) { + tdc = &tdma->channels[i]; + tasklet_kill(&tdc->tasklet); + } + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_dma_runtime_suspend(&pdev->dev); + + return 0; +} + +static int tegra_dma_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_dma *tdma = platform_get_drvdata(pdev); + + clk_disable(tdma->dma_clk); + return 0; +} + +static int tegra_dma_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_dma *tdma = platform_get_drvdata(pdev); + int ret; + + ret = clk_enable(tdma->dma_clk); + if (ret < 0) { + dev_err(dev, "clk_enable failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct dev_pm_ops tegra_dma_dev_pm_ops __devinitconst = { +#ifdef CONFIG_PM_RUNTIME + .runtime_suspend = tegra_dma_runtime_suspend, + .runtime_resume = tegra_dma_runtime_resume, +#endif +}; + +static struct platform_driver tegra_dmac_driver = { + .driver = { + .name = "tegra20-apbdma", + .owner = THIS_MODULE, + .pm = &tegra_dma_dev_pm_ops, + .of_match_table = of_match_ptr(tegra_dma_of_match), + }, + .probe = tegra_dma_probe, + .remove = __devexit_p(tegra_dma_remove), +}; + +module_platform_driver(tegra_dmac_driver); + +MODULE_ALIAS("platform:tegra20-apbdma"); +MODULE_DESCRIPTION("NVIDIA Tegra APB DMA Controller driver"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bc8e261233cf0f1ad838f60bac782df3a5bc6bac Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 7 Jun 2012 23:39:20 +0300 Subject: wlcore: add support macros to easily add conf debugfs entries The current debugfs code contains too much code duplication of bolierplate code. Add some macro magic to avoid this and enable adding new debugfs entries by using just a few lines. Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 75 ++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index fcd60636e9d1..50300ff0acb4 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -102,6 +102,81 @@ static const struct file_operations tx_queue_len_ops = { .llseek = default_llseek, }; +static void chip_op_handler(struct wl1271 *wl, unsigned long value, + void *arg) +{ + int ret; + int (*chip_op) (struct wl1271 *wl); + + if (!arg) { + wl1271_warning("debugfs chip_op_handler with no callback"); + return; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + return; + + chip_op = arg; + chip_op(wl); + + wl1271_ps_elp_sleep(wl); +} + + +static inline void no_write_handler(struct wl1271 *wl, + unsigned long value, + unsigned long param) +{ +} + +#define WL12XX_CONF_DEBUGFS(param, conf_sub_struct, \ + min_val, max_val, write_handler_locked, \ + write_handler_arg) \ + static ssize_t param##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ + { \ + struct wl1271 *wl = file->private_data; \ + return wl1271_format_buffer(user_buf, count, \ + ppos, "%d\n", \ + wl->conf.conf_sub_struct.param); \ + } \ + \ + static ssize_t param##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ + { \ + struct wl1271 *wl = file->private_data; \ + unsigned long value; \ + int ret; \ + \ + ret = kstrtoul_from_user(user_buf, count, 10, &value); \ + if (ret < 0) { \ + wl1271_warning("illegal value for " #param); \ + return -EINVAL; \ + } \ + \ + if (value < min_val || value > max_val) { \ + wl1271_warning(#param " is not in valid range"); \ + return -ERANGE; \ + } \ + \ + mutex_lock(&wl->mutex); \ + wl->conf.conf_sub_struct.param = value; \ + \ + write_handler_locked(wl, value, write_handler_arg); \ + \ + mutex_unlock(&wl->mutex); \ + return count; \ + } \ + \ + static const struct file_operations param##_ops = { \ + .read = param##_read, \ + .write = param##_write, \ + .open = simple_open, \ + .llseek = default_llseek, \ + }; static ssize_t gpio_power_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { -- cgit v1.2.3 From 3e8d69352bd9ac7287ab735d45582d79c1f9f2d9 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 7 Jun 2012 23:39:21 +0300 Subject: wlcore: add debugfs control over rx interrupt pacing Add control over several conf fields which combined control the rx interrupt pacing mechanism, that is avoiding getting an interrupt following a single frame rx but instead have the FW trigger the interrupt only after a certain amount of frames received or a timeout. Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 50300ff0acb4..827a728fd663 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -177,6 +177,14 @@ static inline void no_write_handler(struct wl1271 *wl, .open = simple_open, \ .llseek = default_llseek, \ }; + +WL12XX_CONF_DEBUGFS(irq_pkt_threshold, rx, 0, 65535, + chip_op_handler, wl1271_acx_init_rx_interrupt) +WL12XX_CONF_DEBUGFS(irq_blk_threshold, rx, 0, 65535, + chip_op_handler, wl1271_acx_init_rx_interrupt) +WL12XX_CONF_DEBUGFS(irq_timeout, rx, 0, 100, + chip_op_handler, wl1271_acx_init_rx_interrupt) + static ssize_t gpio_power_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -957,6 +965,9 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(dynamic_ps_timeout, rootdir); DEBUGFS_ADD(forced_ps, rootdir); DEBUGFS_ADD(split_scan_timeout, rootdir); + DEBUGFS_ADD(irq_pkt_threshold, rootdir); + DEBUGFS_ADD(irq_blk_threshold, rootdir); + DEBUGFS_ADD(irq_timeout, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) -- cgit v1.2.3 From f74ea74b82cf6b6aeef1e46d68fbbb02d2bce8b7 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:22 +0300 Subject: wl18xx: add support to clear FW statistics This patch calls ACX_CLEAR_STATISTICS to clear the firmware statistics. The trigger is a new debugfs file called clear_fw_statistics in the fw_stats directory. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/acx.c | 24 ++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/acx.h | 9 +++++++++ drivers/net/wireless/ti/wl18xx/debugfs.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index 01ba40321435..72840e23bf59 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -85,3 +85,27 @@ out: kfree(acx); return ret; } + +int wl18xx_acx_clear_statistics(struct wl1271 *wl) +{ + struct wl18xx_acx_clear_statistics *acx; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx clear statistics"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + ret = wl1271_cmd_configure(wl, ACX_CLEAR_STATISTICS, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to clear firmware statistics: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 7d74b031f114..3e5cd9fddcf1 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -25,6 +25,10 @@ #include "../wlcore/wlcore.h" #include "../wlcore/acx.h" +enum { + ACX_CLEAR_STATISTICS = 0x0047, +}; + /* numbers of bits the length field takes (add 1 for the actual number) */ #define WL18XX_HOST_IF_LEN_SIZE_FIELD 15 @@ -253,9 +257,14 @@ struct wl18xx_acx_statistics { struct wl18xx_acx_mem_stats mem; } __packed; +struct wl18xx_acx_clear_statistics { + struct acx_header header; +}; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); int wl18xx_acx_set_checksum_state(struct wl1271 *wl); +int wl18xx_acx_clear_statistics(struct wl1271 *wl); #endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 93c625b23a2f..468651c2f54c 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -158,6 +158,34 @@ WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u"); +static ssize_t clear_fw_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + int ret; + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl18xx_acx_clear_statistics(wl); + if (ret < 0) { + count = ret; + goto out; + } +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations clear_fw_stats_ops = { + .write = clear_fw_stats_write, + .open = simple_open, + .llseek = default_llseek, +}; + int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -176,6 +204,8 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, goto err; } + DEBUGFS_ADD(clear_fw_stats, stats); + DEBUGFS_FWSTATS_ADD(debug, debug1); DEBUGFS_FWSTATS_ADD(debug, debug2); DEBUGFS_FWSTATS_ADD(debug, debug3); -- cgit v1.2.3 From 9c6ead570c6b88b865fca15bdc0d09e2d90d2130 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:23 +0300 Subject: wlcore: export raw binary with the FW statistics in debugfs Instead of parsing all the binary data returned by the firmware, we should simply export the binary and let the userspace do the parsing. This commit adds a new file to debugfs to do that. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 827a728fd663..689a847005c9 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -944,6 +944,25 @@ static const struct file_operations beacon_filtering_ops = { .llseek = default_llseek, }; +static ssize_t fw_stats_raw_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + wl1271_debugfs_update_stats(wl); + + return simple_read_from_buffer(userbuf, count, ppos, + wl->stats.fw_stats, + wl->stats.fw_stats_len); +} + +static const struct file_operations fw_stats_raw_ops = { + .read = fw_stats_raw_read, + .open = simple_open, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -968,6 +987,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(irq_pkt_threshold, rootdir); DEBUGFS_ADD(irq_blk_threshold, rootdir); DEBUGFS_ADD(irq_timeout, rootdir); + DEBUGFS_ADD(fw_stats_raw, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) -- cgit v1.2.3 From 648f6ed9f7f0e4d56d65266734e748a02f8e2df7 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:24 +0300 Subject: wlcore/wl18xx/wl12xx: use u8 instead of bool for host_fast_wakeup_support The conf structure is going to be exported to a file, so we should use only well defined types. bool is not well defined and may vary from platform to platform, so change the host_fast_wakeup_support type to u8 instead. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 2 +- drivers/net/wireless/ti/wl18xx/main.c | 2 +- drivers/net/wireless/ti/wlcore/conf.h | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 8338045d226f..85d1600ee340 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -253,7 +253,7 @@ static struct wlcore_conf wl12xx_conf = { }, .pm_config = { .host_clk_settling_time = 5000, - .host_fast_wakeup_support = false + .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE, }, .roam_trigger = { .trigger_pacing = 1, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index fd02795f830c..b6a80cd5e9ef 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -376,7 +376,7 @@ static struct wlcore_conf wl18xx_conf = { }, .pm_config = { .host_clk_settling_time = 5000, - .host_fast_wakeup_support = false + .host_fast_wakeup_support = CONF_FAST_WAKEUP_DISABLE, }, .roam_trigger = { .trigger_pacing = 1, diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index d2c4885d0d77..2e84ae113fce 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -981,6 +981,11 @@ struct conf_itrim_settings { u32 timeout; }; +enum conf_fast_wakeup { + CONF_FAST_WAKEUP_ENABLE, + CONF_FAST_WAKEUP_DISABLE, +}; + struct conf_pm_config_settings { /* * Host clock settling time @@ -992,9 +997,9 @@ struct conf_pm_config_settings { /* * Host fast wakeup support * - * Range: true, false + * Range: enum conf_fast_wakeup */ - bool host_fast_wakeup_support; + u8 host_fast_wakeup_support; }; struct conf_roam_trigger_settings { -- cgit v1.2.3 From b551a3c9eb625326b11154a604fce2108f70fb96 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:25 +0300 Subject: wlcore: use u8 instead of enum for bcn_filt_mode Since we will export the conf structure as a file, we need to use well defined types. Instead of using enum, whose size may vary, use u8 for bcn_filt_mode instead. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 2e84ae113fce..0950bd22f4d9 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -824,7 +824,7 @@ struct conf_conn_settings { * * Range: CONF_BCN_FILT_MODE_* */ - enum conf_bcn_filt_mode bcn_filt_mode; + u8 bcn_filt_mode; /* * Configure Beacon filter pass-thru rules. -- cgit v1.2.3 From 34bacf73c62cd71542ee12eba5896e1f360faa9b Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:26 +0300 Subject: wlcore/wl18xx: the conf structs must be packed so they can be exported Since we are now going to export the conf structure and read it from a file, it should be packed to avoid surprises with padding bytes. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/conf.h | 4 ++-- drivers/net/wireless/ti/wlcore/conf.h | 44 +++++++++++++++++------------------ 2 files changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index 4e0f189b2539..b75a6d359712 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -48,10 +48,10 @@ struct wl18xx_conf_phy { s8 low_power_val; s8 med_power_val; s8 high_power_val; -}; +} __packed; struct wl18xx_priv_conf { struct wl18xx_conf_phy phy; -}; +} __packed; #endif /* __WL18XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 0950bd22f4d9..27d919fa997f 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -318,7 +318,7 @@ enum { struct conf_sg_settings { u32 params[CONF_SG_PARAMS_MAX]; u8 state; -}; +} __packed; enum conf_rx_queue_type { CONF_RX_QUEUE_TYPE_LOW_PRIORITY, /* All except the high priority */ @@ -402,7 +402,7 @@ struct conf_rx_settings { * Range: RX_QUEUE_TYPE_RX_LOW_PRIORITY, RX_QUEUE_TYPE_RX_HIGH_PRIORITY, */ u8 queue_type; -}; +} __packed; #define CONF_TX_MAX_RATE_CLASSES 10 @@ -501,7 +501,7 @@ struct conf_tx_rate_class { * the policy (0 - long preamble, 1 - short preamble. */ u8 aflags; -}; +} __packed; #define CONF_TX_MAX_AC_COUNT 4 @@ -558,7 +558,7 @@ struct conf_tx_ac_category { * Range: u16 */ u16 tx_op_limit; -}; +} __packed; #define CONF_TX_MAX_TID_COUNT 8 @@ -592,7 +592,7 @@ struct conf_tx_tid { u8 ps_scheme; u8 ack_policy; u32 apsd_conf[2]; -}; +} __packed; struct conf_tx_settings { /* @@ -678,7 +678,7 @@ struct conf_tx_settings { /* Time in ms for Tx watchdog timer to expire */ u32 tx_watchdog_timeout; -}; +} __packed; enum { CONF_WAKE_UP_EVENT_BEACON = 0x01, /* Wake on every Beacon*/ @@ -725,7 +725,7 @@ struct conf_bcn_filt_rule { * Version for the vendor specifie IE (221) */ u8 version[CONF_BCN_IE_VER_LEN]; -}; +} __packed; #define CONF_MAX_RSSI_SNR_TRIGGERS 8 @@ -776,7 +776,7 @@ struct conf_sig_weights { * Range: u8 */ u8 snr_pkt_avg_weight; -}; +} __packed; enum conf_bcn_filt_mode { CONF_BCN_FILT_MODE_DISABLED = 0, @@ -951,7 +951,7 @@ struct conf_conn_settings { * Range: u16 */ u8 max_listen_interval; -}; +} __packed; enum { CONF_REF_CLK_19_2_E, @@ -979,7 +979,7 @@ struct conf_itrim_settings { /* moderation timeout in microsecs from the last TX */ u32 timeout; -}; +} __packed; enum conf_fast_wakeup { CONF_FAST_WAKEUP_ENABLE, @@ -1000,7 +1000,7 @@ struct conf_pm_config_settings { * Range: enum conf_fast_wakeup */ u8 host_fast_wakeup_support; -}; +} __packed; struct conf_roam_trigger_settings { /* @@ -1037,7 +1037,7 @@ struct conf_roam_trigger_settings { * Range: 0 - 255 */ u8 avg_weight_snr_data; -}; +} __packed; struct conf_scan_settings { /* @@ -1083,7 +1083,7 @@ struct conf_scan_settings { * Range: u32 Microsecs */ u32 split_scan_timeout; -}; +} __packed; struct conf_sched_scan_settings { /* @@ -1121,7 +1121,7 @@ struct conf_sched_scan_settings { /* SNR threshold to be used for filtering */ s8 snr_threshold; -}; +} __packed; struct conf_ht_setting { u8 rx_ba_win_size; @@ -1130,7 +1130,7 @@ struct conf_ht_setting { /* bitmap of enabled TIDs for TX BA sessions */ u8 tx_ba_tid_bitmap; -}; +} __packed; struct conf_memory_settings { /* Number of stations supported in IBSS mode */ @@ -1170,7 +1170,7 @@ struct conf_memory_settings { * Range: 0-120 */ u8 tx_min; -}; +} __packed; struct conf_fm_coex { u8 enable; @@ -1183,7 +1183,7 @@ struct conf_fm_coex { u16 ldo_stabilization_time; u8 fm_disturbed_band_margin; u8 swallow_clk_diff; -}; +} __packed; struct conf_rx_streaming_settings { /* @@ -1212,7 +1212,7 @@ struct conf_rx_streaming_settings { * enable rx streaming also when there is no coex activity */ u8 always; -}; +} __packed; struct conf_fwlog { /* Continuous or on-demand */ @@ -1236,7 +1236,7 @@ struct conf_fwlog { /* Regulates the frequency of log messages */ u8 threshold; -}; +} __packed; #define ACX_RATE_MGMT_NUM_OF_RATES 13 struct conf_rate_policy_settings { @@ -1255,7 +1255,7 @@ struct conf_rate_policy_settings { u8 rate_check_up; u8 rate_check_down; u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES]; -}; +} __packed; struct conf_hangover_settings { u32 recover_time; @@ -1269,7 +1269,7 @@ struct conf_hangover_settings { u8 quiet_time; u8 increase_time; u8 window_size; -}; +} __packed; struct wlcore_conf { struct conf_sg_settings sg; @@ -1288,6 +1288,6 @@ struct wlcore_conf { struct conf_fwlog fwlog; struct conf_rate_policy_settings rate; struct conf_hangover_settings hangover; -}; +} __packed; #endif -- cgit v1.2.3 From 18b70ac9c7fd640cbd5921b5ca2705db0f8b9d83 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:27 +0300 Subject: wlcore/wl18xx: export conf struct in a debugfs file Add conf file header structure, magic and version values and export the entire conf struct in debugfs. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/conf.h | 6 +++++ drivers/net/wireless/ti/wl18xx/debugfs.c | 44 ++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/conf.h | 22 ++++++++++++++++ 3 files changed, 72 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index b75a6d359712..130546aa970d 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -22,6 +22,12 @@ #ifndef __WL18XX_CONF_H__ #define __WL18XX_CONF_H__ +#define WL18XX_CONF_MAGIC 0x10e100ca +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0001) +#define WL18XX_CONF_MASK 0x0000ffff +#define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ + sizeof(struct wl18xx_priv_conf)) + struct wl18xx_conf_phy { u8 phy_standalone; u8 rdl; diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 468651c2f54c..96b149662906 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -158,6 +158,48 @@ WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fw_gen_free_mem_blks, "%u"); +static ssize_t conf_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + struct wl18xx_priv *priv = wl->priv; + struct wlcore_conf_header header; + char *buf, *pos; + size_t len; + int ret; + + len = WL18XX_CONF_SIZE; + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + header.magic = cpu_to_le32(WL18XX_CONF_MAGIC); + header.version = cpu_to_le32(WL18XX_CONF_VERSION); + header.checksum = 0; + + mutex_lock(&wl->mutex); + + pos = buf; + memcpy(pos, &header, sizeof(header)); + pos += sizeof(header); + memcpy(pos, &wl->conf, sizeof(wl->conf)); + pos += sizeof(wl->conf); + memcpy(pos, &priv->conf, sizeof(priv->conf)); + + mutex_unlock(&wl->mutex); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + return ret; +} + +static const struct file_operations conf_ops = { + .read = conf_read, + .open = simple_open, + .llseek = default_llseek, +}; + static ssize_t clear_fw_stats_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -327,6 +369,8 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(mem, fwlog_free_mem_blks); DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks); + DEBUGFS_ADD(conf, moddir); + return 0; err: diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 27d919fa997f..03c635872335 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -1271,6 +1271,22 @@ struct conf_hangover_settings { u8 window_size; } __packed; +/* + * The conf version consists of 4 bytes. The two MSB are the wlcore + * version, the two LSB are the lower driver's private conf + * version. + */ +#define WLCORE_CONF_VERSION (0x0001 << 16) +#define WLCORE_CONF_MASK 0xffff0000 +#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ + sizeof(struct wlcore_conf)) + +struct wlcore_conf_header { + __le32 magic; + __le32 version; + __le32 checksum; +} __packed; + struct wlcore_conf { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1290,4 +1306,10 @@ struct wlcore_conf { struct conf_hangover_settings hangover; } __packed; +struct wlcore_conf_file { + struct wlcore_conf_header header; + struct wlcore_conf core; + u8 priv[0]; +} __packed; + #endif -- cgit v1.2.3 From 640dfb9b85bc80c607c07531dc5428418ef39974 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 7 Jun 2012 23:39:28 +0300 Subject: wl18xx: read configuration structure from a binary file Instead of using the hardcoded configuration structure, try to read it from a "firmware" file called wl18xx-conf.bin. If the file doesn't exist, fall back to the hardcoded version. If the file exists but is illegal, bail out. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 68 ++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index b6a80cd5e9ef..9cdf24f70964 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "../wlcore/wlcore.h" #include "../wlcore/debug.h" @@ -1046,15 +1047,65 @@ static s8 wl18xx_get_pg_ver(struct wl1271 *wl) return (s8)fuse; } -static void wl18xx_conf_init(struct wl1271 *wl) +#define WL18XX_CONF_FILE_NAME "ti-connectivity/wl18xx-conf.bin" +static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev) { struct wl18xx_priv *priv = wl->priv; + struct wlcore_conf_file *conf_file; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, WL18XX_CONF_FILE_NAME, dev); + if (ret < 0) { + wl1271_error("could not get configuration binary %s: %d", + WL18XX_CONF_FILE_NAME, ret); + goto out_fallback; + } + + if (fw->size != WL18XX_CONF_SIZE) { + wl1271_error("configuration binary file size is wrong, " + "expected %d got %d", WL18XX_CONF_SIZE, fw->size); + ret = -EINVAL; + goto out; + } + + conf_file = (struct wlcore_conf_file *) fw->data; + + if (conf_file->header.magic != cpu_to_le32(WL18XX_CONF_MAGIC)) { + wl1271_error("configuration binary file magic number mismatch, " + "expected 0x%0x got 0x%0x", WL18XX_CONF_MAGIC, + conf_file->header.magic); + ret = -EINVAL; + goto out; + } + + if (conf_file->header.version != cpu_to_le32(WL18XX_CONF_VERSION)) { + wl1271_error("configuration binary file version not supported, " + "expected 0x%08x got 0x%08x", + WL18XX_CONF_VERSION, conf_file->header.version); + ret = -EINVAL; + goto out; + } + + memcpy(&wl->conf, &conf_file->core, sizeof(wl18xx_conf)); + memcpy(&priv->conf, &conf_file->priv, sizeof(priv->conf)); + + goto out; + +out_fallback: + wl1271_warning("falling back to default config"); /* apply driver default configuration */ memcpy(&wl->conf, &wl18xx_conf, sizeof(wl18xx_conf)); - /* apply default private configuration */ memcpy(&priv->conf, &wl18xx_default_priv_conf, sizeof(priv->conf)); + + /* For now we just fallback */ + return 0; + +out: + release_firmware(fw); + return ret; } static int wl18xx_plt_init(struct wl1271 *wl) @@ -1261,11 +1312,13 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) struct wl1271 *wl; struct ieee80211_hw *hw; struct wl18xx_priv *priv; + int ret; hw = wlcore_alloc_hw(sizeof(*priv)); if (IS_ERR(hw)) { wl1271_error("can't allocate hw"); - return PTR_ERR(hw); + ret = PTR_ERR(hw); + goto out; } wl = hw->priv; @@ -1305,10 +1358,13 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) sizeof(wl18xx_siso20_ht_cap)); } else { wl1271_error("invalid ht_mode '%s'", ht_mode_param); + ret = -EINVAL; goto out_free; } - wl18xx_conf_init(wl); + ret = wl18xx_conf_init(wl, &pdev->dev); + if (ret < 0) + goto out_free; if (!strcmp(board_type_param, "fpga")) { priv->board_type = BOARD_TYPE_FPGA_18XX; @@ -1326,6 +1382,7 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) priv->conf.phy.low_band_component_type = 0x06; } else { wl1271_error("invalid board type '%s'", board_type_param); + ret = -EINVAL; goto out_free; } @@ -1373,7 +1430,8 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) out_free: wlcore_free_hw(wl); - return -EINVAL; +out: + return ret; } static const struct platform_device_id wl18xx_id_table[] __devinitconst = { -- cgit v1.2.3 From e27454b01304188205190238791b31efbfecfc04 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 23 May 2012 08:39:43 +0300 Subject: wl18xx: allow FW-log by default for PG2.0 This is supported by new FW versions (.88+). Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 9cdf24f70964..c26015b8830c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -600,7 +600,6 @@ static int wl18xx_identify_chip(struct wl1271 *wl) /* wl18xx uses the same firmware for PLT */ wl->plt_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | - WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_TX_PAD_LAST_FRAME; -- cgit v1.2.3 From 8dd8e53c6f0a5116b988445484b1d68d8e22ced9 Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Tue, 22 May 2012 12:34:10 +0300 Subject: wl18xx: update fw statistics Aligned to the struct in FW 8.2.0.0.91 and updated the debugfs entries accordingly. Signed-off-by: Ido Reis Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/acx.h | 33 +++++++++++++++---- drivers/net/wireless/ti/wl18xx/debugfs.c | 56 ++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 3e5cd9fddcf1..ebbaf611e97b 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -85,6 +85,15 @@ struct wl18xx_acx_checksum_state { u8 pad[3]; } __packed; + +struct wl18xx_acx_error_stats { + u32 error_frame; + u32 error_null_Frame_tx_start; + u32 error_numll_frame_cts_start; + u32 error_bar_retry; + u32 error_frame_cts_nul_flid; +} __packed; + struct wl18xx_acx_debug_stats { u32 debug1; u32 debug2; @@ -100,6 +109,8 @@ struct wl18xx_acx_ring_stats { } __packed; struct wl18xx_acx_tx_stats { + u32 tx_prepared_descs; + u32 tx_cmplt; u32 tx_template_prepared; u32 tx_data_prepared; u32 tx_template_programmed; @@ -136,7 +147,8 @@ struct wl18xx_acx_tx_stats { } __packed; struct wl18xx_acx_rx_stats { - u32 rx_out_of_mem; + u32 rx_beacon_early_term; + u32 rx_out_of_mpdu_nodes; u32 rx_hdr_overflow; u32 rx_dropped_frame; u32 rx_done_stage; @@ -148,6 +160,9 @@ struct wl18xx_acx_rx_stats { u32 rx_cmplt_task; u32 rx_phy_hdr; u32 rx_timeout; + u32 rx_timeout_wa; + u32 rx_wa_density_dropped_frame; + u32 rx_wa_ba_not_expected; u32 rx_frame_checksum; u32 rx_checksum_result; u32 defrag_called; @@ -158,6 +173,7 @@ struct wl18xx_acx_rx_stats { u32 defrag_decrypt_failed; u32 decrypt_key_not_found; u32 defrag_need_decrypt; + u32 rx_tkip_replays; } __packed; struct wl18xx_acx_isr_stats { @@ -197,20 +213,25 @@ struct wl18xx_acx_rx_filter_stats { u32 data_filter; u32 ibss_filter; u32 protection_filter; + u32 accum_arp_pend_requests; + u32 max_arp_queue_dep; } __packed; struct wl18xx_acx_rx_rate_stats { u32 rx_frames_per_rates[50]; } __packed; -#define AGGR_STATS_TX_SIZE_LEN 11 -#define AGGR_STATS_RX_SIZE_LEN 11 +#define AGGR_STATS_TX_AGG 16 +#define AGGR_STATS_TX_RATE 16 +#define AGGR_STATS_RX_SIZE_LEN 16 struct wl18xx_acx_aggr_stats { - u32 tx_size[AGGR_STATS_TX_SIZE_LEN]; + u32 tx_agg_vs_rate[AGGR_STATS_TX_AGG * AGGR_STATS_TX_RATE]; u32 rx_size[AGGR_STATS_RX_SIZE_LEN]; } __packed; +#define PIPE_STATS_HW_FIFO 11 + struct wl18xx_acx_pipeline_stats { u32 hs_tx_stat_fifo_int; u32 hs_rx_stat_fifo_int; @@ -230,6 +251,7 @@ struct wl18xx_acx_pipeline_stats { u32 dec_packet_out; u32 cs_rx_packet_in; u32 cs_rx_packet_out; + u16 pipeline_fifo_full[PIPE_STATS_HW_FIFO]; } __packed; struct wl18xx_acx_mem_stats { @@ -242,13 +264,12 @@ struct wl18xx_acx_mem_stats { struct wl18xx_acx_statistics { struct acx_header header; - struct wl18xx_acx_ring_stats ring; + struct wl18xx_acx_error_stats error; struct wl18xx_acx_debug_stats debug; struct wl18xx_acx_tx_stats tx; struct wl18xx_acx_rx_stats rx; struct wl18xx_acx_isr_stats isr; struct wl18xx_acx_pwr_stats pwr; - struct wl18xx_acx_event_stats event; struct wl18xx_acx_ps_poll_stats ps_poll; struct wl18xx_acx_rx_filter_stats rx_filter; struct wl18xx_acx_rx_rate_stats rx_rate; diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 96b149662906..3ce6f1039af3 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -40,9 +40,14 @@ WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug4, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug5, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(debug, debug6, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, prepared_descs, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(ring, tx_cmplt, "%u"); - +WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(error, error_null_Frame_tx_start, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(error, error_numll_frame_cts_start, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(error, error_bar_retry, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(error, error_frame_cts_nul_flid, "%u"); + +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_prepared_descs, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_cmplt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_prepared, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_data_prepared, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, tx_template_programmed, "%u"); @@ -77,7 +82,8 @@ WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_failed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_hit, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(tx, frag_cache_miss, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mem, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_beacon_early_term, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_out_of_mpdu_nodes, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_hdr_overflow, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_dropped_frame, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_done, "%u"); @@ -88,6 +94,9 @@ WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_pre_complt, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_cmplt_task, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_phy_hdr, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_timeout_wa, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_density_dropped_frame, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_wa_ba_not_expected, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_frame_checksum, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_checksum_result, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_called, "%u"); @@ -98,6 +107,7 @@ WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_defrag, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_decrypt_failed, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, decrypt_key_not_found, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx, defrag_need_decrypt, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx, rx_tkip_replays, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(isr, irqs, "%u"); @@ -109,10 +119,6 @@ WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pwr, cont_miss_bcns_spread, WL18XX_DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_bcns_cnt, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(event, calibration, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(event, rx_mismatch, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, "%u"); - WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, ps_poll_timeouts, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_timeouts, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(ps_poll, upsd_max_ap_turn, "%u"); @@ -127,11 +133,13 @@ WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, dup_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, data_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, ibss_filter, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, protection_filter, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, accum_arp_pend_requests, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE(rx_filter, max_arp_queue_dep, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(rx_rate, rx_frames_per_rates, "%u"); -WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_size, - AGGR_STATS_TX_SIZE_LEN); +WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, tx_agg_vs_rate, + AGGR_STATS_TX_AGG*AGGR_STATS_TX_RATE); WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(aggr_size, rx_size, AGGR_STATS_RX_SIZE_LEN); @@ -153,6 +161,9 @@ WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, dec_packet_out, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_in, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(pipeline, cs_rx_packet_out, "%u"); +WL18XX_DEBUGFS_FWSTATS_FILE_ARRAY(pipeline, pipeline_fifo_full, + PIPE_STATS_HW_FIFO); + WL18XX_DEBUGFS_FWSTATS_FILE(mem, rx_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, tx_free_mem_blks, "%u"); WL18XX_DEBUGFS_FWSTATS_FILE(mem, fwlog_free_mem_blks, "%u"); @@ -255,9 +266,14 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(debug, debug5); DEBUGFS_FWSTATS_ADD(debug, debug6); - DEBUGFS_FWSTATS_ADD(ring, prepared_descs); - DEBUGFS_FWSTATS_ADD(ring, tx_cmplt); + DEBUGFS_FWSTATS_ADD(error, error_frame); + DEBUGFS_FWSTATS_ADD(error, error_null_Frame_tx_start); + DEBUGFS_FWSTATS_ADD(error, error_numll_frame_cts_start); + DEBUGFS_FWSTATS_ADD(error, error_bar_retry); + DEBUGFS_FWSTATS_ADD(error, error_frame_cts_nul_flid); + DEBUGFS_FWSTATS_ADD(tx, tx_prepared_descs); + DEBUGFS_FWSTATS_ADD(tx, tx_cmplt); DEBUGFS_FWSTATS_ADD(tx, tx_template_prepared); DEBUGFS_FWSTATS_ADD(tx, tx_data_prepared); DEBUGFS_FWSTATS_ADD(tx, tx_template_programmed); @@ -292,7 +308,8 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(tx, frag_cache_hit); DEBUGFS_FWSTATS_ADD(tx, frag_cache_miss); - DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mem); + DEBUGFS_FWSTATS_ADD(rx, rx_beacon_early_term); + DEBUGFS_FWSTATS_ADD(rx, rx_out_of_mpdu_nodes); DEBUGFS_FWSTATS_ADD(rx, rx_hdr_overflow); DEBUGFS_FWSTATS_ADD(rx, rx_dropped_frame); DEBUGFS_FWSTATS_ADD(rx, rx_done); @@ -303,6 +320,9 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(rx, rx_cmplt_task); DEBUGFS_FWSTATS_ADD(rx, rx_phy_hdr); DEBUGFS_FWSTATS_ADD(rx, rx_timeout); + DEBUGFS_FWSTATS_ADD(rx, rx_timeout_wa); + DEBUGFS_FWSTATS_ADD(rx, rx_wa_density_dropped_frame); + DEBUGFS_FWSTATS_ADD(rx, rx_wa_ba_not_expected); DEBUGFS_FWSTATS_ADD(rx, rx_frame_checksum); DEBUGFS_FWSTATS_ADD(rx, rx_checksum_result); DEBUGFS_FWSTATS_ADD(rx, defrag_called); @@ -313,6 +333,7 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(rx, defrag_decrypt_failed); DEBUGFS_FWSTATS_ADD(rx, decrypt_key_not_found); DEBUGFS_FWSTATS_ADD(rx, defrag_need_decrypt); + DEBUGFS_FWSTATS_ADD(rx, rx_tkip_replays); DEBUGFS_FWSTATS_ADD(isr, irqs); @@ -322,10 +343,6 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(pwr, cont_miss_bcns_spread); DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_bcns_cnt); - DEBUGFS_FWSTATS_ADD(event, calibration); - DEBUGFS_FWSTATS_ADD(event, rx_mismatch); - DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); - DEBUGFS_FWSTATS_ADD(ps_poll, ps_poll_timeouts); DEBUGFS_FWSTATS_ADD(ps_poll, upsd_timeouts); DEBUGFS_FWSTATS_ADD(ps_poll, upsd_max_ap_turn); @@ -340,10 +357,12 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(rx_filter, data_filter); DEBUGFS_FWSTATS_ADD(rx_filter, ibss_filter); DEBUGFS_FWSTATS_ADD(rx_filter, protection_filter); + DEBUGFS_FWSTATS_ADD(rx_filter, accum_arp_pend_requests); + DEBUGFS_FWSTATS_ADD(rx_filter, max_arp_queue_dep); DEBUGFS_FWSTATS_ADD(rx_rate, rx_frames_per_rates); - DEBUGFS_FWSTATS_ADD(aggr_size, tx_size); + DEBUGFS_FWSTATS_ADD(aggr_size, tx_agg_vs_rate); DEBUGFS_FWSTATS_ADD(aggr_size, rx_size); DEBUGFS_FWSTATS_ADD(pipeline, hs_tx_stat_fifo_int); @@ -363,6 +382,7 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(pipeline, dec_packet_out); DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_in); DEBUGFS_FWSTATS_ADD(pipeline, cs_rx_packet_out); + DEBUGFS_FWSTATS_ADD(pipeline, pipeline_fifo_full); DEBUGFS_FWSTATS_ADD(mem, rx_free_mem_blks); DEBUGFS_FWSTATS_ADD(mem, tx_free_mem_blks); -- cgit v1.2.3 From d61c6b5550c759728e702e68c8423a23a6991fc3 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 29 May 2012 12:40:50 +0300 Subject: wl18xx: align wl18xx_conf_phy with FW variant and remove it wl18xx_conf_phy represents part of the FW native wl18xx_mac_and_phy_params structure. Remove it and replace the phy part of the wl18xx conf with the FW bound structure. This allows us to set/override all members. Increment the wlconf version to ensure compatibility with the new structure Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/conf.h | 37 ++++++++++++++++++++--- drivers/net/wireless/ti/wl18xx/main.c | 52 ++------------------------------ drivers/net/wireless/ti/wl18xx/reg.h | 57 ----------------------------------- 3 files changed, 36 insertions(+), 110 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index 130546aa970d..fac0b7e87e75 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -23,41 +23,70 @@ #define __WL18XX_CONF_H__ #define WL18XX_CONF_MAGIC 0x10e100ca -#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0001) +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0002) #define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ sizeof(struct wl18xx_priv_conf)) -struct wl18xx_conf_phy { +#define NUM_OF_CHANNELS_11_ABG 150 +#define NUM_OF_CHANNELS_11_P 7 +#define WL18XX_NUM_OF_SUB_BANDS 9 +#define SRF_TABLE_LEN 16 +#define PIN_MUXING_SIZE 2 + +struct wl18xx_mac_and_phy_params { u8 phy_standalone; u8 rdl; u8 enable_clpc; u8 enable_tx_low_pwr_on_siso_rdl; u8 auto_detect; u8 dedicated_fem; + u8 low_band_component; + + /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ u8 low_band_component_type; + u8 high_band_component; + + /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ u8 high_band_component_type; + u8 number_of_assembled_ant2_4; + u8 number_of_assembled_ant5; + u8 pin_muxing_platform_options[PIN_MUXING_SIZE]; + u8 external_pa_dc2dc; u8 tcxo_ldo_voltage; u8 xtal_itrim_val; u8 srf_state; + u8 srf1[SRF_TABLE_LEN]; + u8 srf2[SRF_TABLE_LEN]; + u8 srf3[SRF_TABLE_LEN]; u8 io_configuration; u8 sdio_configuration; u8 settings; u8 rx_profile; + u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG]; + u8 pwr_limit_reference_11_abg; + u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P]; + u8 pwr_limit_reference_11p; + u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; + u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; u8 primary_clock_setting_time; u8 clock_valid_on_wake_up; u8 secondary_clock_setting_time; - u8 pwr_limit_reference_11_abg; + u8 board_type; + /* enable point saturation */ u8 psat; + /* low/medium/high Tx power in dBm */ s8 low_power_val; s8 med_power_val; s8 high_power_val; + u8 padding[1]; } __packed; struct wl18xx_priv_conf { - struct wl18xx_conf_phy phy; + /* this structure is copied wholesale to FW */ + struct wl18xx_mac_and_phy_params phy; } __packed; #endif /* __WL18XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c26015b8830c..78b2e54a0be7 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -716,63 +716,17 @@ static void wl18xx_pre_upload(struct wl1271 *wl) static void wl18xx_set_mac_and_phy(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; - struct wl18xx_conf_phy *phy = &priv->conf.phy; - struct wl18xx_mac_and_phy_params params; size_t len; - memset(¶ms, 0, sizeof(params)); - - params.phy_standalone = phy->phy_standalone; - params.rdl = phy->rdl; - params.enable_clpc = phy->enable_clpc; - params.enable_tx_low_pwr_on_siso_rdl = - phy->enable_tx_low_pwr_on_siso_rdl; - params.auto_detect = phy->auto_detect; - params.dedicated_fem = phy->dedicated_fem; - params.low_band_component = phy->low_band_component; - params.low_band_component_type = - phy->low_band_component_type; - params.high_band_component = phy->high_band_component; - params.high_band_component_type = - phy->high_band_component_type; - params.number_of_assembled_ant2_4 = - n_antennas_2_param; - params.number_of_assembled_ant5 = - n_antennas_5_param; - params.external_pa_dc2dc = dc2dc_param; - params.tcxo_ldo_voltage = phy->tcxo_ldo_voltage; - params.xtal_itrim_val = phy->xtal_itrim_val; - params.srf_state = phy->srf_state; - params.io_configuration = phy->io_configuration; - params.sdio_configuration = phy->sdio_configuration; - params.settings = phy->settings; - params.rx_profile = phy->rx_profile; - params.primary_clock_setting_time = - phy->primary_clock_setting_time; - params.clock_valid_on_wake_up = - phy->clock_valid_on_wake_up; - params.secondary_clock_setting_time = - phy->secondary_clock_setting_time; - params.pwr_limit_reference_11_abg = - phy->pwr_limit_reference_11_abg; - - params.board_type = priv->board_type; - - /* for PG2 only */ - params.psat = phy->psat; - params.low_power_val = phy->low_power_val; - params.med_power_val = phy->med_power_val; - params.high_power_val = phy->high_power_val; - /* the parameters struct is smaller for PG1 */ if (wl->chip.id == CHIP_ID_185x_PG10) len = offsetof(struct wl18xx_mac_and_phy_params, psat) + 1; else - len = sizeof(params); + len = sizeof(struct wl18xx_mac_and_phy_params); wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); - wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)¶ms, - len, false); + wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, len, + false); } static void wl18xx_enable_interrupts(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index a824b26702a8..937b71d8783f 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -164,13 +164,6 @@ */ #define WL18XX_SCR_PAD8_PLT 0xBABABEBE -/* TODO: maybe move elsewhere? */ -#define NUM_OF_CHANNELS_11_ABG 150 -#define NUM_OF_CHANNELS_11_P 7 -#define WL18XX_NUM_OF_SUB_BANDS 9 -#define SRF_TABLE_LEN 16 -#define PIN_MUXING_SIZE 2 - enum { COMPONENT_NO_SWITCH = 0x0, COMPONENT_2_WAY_SWITCH = 0x1, @@ -195,54 +188,4 @@ enum { NUM_BOARD_TYPES, }; -struct wl18xx_mac_and_phy_params { - u8 phy_standalone; - u8 rdl; - u8 enable_clpc; - u8 enable_tx_low_pwr_on_siso_rdl; - u8 auto_detect; - u8 dedicated_fem; - - u8 low_band_component; - - /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ - u8 low_band_component_type; - - u8 high_band_component; - - /* Bit 0: One Hot, Bit 1: Control Enable, Bit 2: 1.8V, Bit 3: 3V */ - u8 high_band_component_type; - u8 number_of_assembled_ant2_4; - u8 number_of_assembled_ant5; - u8 pin_muxing_platform_options[PIN_MUXING_SIZE]; - u8 external_pa_dc2dc; - u8 tcxo_ldo_voltage; - u8 xtal_itrim_val; - u8 srf_state; - u8 srf1[SRF_TABLE_LEN]; - u8 srf2[SRF_TABLE_LEN]; - u8 srf3[SRF_TABLE_LEN]; - u8 io_configuration; - u8 sdio_configuration; - u8 settings; - u8 rx_profile; - u8 per_chan_pwr_limit_arr_11abg[NUM_OF_CHANNELS_11_ABG]; - u8 pwr_limit_reference_11_abg; - u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P]; - u8 pwr_limit_reference_11p; - u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; - u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS]; - u8 primary_clock_setting_time; - u8 clock_valid_on_wake_up; - u8 secondary_clock_setting_time; - u8 board_type; - /* enable point saturation */ - u8 psat; - /* low/medium/high Tx power in dBm */ - s8 low_power_val; - s8 med_power_val; - s8 high_power_val; - u8 padding[1]; -} __packed; - #endif /* __REG_H__ */ -- cgit v1.2.3 From 17d97719dc2fe2b9c13831dc2c8572b54efe00b4 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 29 May 2012 12:44:12 +0300 Subject: wl18xx: clean up phy module parameters Give all wl18xx phy module paramters -1 as a default value, indicating the paramter was not set. Add previous default values to the default 18xx priv conf structure. Remove the board_type field from wl18xx priv. The field with the same name inside the phy conf is good enough for our purposes. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 112 ++++++++++++++++---------------- drivers/net/wireless/ti/wl18xx/wl18xx.h | 2 - 2 files changed, 56 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 78b2e54a0be7..b2ccff7d6188 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -45,16 +45,18 @@ static char *ht_mode_param = "wide"; static char *board_type_param = "hdk"; -static bool dc2dc_param = false; -static int n_antennas_2_param = 1; -static int n_antennas_5_param = 1; static bool checksum_param = false; static bool enable_11a_param = true; -static int low_band_component = -1; -static int low_band_component_type = -1; -static int high_band_component = -1; -static int high_band_component_type = -1; -static int pwr_limit_reference_11_abg = -1; + +/* phy paramters */ +static int dc2dc_param = -1; +static int n_antennas_2_param = -1; +static int n_antennas_5_param = -1; +static int low_band_component_param = -1; +static int low_band_component_type_param = -1; +static int high_band_component_param = -1; +static int high_band_component_type_param = -1; +static int pwr_limit_reference_11_abg_param = -1; static const u8 wl18xx_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ @@ -516,6 +518,9 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .low_power_val = 0x00, .med_power_val = 0x0a, .high_power_val = 0x1e, + .external_pa_dc2dc = 0, + .number_of_assembled_ant2_4 = 1, + .number_of_assembled_ant5 = 1, }, }; @@ -1320,17 +1325,17 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) goto out_free; if (!strcmp(board_type_param, "fpga")) { - priv->board_type = BOARD_TYPE_FPGA_18XX; + priv->conf.phy.board_type = BOARD_TYPE_FPGA_18XX; } else if (!strcmp(board_type_param, "hdk")) { - priv->board_type = BOARD_TYPE_HDK_18XX; + priv->conf.phy.board_type = BOARD_TYPE_HDK_18XX; /* HACK! Just for now we hardcode HDK to 0x06 */ priv->conf.phy.low_band_component_type = 0x06; } else if (!strcmp(board_type_param, "dvp")) { - priv->board_type = BOARD_TYPE_DVP_18XX; + priv->conf.phy.board_type = BOARD_TYPE_DVP_18XX; } else if (!strcmp(board_type_param, "evb")) { - priv->board_type = BOARD_TYPE_EVB_18XX; + priv->conf.phy.board_type = BOARD_TYPE_EVB_18XX; } else if (!strcmp(board_type_param, "com8")) { - priv->board_type = BOARD_TYPE_COM8_18XX; + priv->conf.phy.board_type = BOARD_TYPE_COM8_18XX; /* HACK! Just for now we hardcode COM8 to 0x06 */ priv->conf.phy.low_band_component_type = 0x06; } else { @@ -1339,38 +1344,26 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) goto out_free; } - /* - * If the module param is not set, update it with the one from - * conf. If it is set, overwrite conf with it. - */ - if (low_band_component == -1) - low_band_component = priv->conf.phy.low_band_component; - else - priv->conf.phy.low_band_component = low_band_component; - if (low_band_component_type == -1) - low_band_component_type = - priv->conf.phy.low_band_component_type; - else + /* If the module param is set, update it in conf */ + if (low_band_component_param != -1) + priv->conf.phy.low_band_component = low_band_component_param; + if (low_band_component_type_param != -1) priv->conf.phy.low_band_component_type = - low_band_component_type; - - if (high_band_component == -1) - high_band_component = priv->conf.phy.high_band_component; - else - priv->conf.phy.high_band_component = high_band_component; - if (high_band_component_type == -1) - high_band_component_type = - priv->conf.phy.high_band_component_type; - else + low_band_component_type_param; + if (high_band_component_param != -1) + priv->conf.phy.high_band_component = high_band_component_param; + if (high_band_component_type_param != -1) priv->conf.phy.high_band_component_type = - high_band_component_type; - - if (pwr_limit_reference_11_abg == -1) - pwr_limit_reference_11_abg = - priv->conf.phy.pwr_limit_reference_11_abg; - else + high_band_component_type_param; + if (pwr_limit_reference_11_abg_param != -1) priv->conf.phy.pwr_limit_reference_11_abg = - pwr_limit_reference_11_abg; + pwr_limit_reference_11_abg_param; + if (n_antennas_2_param != -1) + priv->conf.phy.number_of_assembled_ant2_4 = n_antennas_2_param; + if (n_antennas_5_param != -1) + priv->conf.phy.number_of_assembled_ant5 = n_antennas_5_param; + if (dc2dc_param != -1) + priv->conf.phy.external_pa_dc2dc = dc2dc_param; if (!checksum_param) { wl18xx_ops.set_rx_csum = NULL; @@ -1422,38 +1415,45 @@ module_param_named(board_type, board_type_param, charp, S_IRUSR); MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or " "dvp"); -module_param_named(dc2dc, dc2dc_param, bool, S_IRUSR); -MODULE_PARM_DESC(dc2dc, "External DC2DC: boolean (defaults to false)"); - -module_param_named(n_antennas_2, n_antennas_2_param, uint, S_IRUSR); -MODULE_PARM_DESC(n_antennas_2, "Number of installed 2.4GHz antennas: 1 (default) or 2"); - -module_param_named(n_antennas_5, n_antennas_5_param, uint, S_IRUSR); -MODULE_PARM_DESC(n_antennas_5, "Number of installed 5GHz antennas: 1 (default) or 2"); - module_param_named(checksum, checksum_param, bool, S_IRUSR); MODULE_PARM_DESC(checksum, "Enable TCP checksum: boolean (defaults to false)"); module_param_named(enable_11a, enable_11a_param, bool, S_IRUSR); MODULE_PARM_DESC(enable_11a, "Enable 11a (5GHz): boolean (defaults to true)"); -module_param(low_band_component, uint, S_IRUSR); +module_param_named(dc2dc, dc2dc_param, int, S_IRUSR); +MODULE_PARM_DESC(dc2dc, "External DC2DC: u8 (defaults to 0)"); + +module_param_named(n_antennas_2, n_antennas_2_param, int, S_IRUSR); +MODULE_PARM_DESC(n_antennas_2, + "Number of installed 2.4GHz antennas: 1 (default) or 2"); + +module_param_named(n_antennas_5, n_antennas_5_param, int, S_IRUSR); +MODULE_PARM_DESC(n_antennas_5, + "Number of installed 5GHz antennas: 1 (default) or 2"); + +module_param_named(low_band_component, low_band_component_param, int, + S_IRUSR); MODULE_PARM_DESC(low_band_component, "Low band component: u8 " "(default is 0x01)"); -module_param(low_band_component_type, uint, S_IRUSR); +module_param_named(low_band_component_type, low_band_component_type_param, + int, S_IRUSR); MODULE_PARM_DESC(low_band_component_type, "Low band component type: u8 " "(default is 0x05 or 0x06 depending on the board_type)"); -module_param(high_band_component, uint, S_IRUSR); +module_param_named(high_band_component, high_band_component_param, int, + S_IRUSR); MODULE_PARM_DESC(high_band_component, "High band component: u8, " "(default is 0x01)"); -module_param(high_band_component_type, uint, S_IRUSR); +module_param_named(high_band_component_type, high_band_component_type_param, + int, S_IRUSR); MODULE_PARM_DESC(high_band_component_type, "High band component type: u8 " "(default is 0x09)"); -module_param(pwr_limit_reference_11_abg, uint, S_IRUSR); +module_param_named(pwr_limit_reference_11_abg, + pwr_limit_reference_11_abg_param, int, S_IRUSR); MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 " "(default is 0xc8)"); diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index b9c43097d2f6..bc67a4750615 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -35,8 +35,6 @@ struct wl18xx_priv { /* Index of last released Tx desc in FW */ u8 last_fw_rls_idx; - u8 board_type; - /* number of VIFs requiring extra spare mem-blocks */ int extra_spare_vif_count; }; -- cgit v1.2.3 From 3e3947fffc2ebe8c52891b3fca195172875dc951 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 29 May 2012 18:38:05 +0300 Subject: wlcore/wl12xx/wl18xx: make NVS file optional for wl18xx Don't spew errors when we can't find the NVS file in wlcore. Instead fail the wl12xx boot HW op if the NVS isn't found. Take this opportunity to remove some dead code from register_hw() which looks for the NVS again needlessly. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/boot.c | 4 +++- drivers/net/wireless/ti/wlcore/main.c | 22 ++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index ed718f7ddcce..0fda500c01c9 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -211,8 +211,10 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl) u32 dest_addr, val; u8 *nvs_ptr, *nvs_aligned; - if (wl->nvs == NULL) + if (wl->nvs == NULL) { + wl1271_error("NVS file is needed during boot"); return -ENODEV; + } if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) { struct wl1271_nvs_file *nvs = diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 176a3117889b..1156e3f578c1 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -715,7 +715,7 @@ out: return ret; } -static int wl1271_fetch_nvs(struct wl1271 *wl) +static void wl1271_fetch_nvs(struct wl1271 *wl) { const struct firmware *fw; int ret; @@ -723,16 +723,15 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev); if (ret < 0) { - wl1271_error("could not get nvs file %s: %d", WL12XX_NVS_NAME, - ret); - return ret; + wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d", + WL12XX_NVS_NAME, ret); + return; } wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); - ret = -ENOMEM; goto out; } @@ -740,8 +739,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) out: release_firmware(fw); - - return ret; } void wl12xx_queue_recovery_work(struct wl1271 *wl) @@ -989,13 +986,6 @@ static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt) if (ret < 0) goto out; - /* No NVS from netlink, try to get it from the filesystem */ - if (wl->nvs == NULL) { - ret = wl1271_fetch_nvs(wl); - if (ret < 0) - goto out; - } - out: return ret; } @@ -4937,8 +4927,8 @@ static int wl1271_register_hw(struct wl1271 *wl) if (wl->mac80211_registered) return 0; - ret = wl1271_fetch_nvs(wl); - if (ret == 0) { + wl1271_fetch_nvs(wl); + if (wl->nvs != NULL) { /* NOTE: The wl->nvs->nvs element must be first, in * order to simplify the casting, we assume it is at * the beginning of the wl->nvs structure. -- cgit v1.2.3 From c1dcad2d32d0252e8a3023d20311b52a187ecda3 Mon Sep 17 00:00:00 2001 From: Bernhard Seibold Date: Wed, 15 Feb 2012 13:40:43 +0100 Subject: HID: Driver for Lenovo Keyboard with Trackpoint This driver for the "Lenovo ThinkPad USB Keyboard with Trackpoint" supports setting various device attributes, controlling mute and microphone mute LEDs and enables use of the microphone mute key. Signed-off-by: Bernhard Seibold Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 12 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 + drivers/hid/hid-lenovo-tpkbd.c | 564 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 581 insertions(+) create mode 100644 drivers/hid/hid-lenovo-tpkbd.c (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e9c68fedfcff..ca7e76cab83d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -268,6 +268,18 @@ config HID_LCPOWER ---help--- Support for LC-Power RC1000MCE RF remote control. +config HID_LENOVO_TPKBD + tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" + depends on USB_HID + select LEDS_CLASS + ---help--- + Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. + + Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint + and would like to use device-specific features like changing the + sensitivity of the trackpoint, using the microphone mute button or + controlling the mute and microphone mute LEDs. + config HID_LOGITECH tristate "Logitech devices" if EXPERT depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ca6cc9f0485c..05913c533ec5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -54,6 +54,7 @@ obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o +obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8e3a6b261477..f69568032c04 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1544,6 +1544,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 9373f535dfe9..c607e6f33953 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -473,6 +473,9 @@ #define USB_DEVICE_ID_LD_HYBRID 0x2090 #define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 +#define USB_VENDOR_ID_LENOVO 0x17ef +#define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 + #define USB_VENDOR_ID_LG 0x1fd2 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c new file mode 100644 index 000000000000..77d2df04c97b --- /dev/null +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -0,0 +1,564 @@ +/* + * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint + * + * Copyright (c) 2012 Bernhard Seibold + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "usbhid/usbhid.h" + +#include "hid-ids.h" + +/* This is only used for the trackpoint part of the driver, hence _tp */ +struct tpkbd_data_pointer { + int led_state; + struct led_classdev led_mute; + struct led_classdev led_micmute; + int press_to_select; + int dragging; + int release_to_select; + int select_right; + int sensitivity; + int press_speed; +}; + +#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) + +static int tpkbd_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + struct usbhid_device *uhdev; + + uhdev = (struct usbhid_device *) hdev->driver_data; + if (uhdev->ifnum == 1 && usage->hid == (HID_UP_BUTTON | 0x0010)) { + map_key_clear(KEY_MICMUTE); + return 1; + } + return 0; +} + +#undef map_key_clear + +static int tpkbd_features_set(struct hid_device *hdev) +{ + struct hid_report *report; + struct tpkbd_data_pointer *data_pointer; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; + + report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; + report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; + report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; + report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; + report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver + report->field[2]->value[0] = data_pointer->sensitivity; + report->field[3]->value[0] = data_pointer->press_speed; + + usbhid_submit_report(hdev, report, USB_DIR_OUT); + return 0; +} + +static ssize_t pointer_press_to_select_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); +} + +static ssize_t pointer_press_to_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int value; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->press_to_select = value; + tpkbd_features_set(hdev); + + return count; +} + +static ssize_t pointer_dragging_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); +} + +static ssize_t pointer_dragging_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int value; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->dragging = value; + tpkbd_features_set(hdev); + + return count; +} + +static ssize_t pointer_release_to_select_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); +} + +static ssize_t pointer_release_to_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int value; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->release_to_select = value; + tpkbd_features_set(hdev); + + return count; +} + +static ssize_t pointer_select_right_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); +} + +static ssize_t pointer_select_right_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int value; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (kstrtoint(buf, 10, &value)) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + + data_pointer->select_right = value; + tpkbd_features_set(hdev); + + return count; +} + +static ssize_t pointer_sensitivity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + data_pointer->sensitivity); +} + +static ssize_t pointer_sensitivity_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int value; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) + return -EINVAL; + + data_pointer->sensitivity = value; + tpkbd_features_set(hdev); + + return count; +} + +static ssize_t pointer_press_speed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + data_pointer->press_speed); +} + +static ssize_t pointer_press_speed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int value; + + hdev = container_of(dev, struct hid_device, dev); + if (hdev == NULL) + return -ENODEV; + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) + return -EINVAL; + + data_pointer->press_speed = value; + tpkbd_features_set(hdev); + + return count; +} + +static struct device_attribute dev_attr_pointer_press_to_select = + __ATTR(press_to_select, S_IWUSR | S_IRUGO, + pointer_press_to_select_show, + pointer_press_to_select_store); + +static struct device_attribute dev_attr_pointer_dragging = + __ATTR(dragging, S_IWUSR | S_IRUGO, + pointer_dragging_show, + pointer_dragging_store); + +static struct device_attribute dev_attr_pointer_release_to_select = + __ATTR(release_to_select, S_IWUSR | S_IRUGO, + pointer_release_to_select_show, + pointer_release_to_select_store); + +static struct device_attribute dev_attr_pointer_select_right = + __ATTR(select_right, S_IWUSR | S_IRUGO, + pointer_select_right_show, + pointer_select_right_store); + +static struct device_attribute dev_attr_pointer_sensitivity = + __ATTR(sensitivity, S_IWUSR | S_IRUGO, + pointer_sensitivity_show, + pointer_sensitivity_store); + +static struct device_attribute dev_attr_pointer_press_speed = + __ATTR(press_speed, S_IWUSR | S_IRUGO, + pointer_press_speed_show, + pointer_press_speed_store); + +static struct attribute *tpkbd_attributes_pointer[] = { + &dev_attr_pointer_press_to_select.attr, + &dev_attr_pointer_dragging.attr, + &dev_attr_pointer_release_to_select.attr, + &dev_attr_pointer_select_right.attr, + &dev_attr_pointer_sensitivity.attr, + &dev_attr_pointer_press_speed.attr, + NULL +}; + +static const struct attribute_group tpkbd_attr_group_pointer = { + .attrs = tpkbd_attributes_pointer, +}; + +static enum led_brightness tpkbd_led_brightness_get( + struct led_classdev *led_cdev) +{ + struct device *dev; + struct hid_device *hdev; + struct tpkbd_data_pointer *data_pointer; + int led_nr = 0; + + dev = led_cdev->dev->parent; + hdev = container_of(dev, struct hid_device, dev); + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (led_cdev == &data_pointer->led_micmute) + led_nr = 1; + + return data_pointer->led_state & (1 << led_nr) + ? LED_FULL + : LED_OFF; +} + +static void tpkbd_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct device *dev; + struct hid_device *hdev; + struct hid_report *report; + struct tpkbd_data_pointer *data_pointer; + int led_nr = 0; + + dev = led_cdev->dev->parent; + hdev = container_of(dev, struct hid_device, dev); + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + if (led_cdev == &data_pointer->led_micmute) + led_nr = 1; + + if (value == LED_OFF) + data_pointer->led_state &= ~(1 << led_nr); + else + data_pointer->led_state |= 1 << led_nr; + + report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3]; + report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1; + report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1; + usbhid_submit_report(hdev, report, USB_DIR_OUT); +} + +static int tpkbd_probe_tp(struct hid_device *hdev) +{ + struct device *dev = &hdev->dev; + struct tpkbd_data_pointer *data_pointer; + size_t name_sz = strlen(dev_name(dev)) + 16; + char *name_mute, *name_micmute; + int ret; + + if (sysfs_create_group(&hdev->dev.kobj, + &tpkbd_attr_group_pointer)) { + hid_warn(hdev, "Could not create sysfs group\n"); + } + + data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL); + if (data_pointer == NULL) { + hid_err(hdev, "Could not allocate memory for driver data\n"); + return -ENOMEM; + } + + // set same default values as windows driver + data_pointer->sensitivity = 0xa0; + data_pointer->press_speed = 0x38; + + name_mute = kzalloc(name_sz, GFP_KERNEL); + if (name_mute == NULL) { + hid_err(hdev, "Could not allocate memory for led data\n"); + ret = -ENOMEM; + goto err; + } + snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); + + name_micmute = kzalloc(name_sz, GFP_KERNEL); + if (name_micmute == NULL) { + hid_err(hdev, "Could not allocate memory for led data\n"); + ret = -ENOMEM; + goto err2; + } + snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev)); + + hid_set_drvdata(hdev, data_pointer); + + data_pointer->led_mute.name = name_mute; + data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get; + data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set; + data_pointer->led_mute.dev = dev; + led_classdev_register(dev, &data_pointer->led_mute); + + data_pointer->led_micmute.name = name_micmute; + data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get; + data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set; + data_pointer->led_micmute.dev = dev; + led_classdev_register(dev, &data_pointer->led_micmute); + + tpkbd_features_set(hdev); + + return 0; + +err2: + kfree(name_mute); +err: + kfree(data_pointer); + return ret; +} + +static int tpkbd_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct usbhid_device *uhdev; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "hid_parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hid_hw_start failed\n"); + goto err_free; + } + + uhdev = (struct usbhid_device *) hdev->driver_data; + + if (uhdev->ifnum == 1) + return tpkbd_probe_tp(hdev); + + return 0; +err_free: + return ret; +} + +static void tpkbd_remove_tp(struct hid_device *hdev) +{ + struct tpkbd_data_pointer *data_pointer; + + sysfs_remove_group(&hdev->dev.kobj, + &tpkbd_attr_group_pointer); + + data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev); + + led_classdev_unregister(&data_pointer->led_micmute); + led_classdev_unregister(&data_pointer->led_mute); + + hid_set_drvdata(hdev, NULL); + kfree(data_pointer); +} + +static void tpkbd_remove(struct hid_device *hdev) +{ + struct usbhid_device *uhdev; + + uhdev = (struct usbhid_device *) hdev->driver_data; + if (uhdev->ifnum == 1) + tpkbd_remove_tp(hdev); + + hid_hw_stop(hdev); +} + +static const struct hid_device_id tpkbd_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, tpkbd_devices); + +static struct hid_driver tpkbd_driver = { + .name = "lenovo_tpkbd", + .id_table = tpkbd_devices, + .input_mapping = tpkbd_input_mapping, + .probe = tpkbd_probe, + .remove = tpkbd_remove, +}; + +static int __init tpkbd_init(void) +{ + return hid_register_driver(&tpkbd_driver); +} + +static void __exit tpkbd_exit(void) +{ + hid_unregister_driver(&tpkbd_driver); +} + +module_init(tpkbd_init); +module_exit(tpkbd_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ff164324123c0fe181d8de7dadcc7b3fbe25f2cf Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 7 Jun 2012 15:15:59 +0200 Subject: x86/apic: Make cpu_mask_to_apicid() operations return error code Current cpu_mask_to_apicid() and cpu_mask_to_apicid_and() implementations have few shortcomings: 1. A value returned by cpu_mask_to_apicid() is written to hardware registers unconditionally. Should BAD_APICID get ever returned it will be written to a hardware too. But the value of BAD_APICID is not universal across all hardware in all modes and might cause unexpected results, i.e. interrupts might get routed to CPUs that are not configured to receive it. 2. Because the value of BAD_APICID is not universal it is counter- intuitive to return it for a hardware where it does not make sense (i.e. x2apic). 3. cpu_mask_to_apicid_and() operation is thought as an complement to cpu_mask_to_apicid() that only applies a AND mask on top of a cpumask being passed. Yet, as consequence of 18374d8 commit the two operations are inconsistent in that of: cpu_mask_to_apicid() should not get a offline CPU with the cpumask cpu_mask_to_apicid_and() should not fail and return BAD_APICID These limitations are impossible to realize just from looking at the operations prototypes. Most of these shortcomings are resolved by returning a error code instead of BAD_APICID. As the result, faults are reported back early rather than possibilities to cause a unexpected behaviour exist (in case of [1]). The only exception is setup_timer_IRQ0_pin() routine. Although obviously controversial to this fix, its existing behaviour is preserved to not break the fragile check_timer() and would better addressed in a separate fix. Signed-off-by: Alexander Gordeev Acked-by: Suresh Siddha Cc: Yinghai Lu Link: http://lkml.kernel.org/r/20120607131559.GF4759@dhcp-26-207.brq.redhat.com Signed-off-by: Ingo Molnar --- drivers/iommu/intel_irq_remapping.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 6d347064b8b0..dafbad06390a 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -924,6 +924,7 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irq_cfg *cfg = data->chip_data; unsigned int dest, irq = data->irq; struct irte irte; + int err; if (!cpumask_intersects(mask, cpu_online_mask)) return -EINVAL; @@ -931,10 +932,16 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, if (get_irte(irq, &irte)) return -EBUSY; - if (assign_irq_vector(irq, cfg, mask)) - return -EBUSY; + err = assign_irq_vector(irq, cfg, mask); + if (err) + return err; - dest = apic->cpu_mask_to_apicid_and(cfg->domain, mask); + err = apic->cpu_mask_to_apicid_and(cfg->domain, mask, &dest); + if (err) { + if (assign_irq_vector(irq, cfg, data->affinity)); + pr_err("Failed to recover vector for irq %d\n", irq); + return err; + } irte.vector = cfg->vector; irte.dest_id = IRTE_DEST(dest); -- cgit v1.2.3 From bf947fcb77ff858f223c49c76e2d130095fa2585 Mon Sep 17 00:00:00 2001 From: Donald Dutile Date: Mon, 4 Jun 2012 17:29:01 -0400 Subject: iommu/dmar: Replace printks with appropriate pr_*() Just some cleanup so next patch can keep the info printing the same way throughout the file. Replace printk(KERN_* with pr_*() functions. Signed-off-by: Donald Dutile Cc: iommu@lists.linux-foundation.org Cc: chrisw@redhat.com Cc: suresh.b.siddha@intel.com Cc: dwmw2@infradead.org Link: http://lkml.kernel.org/r/1338845342-12464-2-git-send-email-ddutile@redhat.com Signed-off-by: Ingo Molnar --- drivers/iommu/dmar.c | 83 ++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 3a74e4410fc0..1e5a10de3471 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -83,15 +83,14 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, * ignore it */ if (!bus) { - printk(KERN_WARNING - PREFIX "Device scope bus [%d] not found\n", - scope->bus); + pr_warn(PREFIX "Device scope bus [%d] not found\n", + scope->bus); break; } pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn)); if (!pdev) { - printk(KERN_WARNING PREFIX - "Device scope device [%04x:%02x:%02x.%02x] not found\n", + pr_warn(PREFIX "Device scope device" + "[%04x:%02x:%02x.%02x] not found\n", segment, bus->number, path->dev, path->fn); break; } @@ -100,9 +99,9 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, bus = pdev->subordinate; } if (!pdev) { - printk(KERN_WARNING PREFIX - "Device scope device [%04x:%02x:%02x.%02x] not found\n", - segment, scope->bus, path->dev, path->fn); + pr_warn(PREFIX + "Device scope device [%04x:%02x:%02x.%02x] not found\n", + segment, scope->bus, path->dev, path->fn); *dev = NULL; return 0; } @@ -110,8 +109,7 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, pdev->subordinate) || (scope->entry_type == \ ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) { pci_dev_put(pdev); - printk(KERN_WARNING PREFIX - "Device scope type does not match for %s\n", + pr_warn(PREFIX "Device scope type does not match for %s\n", pci_name(pdev)); return -EINVAL; } @@ -134,8 +132,7 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) (*cnt)++; else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC) { - printk(KERN_WARNING PREFIX - "Unsupported device scope\n"); + pr_warn(PREFIX "Unsupported device scope\n"); } start += scope->length; } @@ -261,25 +258,23 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header) case ACPI_DMAR_TYPE_HARDWARE_UNIT: drhd = container_of(header, struct acpi_dmar_hardware_unit, header); - printk (KERN_INFO PREFIX - "DRHD base: %#016Lx flags: %#x\n", + pr_info(PREFIX "DRHD base: %#016Lx flags: %#x\n", (unsigned long long)drhd->address, drhd->flags); break; case ACPI_DMAR_TYPE_RESERVED_MEMORY: rmrr = container_of(header, struct acpi_dmar_reserved_memory, header); - printk (KERN_INFO PREFIX - "RMRR base: %#016Lx end: %#016Lx\n", + pr_info(PREFIX "RMRR base: %#016Lx end: %#016Lx\n", (unsigned long long)rmrr->base_address, (unsigned long long)rmrr->end_address); break; case ACPI_DMAR_TYPE_ATSR: atsr = container_of(header, struct acpi_dmar_atsr, header); - printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags); + pr_info(PREFIX "ATSR flags: %#x\n", atsr->flags); break; case ACPI_DMAR_HARDWARE_AFFINITY: rhsa = container_of(header, struct acpi_dmar_rhsa, header); - printk(KERN_INFO PREFIX "RHSA base: %#016Lx proximity domain: %#x\n", + pr_info(PREFIX "RHSA base: %#016Lx proximity domain: %#x\n", (unsigned long long)rhsa->base_address, rhsa->proximity_domain); break; @@ -299,7 +294,7 @@ static int __init dmar_table_detect(void) &dmar_tbl_size); if (ACPI_SUCCESS(status) && !dmar_tbl) { - printk (KERN_WARNING PREFIX "Unable to map DMAR\n"); + pr_warn(PREFIX "Unable to map DMAR\n"); status = AE_NOT_FOUND; } @@ -333,20 +328,18 @@ parse_dmar_table(void) return -ENODEV; if (dmar->width < PAGE_SHIFT - 1) { - printk(KERN_WARNING PREFIX "Invalid DMAR haw\n"); + pr_warn(PREFIX "Invalid DMAR haw\n"); return -EINVAL; } - printk (KERN_INFO PREFIX "Host address width %d\n", - dmar->width + 1); + pr_info(PREFIX "Host address width %d\n", dmar->width + 1); entry_header = (struct acpi_dmar_header *)(dmar + 1); while (((unsigned long)entry_header) < (((unsigned long)dmar) + dmar_tbl->length)) { /* Avoid looping forever on bad ACPI tables */ if (entry_header->length == 0) { - printk(KERN_WARNING PREFIX - "Invalid 0-length structure\n"); + pr_warn(PREFIX "Invalid 0-length structure\n"); ret = -EINVAL; break; } @@ -369,8 +362,7 @@ parse_dmar_table(void) #endif break; default: - printk(KERN_WARNING PREFIX - "Unknown DMAR structure type %d\n", + pr_warn(PREFIX "Unknown DMAR structure type %d\n", entry_header->type); ret = 0; /* for forward compatibility */ break; @@ -469,12 +461,12 @@ int __init dmar_table_init(void) ret = parse_dmar_table(); if (ret) { if (ret != -ENODEV) - printk(KERN_INFO PREFIX "parse DMAR table failure.\n"); + pr_info(PREFIX "parse DMAR table failure.\n"); return ret; } if (list_empty(&dmar_drhd_units)) { - printk(KERN_INFO PREFIX "No DMAR devices found\n"); + pr_info(PREFIX "No DMAR devices found\n"); return -ENODEV; } @@ -506,8 +498,7 @@ int __init check_zero_address(void) (((unsigned long)dmar) + dmar_tbl->length)) { /* Avoid looping forever on bad ACPI tables */ if (entry_header->length == 0) { - printk(KERN_WARNING PREFIX - "Invalid 0-length structure\n"); + pr_warn(PREFIX "Invalid 0-length structure\n"); return 0; } @@ -558,8 +549,8 @@ int __init detect_intel_iommu(void) if (ret && irq_remapping_enabled && cpu_has_x2apic && dmar->flags & 0x1) - printk(KERN_INFO - "Queued invalidation will be enabled to support x2apic and Intr-remapping.\n"); + pr_info("Queued invalidation will be enabled to " + "support x2apic and Intr-remapping.\n"); if (ret && !no_iommu && !iommu_detected && !dmar_disabled) { iommu_detected = 1; @@ -602,7 +593,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); if (!iommu->reg) { - printk(KERN_ERR "IOMMU: can't map the region\n"); + pr_err("IOMMU: can't map the region\n"); goto error; } iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); @@ -615,15 +606,13 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) agaw = iommu_calculate_agaw(iommu); if (agaw < 0) { - printk(KERN_ERR - "Cannot get a valid agaw for iommu (seq_id = %d)\n", - iommu->seq_id); + pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n", + iommu->seq_id); goto err_unmap; } msagaw = iommu_calculate_max_sagaw(iommu); if (msagaw < 0) { - printk(KERN_ERR - "Cannot get a valid max agaw for iommu (seq_id = %d)\n", + pr_err("Cannot get a valid max agaw for iommu (seq_id = %d)\n", iommu->seq_id); goto err_unmap; } @@ -640,7 +629,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iounmap(iommu->reg); iommu->reg = ioremap(drhd->reg_base_addr, map_size); if (!iommu->reg) { - printk(KERN_ERR "IOMMU: can't map the region\n"); + pr_err("IOMMU: can't map the region\n"); goto error; } } @@ -710,7 +699,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index) if (fault & DMA_FSTS_IQE) { head = readl(iommu->reg + DMAR_IQH_REG); if ((head >> DMAR_IQ_SHIFT) == index) { - printk(KERN_ERR "VT-d detected invalid descriptor: " + pr_err("VT-d detected invalid descriptor: " "low=%llx, high=%llx\n", (unsigned long long)qi->desc[index].low, (unsigned long long)qi->desc[index].high); @@ -1129,15 +1118,14 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type, reason = dmar_get_fault_reason(fault_reason, &fault_type); if (fault_type == INTR_REMAP) - printk(KERN_ERR "INTR-REMAP: Request device [[%02x:%02x.%d] " + pr_err("INTR-REMAP: Request device [[%02x:%02x.%d] " "fault index %llx\n" "INTR-REMAP:[fault reason %02d] %s\n", (source_id >> 8), PCI_SLOT(source_id & 0xFF), PCI_FUNC(source_id & 0xFF), addr >> 48, fault_reason, reason); else - printk(KERN_ERR - "DMAR:[%s] Request device [%02x:%02x.%d] " + pr_err("DMAR:[%s] Request device [%02x:%02x.%d] " "fault addr %llx \n" "DMAR:[fault reason %02d] %s\n", (type ? "DMA Read" : "DMA Write"), @@ -1157,8 +1145,7 @@ irqreturn_t dmar_fault(int irq, void *dev_id) raw_spin_lock_irqsave(&iommu->register_lock, flag); fault_status = readl(iommu->reg + DMAR_FSTS_REG); if (fault_status) - printk(KERN_ERR "DRHD: handling fault status reg %x\n", - fault_status); + pr_err("DRHD: handling fault status reg %x\n", fault_status); /* TBD: ignore advanced fault log currently */ if (!(fault_status & DMA_FSTS_PPF)) @@ -1224,7 +1211,7 @@ int dmar_set_interrupt(struct intel_iommu *iommu) irq = create_irq(); if (!irq) { - printk(KERN_ERR "IOMMU: no free vectors\n"); + pr_err("IOMMU: no free vectors\n"); return -EINVAL; } @@ -1241,7 +1228,7 @@ int dmar_set_interrupt(struct intel_iommu *iommu) ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu); if (ret) - printk(KERN_ERR "IOMMU: can't request irq\n"); + pr_err("IOMMU: can't request irq\n"); return ret; } @@ -1258,7 +1245,7 @@ int __init enable_drhd_fault_handling(void) ret = dmar_set_interrupt(iommu); if (ret) { - printk(KERN_ERR "DRHD %Lx: failed to enable fault, " + pr_err("DRHD %Lx: failed to enable fault, " " interrupt, ret %d\n", (unsigned long long)drhd->reg_base_addr, ret); return -1; -- cgit v1.2.3 From 6f5cf52114dd87f9ed091678f7dfc8ff21bbe2b3 Mon Sep 17 00:00:00 2001 From: Donald Dutile Date: Mon, 4 Jun 2012 17:29:02 -0400 Subject: iommu/dmar: Reserve mmio space used by the IOMMU, if the BIOS forgets to Intel-iommu initialization doesn't currently reserve the memory used for the IOMMU registers. This can allow the pci resource allocator to assign a device BAR to the same address as the IOMMU registers. This can cause some not so nice side affects when the driver ioremap's that region. Introduced two helper functions to map & unmap the IOMMU registers as well as simplify the init and exit paths. Signed-off-by: Donald Dutile Acked-by: Chris Wright Cc: iommu@lists.linux-foundation.org Cc: suresh.b.siddha@intel.com Cc: dwmw2@infradead.org Link: http://lkml.kernel.org/r/1338845342-12464-3-git-send-email-ddutile@redhat.com Signed-off-by: Ingo Molnar --- drivers/iommu/dmar.c | 111 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 1e5a10de3471..9ab6ebf46f7a 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -570,14 +570,89 @@ int __init detect_intel_iommu(void) } +static void unmap_iommu(struct intel_iommu *iommu) +{ + iounmap(iommu->reg); + release_mem_region(iommu->reg_phys, iommu->reg_size); +} + +/** + * map_iommu: map the iommu's registers + * @iommu: the iommu to map + * @phys_addr: the physical address of the base resgister + * + * Memory map the iommu's registers. Start w/ a single page, and + * possibly expand if that turns out to be insufficent. + */ +static int map_iommu(struct intel_iommu *iommu, u64 phys_addr) +{ + int map_size, err=0; + + iommu->reg_phys = phys_addr; + iommu->reg_size = VTD_PAGE_SIZE; + + if (!request_mem_region(iommu->reg_phys, iommu->reg_size, iommu->name)) { + pr_err("IOMMU: can't reserve memory\n"); + err = -EBUSY; + goto out; + } + + iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size); + if (!iommu->reg) { + pr_err("IOMMU: can't map the region\n"); + err = -ENOMEM; + goto release; + } + + iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); + iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); + + if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { + err = -EINVAL; + warn_invalid_dmar(phys_addr, " returns all ones"); + goto unmap; + } + + /* the registers might be more than one page */ + map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), + cap_max_fault_reg_offset(iommu->cap)); + map_size = VTD_PAGE_ALIGN(map_size); + if (map_size > iommu->reg_size) { + iounmap(iommu->reg); + release_mem_region(iommu->reg_phys, iommu->reg_size); + iommu->reg_size = map_size; + if (!request_mem_region(iommu->reg_phys, iommu->reg_size, + iommu->name)) { + pr_err("IOMMU: can't reserve memory\n"); + err = -EBUSY; + goto out; + } + iommu->reg = ioremap(iommu->reg_phys, iommu->reg_size); + if (!iommu->reg) { + pr_err("IOMMU: can't map the region\n"); + err = -ENOMEM; + goto release; + } + } + err = 0; + goto out; + +unmap: + iounmap(iommu->reg); +release: + release_mem_region(iommu->reg_phys, iommu->reg_size); +out: + return err; +} + int alloc_iommu(struct dmar_drhd_unit *drhd) { struct intel_iommu *iommu; - int map_size; u32 ver; static int iommu_allocated = 0; int agaw = 0; int msagaw = 0; + int err; if (!drhd->reg_base_addr) { warn_invalid_dmar(0, ""); @@ -591,19 +666,13 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->seq_id = iommu_allocated++; sprintf (iommu->name, "dmar%d", iommu->seq_id); - iommu->reg = ioremap(drhd->reg_base_addr, VTD_PAGE_SIZE); - if (!iommu->reg) { - pr_err("IOMMU: can't map the region\n"); + err = map_iommu(iommu, drhd->reg_base_addr); + if (err) { + pr_err("IOMMU: failed to map %s\n", iommu->name); goto error; } - iommu->cap = dmar_readq(iommu->reg + DMAR_CAP_REG); - iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); - - if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { - warn_invalid_dmar(drhd->reg_base_addr, " returns all ones"); - goto err_unmap; - } + err = -EINVAL; agaw = iommu_calculate_agaw(iommu); if (agaw < 0) { pr_err("Cannot get a valid agaw for iommu (seq_id = %d)\n", @@ -621,19 +690,6 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->node = -1; - /* the registers might be more than one page */ - map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), - cap_max_fault_reg_offset(iommu->cap)); - map_size = VTD_PAGE_ALIGN(map_size); - if (map_size > VTD_PAGE_SIZE) { - iounmap(iommu->reg); - iommu->reg = ioremap(drhd->reg_base_addr, map_size); - if (!iommu->reg) { - pr_err("IOMMU: can't map the region\n"); - goto error; - } - } - ver = readl(iommu->reg + DMAR_VER_REG); pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n", iommu->seq_id, @@ -648,10 +704,10 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) return 0; err_unmap: - iounmap(iommu->reg); + unmap_iommu(iommu); error: kfree(iommu); - return -1; + return err; } void free_iommu(struct intel_iommu *iommu) @@ -662,7 +718,8 @@ void free_iommu(struct intel_iommu *iommu) free_dmar_iommu(iommu); if (iommu->reg) - iounmap(iommu->reg); + unmap_iommu(iommu); + kfree(iommu); } -- cgit v1.2.3 From 934b9d1ed71cca5a220ec13e3f76d6aebf5183da Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Fri, 8 Jun 2012 14:24:53 -0400 Subject: wl18xx: avoid some -Wformat warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC drivers/net/wireless/ti/wl18xx/main.o drivers/net/wireless/ti/wl18xx/main.c: In function ‘wl18xx_conf_init’: drivers/net/wireless/ti/wl18xx/main.c:1024:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long unsigned int’ [-Wformat] drivers/net/wireless/ti/wl18xx/main.c:1024:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 3 has type ‘size_t’ [-Wformat] Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index b2ccff7d6188..ed9c3650e08a 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1022,7 +1022,8 @@ static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev) if (fw->size != WL18XX_CONF_SIZE) { wl1271_error("configuration binary file size is wrong, " - "expected %d got %d", WL18XX_CONF_SIZE, fw->size); + "expected %ld got %zd", + WL18XX_CONF_SIZE, fw->size); ret = -EINVAL; goto out; } -- cgit v1.2.3 From ea3b2ea24ef0f2ef9c6795b19cff456195b6728a Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Fri, 8 Jun 2012 18:29:06 +0300 Subject: mtd: nand: initialize bitflip_threshold prior to BBT scanning As of edbc454 [mtd: driver _read() returns max_bitflips; mtd_read() returns -EUCLEAN], 'mtd->bitflip_threshold' must be set for mtd devices having ECC, prior any 'mtd_read()' call. Otherwise, 'mtd_read()' will falsely return -EUCLEAN. Normally, 'mtd->bitflip_threshold' is initialized when the MTD is added. However, this is too late for NAND MTDs, as 'scan_bbt()' is invoked prior the existing initialization of 'mtd->bitflip_threshold'. This is a problem since 'scan_bbt()' calls 'mtd_read()', in the case of a flash-based bad block table. It resulted in a falsely reported bitflips indication during BBT read, which lead to constant scrubbing of the flash BBT blocks. Initialize 'mtd->bitflip_threshold' to its default value (if not already set by the driver), prior to invocation of 'scan_bbt()'. Reported-by: Sascha Hauer Tested-by: Sascha Hauer Signed-off-by: Shmulik Ladkani Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d47586cf64ce..a11253a0fcab 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3501,6 +3501,13 @@ int nand_scan_tail(struct mtd_info *mtd) /* propagate ecc info to mtd_info */ mtd->ecclayout = chip->ecc.layout; mtd->ecc_strength = chip->ecc.strength; + /* + * Initialize bitflip_threshold to its default prior scan_bbt() call. + * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be + * properly set. + */ + if (!mtd->bitflip_threshold) + mtd->bitflip_threshold = mtd->ecc_strength; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) -- cgit v1.2.3 From 4a31bd28e86ac50eb620f6b5b36464c45b5fa38f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Jan 2012 13:52:34 +0100 Subject: ARM: nomadik: convert to generic clock Remove more custom stuff by simply converting the Nomadik machine to use generic clocks and move the driver to drivers/clk. Acked-by: Arnd Bergmann Cc: Mike Turquette Signed-off-by: Linus Walleij --- drivers/clk/Makefile | 1 + drivers/clk/clk-nomadik.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 drivers/clk/clk-nomadik.c (limited to 'drivers') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b9a5158a30b1..26b6b92942e1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \ clk-mux.o clk-divider.o clk-fixed-factor.o # SoCs specific +obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_PLAT_SPEAR) += spear/ diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c new file mode 100644 index 000000000000..517a8ff7121e --- /dev/null +++ b/drivers/clk/clk-nomadik.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include + +/* + * The Nomadik clock tree is described in the STN8815A12 DB V4.2 + * reference manual for the chip, page 94 ff. + */ + +void __init nomadik_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + clk_register_clkdev(clk, NULL, "gpio.0"); + clk_register_clkdev(clk, NULL, "gpio.1"); + clk_register_clkdev(clk, NULL, "gpio.2"); + clk_register_clkdev(clk, NULL, "gpio.3"); + clk_register_clkdev(clk, NULL, "rng"); + + /* + * The 2.4 MHz TIMCLK reference clock is active at boot time, this is + * actually the MXTALCLK @19.2 MHz divided by 8. This clock is used + * by the timers and watchdog. See page 105 ff. + */ + clk = clk_register_fixed_rate(NULL, "TIMCLK", NULL, CLK_IS_ROOT, + 2400000); + clk_register_clkdev(clk, NULL, "mtu0"); + clk_register_clkdev(clk, NULL, "mtu1"); + + /* + * At boot time, PLL2 is set to generate a set of fixed clocks, + * one of them is CLK48, the 48 MHz clock, routed to the UART, MMC/SD + * I2C, IrDA, USB and SSP blocks. + */ + clk = clk_register_fixed_rate(NULL, "CLK48", NULL, CLK_IS_ROOT, + 48000000); + clk_register_clkdev(clk, NULL, "uart0"); + clk_register_clkdev(clk, NULL, "uart1"); + clk_register_clkdev(clk, NULL, "mmci"); + clk_register_clkdev(clk, NULL, "ssp"); + clk_register_clkdev(clk, NULL, "nmk-i2c.0"); + clk_register_clkdev(clk, NULL, "nmk-i2c.1"); +} -- cgit v1.2.3 From e4a99938706b080b562055f83bf29e28ce0770ad Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 10:04:42 +0800 Subject: regulator: isl6271a: Remove setting map_voltage callback for isl_fixed_ops isl_fixed_ops does not implement set_voltage_sel callback, adding map_voltage here is misleading. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/isl6271a-regulator.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index 4a11b82a6d57..1d145a07ada9 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -81,7 +81,6 @@ static int isl6271a_get_fixed_voltage(struct regulator_dev *dev) static struct regulator_ops isl_fixed_ops = { .get_voltage = isl6271a_get_fixed_voltage, .list_voltage = regulator_list_voltage_linear, - .map_voltage = regulator_map_voltage_linear, }; static const struct regulator_desc isl_rd[] = { -- cgit v1.2.3 From ea7e33045458ffd6d13175031527b6527807633c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 4 Jun 2012 12:44:15 +0800 Subject: regulator: twl: Convert twl6030ldo_ops to use regulator_list_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index c7390711d954..a1d07e517db3 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -577,13 +577,6 @@ static struct regulator_ops twl6030coresmps_ops = { .get_voltage = twl6030coresmps_get_voltage, }; -static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - return ((info->min_mV + (index * 100)) * 1000); -} - static int twl6030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) @@ -621,7 +614,7 @@ static int twl6030ldo_get_voltage(struct regulator_dev *rdev) } static struct regulator_ops twl6030ldo_ops = { - .list_voltage = twl6030ldo_list_voltage, + .list_voltage = regulator_list_voltage_linear, .set_voltage = twl6030ldo_set_voltage, .get_voltage = twl6030ldo_get_voltage, @@ -959,6 +952,8 @@ static struct twlreg_info TWL6030_INFO_##label = { \ .ops = &twl6030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .min_uV = min_mVolts * 1000, \ + .uV_step = 100 * 1000, \ }, \ } @@ -974,6 +969,8 @@ static struct twlreg_info TWL6025_INFO_##label = { \ .ops = &twl6030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .min_uV = min_mVolts * 1000, \ + .uV_step = 100 * 1000, \ }, \ } -- cgit v1.2.3 From d01c3a1e1b1e4e417202adf1e9dec0730eb00e2b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 3 Jun 2012 23:02:34 +0800 Subject: regulator: anatop: Convert to set_voltage_sel and regulator_map_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index d04cf2113245..4324cea01141 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -43,33 +43,15 @@ struct anatop_regulator { struct regulator_init_data *initdata; }; -static int anatop_set_voltage(struct regulator_dev *reg, int min_uV, - int max_uV, unsigned *selector) +static int anatop_set_voltage_sel(struct regulator_dev *reg, unsigned selector) { struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg); - u32 val, sel, mask; - int uv; - - uv = min_uV; - dev_dbg(®->dev, "%s: uv %d, min %d, max %d\n", __func__, - uv, anatop_reg->min_voltage, - anatop_reg->max_voltage); - - if (uv < anatop_reg->min_voltage) { - if (max_uV > anatop_reg->min_voltage) - uv = anatop_reg->min_voltage; - else - return -EINVAL; - } + u32 val, mask; if (!anatop_reg->control_reg) return -ENOTSUPP; - sel = DIV_ROUND_UP(uv - anatop_reg->min_voltage, 25000); - if (sel * 25000 + anatop_reg->min_voltage > anatop_reg->max_voltage) - return -EINVAL; - val = anatop_reg->min_bit_val + sel; - *selector = sel; + val = anatop_reg->min_bit_val + selector; dev_dbg(®->dev, "%s: calculated val %d\n", __func__, val); mask = ((1 << anatop_reg->vol_bit_width) - 1) << anatop_reg->vol_bit_shift; @@ -95,9 +77,10 @@ static int anatop_get_voltage_sel(struct regulator_dev *reg) } static struct regulator_ops anatop_rops = { - .set_voltage = anatop_set_voltage, + .set_voltage_sel = anatop_set_voltage_sel, .get_voltage_sel = anatop_get_voltage_sel, - .list_voltage = regulator_list_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, }; static int __devinit anatop_regulator_probe(struct platform_device *pdev) -- cgit v1.2.3 From ab0d1cbe55014ac65cfc317f8ef2cd5fa4b7d4da Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 12:03:04 +0800 Subject: regulator: max77686: Check pdata->num_regulators earlier If this driver only works when pdata->num_regulators == MAX77686_REGULATORS, let's check it earlier. Also remove unused num_regulators from struct max77686_data. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index b76a0389d0b8..b0c146df42d1 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -65,7 +65,6 @@ enum max77686_ramp_rate { struct max77686_data { struct device *dev; struct max77686_dev *iodev; - int num_regulators; struct regulator_dev **rdev; int ramp_delay; /* in mV/us */ }; @@ -235,6 +234,12 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); + if (!pdata || pdata->num_regulators != MAX77686_REGULATORS) { + dev_err(&pdev->dev, + "Invalid initial data for regulator's initialiation\n"); + return -EINVAL; + } + max77686 = devm_kzalloc(&pdev->dev, sizeof(struct max77686_data), GFP_KERNEL); if (!max77686) @@ -248,8 +253,6 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) rdev = max77686->rdev; max77686->dev = &pdev->dev; max77686->iodev = iodev; - if (pdata) - max77686->num_regulators = pdata->num_regulators; platform_set_drvdata(pdev, max77686); max77686->ramp_delay = RAMP_RATE_NO_CTRL; /* Set 0x3 for RAMP */ @@ -263,34 +266,26 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) MAX77686_REG_BUCK4CTRL1, MAX77686_RAMP_RATE_MASK, max77686->ramp_delay << 6); - if (pdata->num_regulators == MAX77686_REGULATORS) { - for (i = 0; i < MAX77686_REGULATORS; i++) { - config.dev = max77686->dev; - config.regmap = iodev->regmap; - config.driver_data = max77686; - config.init_data = pdata->regulators[i].initdata; - - rdev[i] = regulator_register(®ulators[i], &config); - - if (IS_ERR(rdev[i])) { - ret = PTR_ERR(rdev[i]); - dev_err(max77686->dev, + for (i = 0; i < MAX77686_REGULATORS; i++) { + config.dev = max77686->dev; + config.regmap = iodev->regmap; + config.driver_data = max77686; + config.init_data = pdata->regulators[i].initdata; + + rdev[i] = regulator_register(®ulators[i], &config); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(max77686->dev, "regulator init failed for %d\n", i); rdev[i] = NULL; goto err; - } } - } else { - dev_err(max77686->dev, - "Lack of initial data for regulator's initialiation\n"); - return -EINVAL; } + return 0; err: - for (i = 0; i < MAX77686_REGULATORS; i++) { - if (rdev[i]) - regulator_unregister(rdev[i]); - } + while (--i >= 0) + regulator_unregister(rdev[i]); return ret; } -- cgit v1.2.3 From 98a175b60f46a80dfa44fb0e0807f2e5a351f35f Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Sat, 9 Jun 2012 16:40:38 +0530 Subject: regulator: core: Add regulator_set_voltage_time_sel to calculate ramp delay. This patch adds regulator_set_voltage_time_sel(), to move into core, the commonly used code by drivers to provide the .set_voltage_time_sel callback. It will also allow us to configure different ramp delay for different regulators easily. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/core.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5c6aedaa934d..ff76abde3ab3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2279,6 +2279,30 @@ int regulator_set_voltage_time(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_set_voltage_time); +/** + *regulator_set_voltage_time_sel - get raise/fall time + * @regulator: regulator source + * @old_selector: selector for starting voltage + * @new_selector: selector for target voltage + * + * Provided with the starting and target voltage selectors, this function + * returns time in microseconds required to rise or fall to this new voltage + * + * Drivers providing uV_step in their regulator_desc and ramp_delay in + * regulation_constraints can use this as their set_voltage_time_sel() + * operation. + */ +int regulator_set_voltage_time_sel(struct regulator_dev *rdev, + unsigned int old_selector, + unsigned int new_selector) +{ + if (rdev->desc->ramp_delay && rdev->desc->uV_step) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->desc->ramp_delay) * 1000; + return 0; +} + /** * regulator_sync_voltage - re-apply last regulator output voltage * @regulator: regulator source -- cgit v1.2.3 From 23ca6bf2318fecfbb9cd9d524e82d5029ef365bf Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 17:21:44 +0800 Subject: regulator: max77686: Fix the delay time for set_voltage_time_sel rdev->desc->uV_step * abs(new_selector - old_selector) returns uV. The unit of ramp_rate is mV/us. Thus 1000 should be multiplied. Signed-off-by: Axel Lin Reviewed-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index b0c146df42d1..2dd4ac91d283 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -77,7 +77,7 @@ static int max77686_set_dvs_voltage_time_sel(struct regulator_dev *rdev, return DIV_ROUND_UP(rdev->desc->uV_step * abs(new_selector - old_selector), - ramp_rate[max77686->ramp_delay]); + ramp_rate[max77686->ramp_delay] * 1000); } static int max77686_set_voltage_time_sel(struct regulator_dev *rdev, @@ -85,7 +85,7 @@ static int max77686_set_voltage_time_sel(struct regulator_dev *rdev, { /* Unconditionally 100 mV/us */ return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), 100); + abs(new_selector - old_selector), 100 * 1000); } static struct regulator_ops max77686_ops = { -- cgit v1.2.3 From 578df8babf3b1895d562e1ea0d3a81d1e77e0182 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 11 Jun 2012 13:14:50 +0800 Subject: regulator: core: Return correct delay time in regulator_set_voltage_time_sel rdev->desc->uV_step * abs(new_selector - old_selector) returns uV. The unit of ramp_delay is mV/us. Current code multiples 1000 at wrong place. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ff76abde3ab3..6ffca9b32388 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2299,7 +2299,7 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, if (rdev->desc->ramp_delay && rdev->desc->uV_step) return DIV_ROUND_UP(rdev->desc->uV_step * abs(new_selector - old_selector), - rdev->desc->ramp_delay) * 1000; + rdev->desc->ramp_delay * 1000); return 0; } -- cgit v1.2.3 From e9071b0be5e7ce4903b7f7c370769d485774d3e3 Mon Sep 17 00:00:00 2001 From: Donald Dutile Date: Fri, 8 Jun 2012 17:13:11 -0400 Subject: iommu/dmar: Use pr_format() instead of PREFIX to tidy up pr_*() calls Joe Perches recommended getting rid of the redundant formatting of adding "PREFIX" to all the uses of pr_*() calls. The recommendation helps to reduce source and improve readibility. While cleaning up the PREFIX's, I saw that one of the pr_warn() was redundant in dmar_parse_one_dev_scope(), since the same message was printed after breaking out of the while loop for the same condition, !pdev. So, to avoid a duplicate message, I removed the one in the while loop. Reported-by: Joe Perches Signed-off-by: Donald Dutile Cc: iommu@lists.linux-foundation.org Cc: chrisw@redhat.com Cc: suresh.b.siddha@intel.com Cc: dwmw2@infradead.org Link: http://lkml.kernel.org/r/1339189991-13129-1-git-send-email-ddutile@redhat.com [ Small whitespace fixes. ] Signed-off-by: Ingo Molnar --- drivers/iommu/dmar.c | 54 +++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 9ab6ebf46f7a..86e2f4a62b9a 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -26,6 +26,8 @@ * These routines are used by both DMA-remapping and Interrupt-remapping */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* has to precede printk.h */ + #include #include #include @@ -39,8 +41,6 @@ #include #include -#define PREFIX "DMAR: " - /* No locks are needed as DMA remapping hardware unit * list is constructed at boot time and hotplug of * these units are not supported by the architecture. @@ -83,15 +83,12 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, * ignore it */ if (!bus) { - pr_warn(PREFIX "Device scope bus [%d] not found\n", - scope->bus); + pr_warn("Device scope bus [%d] not found\n", scope->bus); break; } pdev = pci_get_slot(bus, PCI_DEVFN(path->dev, path->fn)); if (!pdev) { - pr_warn(PREFIX "Device scope device" - "[%04x:%02x:%02x.%02x] not found\n", - segment, bus->number, path->dev, path->fn); + /* warning will be printed below */ break; } path ++; @@ -99,8 +96,7 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, bus = pdev->subordinate; } if (!pdev) { - pr_warn(PREFIX - "Device scope device [%04x:%02x:%02x.%02x] not found\n", + pr_warn("Device scope device [%04x:%02x:%02x.%02x] not found\n", segment, scope->bus, path->dev, path->fn); *dev = NULL; return 0; @@ -109,8 +105,8 @@ static int __init dmar_parse_one_dev_scope(struct acpi_dmar_device_scope *scope, pdev->subordinate) || (scope->entry_type == \ ACPI_DMAR_SCOPE_TYPE_BRIDGE && !pdev->subordinate)) { pci_dev_put(pdev); - pr_warn(PREFIX "Device scope type does not match for %s\n", - pci_name(pdev)); + pr_warn("Device scope type does not match for %s\n", + pci_name(pdev)); return -EINVAL; } *dev = pdev; @@ -132,7 +128,7 @@ int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) (*cnt)++; else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC) { - pr_warn(PREFIX "Unsupported device scope\n"); + pr_warn("Unsupported device scope\n"); } start += scope->length; } @@ -258,23 +254,23 @@ dmar_table_print_dmar_entry(struct acpi_dmar_header *header) case ACPI_DMAR_TYPE_HARDWARE_UNIT: drhd = container_of(header, struct acpi_dmar_hardware_unit, header); - pr_info(PREFIX "DRHD base: %#016Lx flags: %#x\n", + pr_info("DRHD base: %#016Lx flags: %#x\n", (unsigned long long)drhd->address, drhd->flags); break; case ACPI_DMAR_TYPE_RESERVED_MEMORY: rmrr = container_of(header, struct acpi_dmar_reserved_memory, header); - pr_info(PREFIX "RMRR base: %#016Lx end: %#016Lx\n", + pr_info("RMRR base: %#016Lx end: %#016Lx\n", (unsigned long long)rmrr->base_address, (unsigned long long)rmrr->end_address); break; case ACPI_DMAR_TYPE_ATSR: atsr = container_of(header, struct acpi_dmar_atsr, header); - pr_info(PREFIX "ATSR flags: %#x\n", atsr->flags); + pr_info("ATSR flags: %#x\n", atsr->flags); break; case ACPI_DMAR_HARDWARE_AFFINITY: rhsa = container_of(header, struct acpi_dmar_rhsa, header); - pr_info(PREFIX "RHSA base: %#016Lx proximity domain: %#x\n", + pr_info("RHSA base: %#016Lx proximity domain: %#x\n", (unsigned long long)rhsa->base_address, rhsa->proximity_domain); break; @@ -294,7 +290,7 @@ static int __init dmar_table_detect(void) &dmar_tbl_size); if (ACPI_SUCCESS(status) && !dmar_tbl) { - pr_warn(PREFIX "Unable to map DMAR\n"); + pr_warn("Unable to map DMAR\n"); status = AE_NOT_FOUND; } @@ -328,18 +324,18 @@ parse_dmar_table(void) return -ENODEV; if (dmar->width < PAGE_SHIFT - 1) { - pr_warn(PREFIX "Invalid DMAR haw\n"); + pr_warn("Invalid DMAR haw\n"); return -EINVAL; } - pr_info(PREFIX "Host address width %d\n", dmar->width + 1); + pr_info("Host address width %d\n", dmar->width + 1); entry_header = (struct acpi_dmar_header *)(dmar + 1); while (((unsigned long)entry_header) < (((unsigned long)dmar) + dmar_tbl->length)) { /* Avoid looping forever on bad ACPI tables */ if (entry_header->length == 0) { - pr_warn(PREFIX "Invalid 0-length structure\n"); + pr_warn("Invalid 0-length structure\n"); ret = -EINVAL; break; } @@ -362,7 +358,7 @@ parse_dmar_table(void) #endif break; default: - pr_warn(PREFIX "Unknown DMAR structure type %d\n", + pr_warn("Unknown DMAR structure type %d\n", entry_header->type); ret = 0; /* for forward compatibility */ break; @@ -461,12 +457,12 @@ int __init dmar_table_init(void) ret = parse_dmar_table(); if (ret) { if (ret != -ENODEV) - pr_info(PREFIX "parse DMAR table failure.\n"); + pr_info("parse DMAR table failure.\n"); return ret; } if (list_empty(&dmar_drhd_units)) { - pr_info(PREFIX "No DMAR devices found\n"); + pr_info("No DMAR devices found\n"); return -ENODEV; } @@ -498,7 +494,7 @@ int __init check_zero_address(void) (((unsigned long)dmar) + dmar_tbl->length)) { /* Avoid looping forever on bad ACPI tables */ if (entry_header->length == 0) { - pr_warn(PREFIX "Invalid 0-length structure\n"); + pr_warn("Invalid 0-length structure\n"); return 0; } @@ -549,8 +545,7 @@ int __init detect_intel_iommu(void) if (ret && irq_remapping_enabled && cpu_has_x2apic && dmar->flags & 0x1) - pr_info("Queued invalidation will be enabled to " - "support x2apic and Intr-remapping.\n"); + pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n"); if (ret && !no_iommu && !iommu_detected && !dmar_disabled) { iommu_detected = 1; @@ -580,9 +575,9 @@ static void unmap_iommu(struct intel_iommu *iommu) * map_iommu: map the iommu's registers * @iommu: the iommu to map * @phys_addr: the physical address of the base resgister - * + * * Memory map the iommu's registers. Start w/ a single page, and - * possibly expand if that turns out to be insufficent. + * possibly expand if that turns out to be insufficent. */ static int map_iommu(struct intel_iommu *iommu, u64 phys_addr) { @@ -1302,8 +1297,7 @@ int __init enable_drhd_fault_handling(void) ret = dmar_set_interrupt(iommu); if (ret) { - pr_err("DRHD %Lx: failed to enable fault, " - " interrupt, ret %d\n", + pr_err("DRHD %Lx: failed to enable fault, interrupt, ret %d\n", (unsigned long long)drhd->reg_base_addr, ret); return -1; } -- cgit v1.2.3 From 3a6490c0840c0ae67cc3a51e1b724bd7e460041e Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Sun, 3 Jun 2012 13:36:51 +0300 Subject: iwlwifi: refactor testmode Create an object that will enacpsulate the testmode functionality that is common to all op modes. * Copy definitions from dvm/dev.h * Copy the testmode logic from dvm/testmode.c * Link iwl-test object into the iwlwifi module * Modify DVM to use iwl-test object Reviewed-by: Amit Beka Reviewed-by: Emmanuel Grumbach Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/Makefile | 1 + drivers/net/wireless/iwlwifi/dvm/agn.h | 17 +- drivers/net/wireless/iwlwifi/dvm/dev.h | 26 +- drivers/net/wireless/iwlwifi/dvm/main.c | 2 +- drivers/net/wireless/iwlwifi/dvm/rx.c | 24 +- drivers/net/wireless/iwlwifi/dvm/testmode.c | 769 ++------------------------ drivers/net/wireless/iwlwifi/dvm/testmode.h | 309 ----------- drivers/net/wireless/iwlwifi/iwl-test.c | 825 ++++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-test.h | 125 +++++ drivers/net/wireless/iwlwifi/iwl-testmode.h | 309 +++++++++++ 10 files changed, 1336 insertions(+), 1071 deletions(-) delete mode 100644 drivers/net/wireless/iwlwifi/dvm/testmode.h create mode 100644 drivers/net/wireless/iwlwifi/iwl-test.c create mode 100644 drivers/net/wireless/iwlwifi/iwl-test.h create mode 100644 drivers/net/wireless/iwlwifi/iwl-testmode.h (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 98c8f6449649..afa9758364ea 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -13,5 +13,6 @@ iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o iwlwifi-objs += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o +iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src) diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 2ae3608472a6..6d102413dd94 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -395,8 +395,10 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) } extern int iwl_alive_start(struct iwl_priv *priv); -/* svtool */ + +/* testmode support */ #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len); extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, @@ -404,13 +406,16 @@ extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct netlink_callback *cb, void *data, int len); extern void iwl_testmode_init(struct iwl_priv *priv); -extern void iwl_testmode_cleanup(struct iwl_priv *priv); +extern void iwl_testmode_free(struct iwl_priv *priv); + #else + static inline int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) { return -ENOSYS; } + static inline int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, @@ -418,12 +423,12 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, { return -ENOSYS; } -static inline -void iwl_testmode_init(struct iwl_priv *priv) + +static inline void iwl_testmode_init(struct iwl_priv *priv) { } -static inline -void iwl_testmode_cleanup(struct iwl_priv *priv) + +static inline void iwl_testmode_free(struct iwl_priv *priv) { } #endif diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 89f2e1040e7f..4620b657948a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -52,6 +52,8 @@ #include "rs.h" #include "tt.h" +#include "iwl-test.h" + /* CT-KILL constants */ #define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ #define CT_KILL_THRESHOLD 114 /* in Celsius */ @@ -596,24 +598,6 @@ struct iwl_lib_ops { void (*temperature)(struct iwl_priv *priv); }; -#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE -struct iwl_testmode_trace { - u32 buff_size; - u32 total_size; - u32 num_chunks; - u8 *cpu_addr; - u8 *trace_addr; - dma_addr_t dma_addr; - bool trace_enabled; -}; -struct iwl_testmode_mem { - u32 buff_size; - u32 num_chunks; - u8 *buff_addr; - bool read_in_progress; -}; -#endif - struct iwl_wipan_noa_data { struct rcu_head rcu_head; u32 length; @@ -670,8 +654,6 @@ struct iwl_priv { enum ieee80211_band band; u8 valid_contexts; - void (*pre_rx_handler)(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb); int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); @@ -895,9 +877,9 @@ struct iwl_priv { struct led_classdev led; unsigned long blink_on, blink_off; bool led_registered; + #ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE - struct iwl_testmode_trace testmode_trace; - struct iwl_testmode_mem testmode_mem; + struct iwl_test tst; u32 tm_fixed_rate; #endif diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 1c2d0233a405..656ed317c6d3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1548,7 +1548,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) iwl_dbgfs_unregister(priv); - iwl_testmode_cleanup(priv); + iwl_testmode_free(priv); iwlagn_mac_unregister(priv); iwl_tt_exit(priv); diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index 0ed90bb8b56a..afdacb25f344 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -1124,8 +1124,6 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); - void (*pre_rx_handler)(struct iwl_priv *, - struct iwl_rx_cmd_buffer *); int err = 0; /* @@ -1135,19 +1133,19 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, */ iwl_notification_wait_notify(&priv->notif_wait, pkt); - /* RX data may be forwarded to userspace (using pre_rx_handler) in one - * of two cases: the first, that the user owns the uCode through - * testmode - in such case the pre_rx_handler is set and no further - * processing takes place. The other case is when the user want to - * monitor the rx w/o affecting the regular flow - the pre_rx_handler - * will be set but the ownership flag != IWL_OWNERSHIP_TM and the flow +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + /* + * RX data may be forwarded to userspace in one + * of two cases: the user owns the fw through testmode or when + * the user requested to monitor the rx w/o affecting the regular flow. + * In these cases the iwl_test object will handle forwarding the rx + * data to user space. + * Note that if the ownership flag != IWL_OWNERSHIP_TM the flow * continues. - * We need to use ACCESS_ONCE to prevent a case where the handler - * changes between the check and the call. */ - pre_rx_handler = ACCESS_ONCE(priv->pre_rx_handler); - if (pre_rx_handler) - pre_rx_handler(priv, rxb); + iwl_test_rx(&priv->tst, priv->hw, rxb); +#endif + if (priv->ucode_owner != IWL_OWNERSHIP_TM) { /* Based on type of command response or notification, * handle those that need handling via function in diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c index a7b59590bb53..aa9518f13e89 100644 --- a/drivers/net/wireless/iwlwifi/dvm/testmode.c +++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c @@ -60,6 +60,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ + #include #include #include @@ -69,355 +70,55 @@ #include #include #include + #include "iwl-debug.h" -#include "iwl-io.h" #include "iwl-trans.h" -#include "iwl-fh.h" -#include "iwl-prph.h" #include "dev.h" #include "agn.h" -#include "testmode.h" - - -/* Periphery registers absolute lower bound. This is used in order to - * differentiate registery access through HBUS_TARG_PRPH_* and - * HBUS_TARG_MEM_* accesses. - */ -#define IWL_TM_ABS_PRPH_START (0xA00000) - -/* The TLVs used in the gnl message policy between the kernel module and - * user space application. iwl_testmode_gnl_msg_policy is to be carried - * through the NL80211_CMD_TESTMODE channel regulated by nl80211. - * See testmode.h - */ -static -struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { - [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, }, - [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, }, - [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, }, - [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, }, +#include "iwl-test.h" +#include "iwl-testmode.h" - [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, }, - [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, - - [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, }, - [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, }, - - [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, - [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, }, - [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, }, - - [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, }, -}; - -/* - * See the struct iwl_rx_packet in commands.h for the format of the - * received events from the device - */ -static inline int get_event_length(struct iwl_rx_cmd_buffer *rxb) +static int iwl_testmode_send_cmd(struct iwl_op_mode *op_mode, + struct iwl_host_cmd *cmd) { - struct iwl_rx_packet *pkt = rxb_addr(rxb); - if (pkt) - return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - else - return 0; + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + return iwl_dvm_send_cmd(priv, cmd); } - -/* - * This function multicasts the spontaneous messages from the device to the - * user space. It is invoked whenever there is a received messages - * from the device. This function is called within the ISR of the rx handlers - * in iwlagn driver. - * - * The parsing of the message content is left to the user space application, - * The message content is treated as unattacked raw data and is encapsulated - * with IWL_TM_ATTR_UCODE_RX_PKT multicasting to the user space. - * - * @priv: the instance of iwlwifi device - * @rxb: pointer to rx data content received by the ISR - * - * See the message policies and TLVs in iwl_testmode_gnl_msg_policy[]. - * For the messages multicasting to the user application, the mandatory - * TLV fields are : - * IWL_TM_ATTR_COMMAND must be IWL_TM_CMD_DEV2APP_UCODE_RX_PKT - * IWL_TM_ATTR_UCODE_RX_PKT for carrying the message content - */ - -static void iwl_testmode_ucode_rx_pkt(struct iwl_priv *priv, - struct iwl_rx_cmd_buffer *rxb) +static bool iwl_testmode_valid_hw_addr(u32 addr) { - struct ieee80211_hw *hw = priv->hw; - struct sk_buff *skb; - void *data; - int length; - - data = (void *)rxb_addr(rxb); - length = get_event_length(rxb); + if (iwlagn_hw_valid_rtc_data_addr(addr)) + return true; - if (!data || length == 0) - return; + if (IWLAGN_RTC_INST_LOWER_BOUND <= addr && + addr < IWLAGN_RTC_INST_UPPER_BOUND) + return true; - skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length, - GFP_ATOMIC); - if (skb == NULL) { - IWL_ERR(priv, - "Run out of memory for messages to user space ?\n"); - return; - } - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || - /* the length doesn't include len_n_flags field, so add it manually */ - nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length + sizeof(__le32), data)) - goto nla_put_failure; - cfg80211_testmode_event(skb, GFP_ATOMIC); - return; - -nla_put_failure: - kfree_skb(skb); - IWL_ERR(priv, "Ouch, overran buffer, check allocation!\n"); + return false; } -void iwl_testmode_init(struct iwl_priv *priv) +static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode) { - priv->pre_rx_handler = NULL; - priv->testmode_trace.trace_enabled = false; - priv->testmode_mem.read_in_progress = false; -} - -static void iwl_mem_cleanup(struct iwl_priv *priv) -{ - if (priv->testmode_mem.read_in_progress) { - kfree(priv->testmode_mem.buff_addr); - priv->testmode_mem.buff_addr = NULL; - priv->testmode_mem.buff_size = 0; - priv->testmode_mem.num_chunks = 0; - priv->testmode_mem.read_in_progress = false; - } -} - -static void iwl_trace_cleanup(struct iwl_priv *priv) -{ - if (priv->testmode_trace.trace_enabled) { - if (priv->testmode_trace.cpu_addr && - priv->testmode_trace.dma_addr) - dma_free_coherent(priv->trans->dev, - priv->testmode_trace.total_size, - priv->testmode_trace.cpu_addr, - priv->testmode_trace.dma_addr); - priv->testmode_trace.trace_enabled = false; - priv->testmode_trace.cpu_addr = NULL; - priv->testmode_trace.trace_addr = NULL; - priv->testmode_trace.dma_addr = 0; - priv->testmode_trace.buff_size = 0; - priv->testmode_trace.total_size = 0; - } -} - - -void iwl_testmode_cleanup(struct iwl_priv *priv) -{ - iwl_trace_cleanup(priv); - iwl_mem_cleanup(priv); + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + return priv->fw->ucode_ver; } +static struct iwl_test_ops tst_ops = { + .send_cmd = iwl_testmode_send_cmd, + .valid_hw_addr = iwl_testmode_valid_hw_addr, + .get_fw_ver = iwl_testmode_get_fw_ver, +}; -/* - * This function handles the user application commands to the ucode. - * - * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_CMD_ID and - * IWL_TM_ATTR_UCODE_CMD_DATA and calls to the handler to send the - * host command to the ucode. - * - * If any mandatory field is missing, -ENOMSG is replied to the user space - * application; otherwise, waits for the host command to be sent and checks - * the return code. In case or error, it is returned, otherwise a reply is - * allocated and the reply RX packet - * is returned. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb) +void iwl_testmode_init(struct iwl_priv *priv) { - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_host_cmd cmd; - struct iwl_rx_packet *pkt; - struct sk_buff *skb; - void *reply_buf; - u32 reply_len; - int ret; - bool cmd_want_skb; - - memset(&cmd, 0, sizeof(struct iwl_host_cmd)); - - if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] || - !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) { - IWL_ERR(priv, "Missing ucode command mandatory fields\n"); - return -ENOMSG; - } - - cmd.flags = CMD_ON_DEMAND | CMD_SYNC; - cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]); - if (cmd_want_skb) - cmd.flags |= CMD_WANT_SKB; - - cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]); - cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); - cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - IWL_DEBUG_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x," - " len %d\n", cmd.id, cmd.flags, cmd.len[0]); - - ret = iwl_dvm_send_cmd(priv, &cmd); - if (ret) { - IWL_ERR(priv, "Failed to send hcmd\n"); - return ret; - } - if (!cmd_want_skb) - return ret; - - /* Handling return of SKB to the user */ - pkt = cmd.resp_pkt; - if (!pkt) { - IWL_ERR(priv, "HCMD received a null response packet\n"); - return ret; - } - - reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20); - reply_buf = kmalloc(reply_len, GFP_KERNEL); - if (!skb || !reply_buf) { - kfree_skb(skb); - kfree(reply_buf); - return -ENOMEM; - } - - /* The reply is in a page, that we cannot send to user space. */ - memcpy(reply_buf, &(pkt->hdr), reply_len); - iwl_free_resp(&cmd); - - if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || - nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) - goto nla_put_failure; - return cfg80211_testmode_reply(skb); - -nla_put_failure: - IWL_DEBUG_INFO(priv, "Failed creating NL attributes\n"); - return -ENOMSG; + iwl_test_init(&priv->tst, priv->trans, &tst_ops); } - -/* - * This function handles the user application commands for register access. - * - * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the - * handlers respectively. - * - * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the - * mandatory fields(IWL_TM_ATTR_REG_OFFSET,IWL_TM_ATTR_REG_VALUE32, - * IWL_TM_ATTR_REG_VALUE8) are missing; Otherwise 0 is replied indicating - * the success of the command execution. - * - * If IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_READ32, the register read - * value is returned with IWL_TM_ATTR_REG_VALUE32. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) +void iwl_testmode_free(struct iwl_priv *priv) { - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u32 ofs, val32, cmd; - u8 val8; - struct sk_buff *skb; - int status = 0; - - if (!tb[IWL_TM_ATTR_REG_OFFSET]) { - IWL_ERR(priv, "Missing register offset\n"); - return -ENOMSG; - } - ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]); - IWL_INFO(priv, "testmode register access command offset 0x%x\n", ofs); - - /* Allow access only to FH/CSR/HBUS in direct mode. - Since we don't have the upper bounds for the CSR and HBUS segments, - we will use only the upper bound of FH for sanity check. */ - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - if ((cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 || - cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 || - cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8) && - (ofs >= FH_MEM_UPPER_BOUND)) { - IWL_ERR(priv, "offset out of segment (0x0 - 0x%x)\n", - FH_MEM_UPPER_BOUND); - return -EINVAL; - } - - switch (cmd) { - case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - val32 = iwl_read_direct32(priv->trans, ofs); - IWL_INFO(priv, "32bit value to read 0x%x\n", val32); - - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - if (!tb[IWL_TM_ATTR_REG_VALUE32]) { - IWL_ERR(priv, "Missing value to write\n"); - return -ENOMSG; - } else { - val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); - IWL_INFO(priv, "32bit value to write 0x%x\n", val32); - iwl_write_direct32(priv->trans, ofs, val32); - } - break; - case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - if (!tb[IWL_TM_ATTR_REG_VALUE8]) { - IWL_ERR(priv, "Missing value to write\n"); - return -ENOMSG; - } else { - val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]); - IWL_INFO(priv, "8bit value to write 0x%x\n", val8); - iwl_write8(priv->trans, ofs, val8); - } - break; - default: - IWL_ERR(priv, "Unknown testmode register command ID\n"); - return -ENOSYS; - } - - return status; - -nla_put_failure: - kfree_skb(skb); - return -EMSGSIZE; + iwl_test_free(&priv->tst); } - static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv) { struct iwl_notification_wait calib_wait; @@ -469,7 +170,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) struct sk_buff *skb; unsigned char *rsp_data_ptr = NULL; int status = 0, rsp_data_len = 0; - u32 devid, inst_size = 0, data_size = 0; + u32 inst_size = 0, data_size = 0; const struct fw_img *img; switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { @@ -563,39 +264,6 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]); break; - case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: - IWL_INFO(priv, "uCode version raw: 0x%x\n", - priv->fw->ucode_ver); - - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, - priv->fw->ucode_ver)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - - case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: - devid = priv->trans->hw_id; - IWL_INFO(priv, "hw version: 0x%x\n", devid); - - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - return -ENOMEM; - } - if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) - IWL_ERR(priv, "Error sending msg : %d\n", status); - break; - case IWL_TM_CMD_APP2DEV_GET_FW_INFO: skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8); if (!skb) { @@ -630,125 +298,6 @@ nla_put_failure: return -EMSGSIZE; } - -/* - * This function handles the user application commands for uCode trace - * - * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the - * handlers respectively. - * - * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned - * value of the actual command execution is replied to the user application. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct sk_buff *skb; - int status = 0; - struct device *dev = priv->trans->dev; - - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - if (priv->testmode_trace.trace_enabled) - return -EBUSY; - - if (!tb[IWL_TM_ATTR_TRACE_SIZE]) - priv->testmode_trace.buff_size = TRACE_BUFF_SIZE_DEF; - else - priv->testmode_trace.buff_size = - nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]); - if (!priv->testmode_trace.buff_size) - return -EINVAL; - if (priv->testmode_trace.buff_size < TRACE_BUFF_SIZE_MIN || - priv->testmode_trace.buff_size > TRACE_BUFF_SIZE_MAX) - return -EINVAL; - - priv->testmode_trace.total_size = - priv->testmode_trace.buff_size + TRACE_BUFF_PADD; - priv->testmode_trace.cpu_addr = - dma_alloc_coherent(dev, - priv->testmode_trace.total_size, - &priv->testmode_trace.dma_addr, - GFP_KERNEL); - if (!priv->testmode_trace.cpu_addr) - return -ENOMEM; - priv->testmode_trace.trace_enabled = true; - priv->testmode_trace.trace_addr = (u8 *)PTR_ALIGN( - priv->testmode_trace.cpu_addr, 0x100); - memset(priv->testmode_trace.trace_addr, 0x03B, - priv->testmode_trace.buff_size); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - sizeof(priv->testmode_trace.dma_addr) + 20); - if (!skb) { - IWL_ERR(priv, "Memory allocation fail\n"); - iwl_trace_cleanup(priv); - return -ENOMEM; - } - if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR, - sizeof(priv->testmode_trace.dma_addr), - (u64 *)&priv->testmode_trace.dma_addr)) - goto nla_put_failure; - status = cfg80211_testmode_reply(skb); - if (status < 0) { - IWL_ERR(priv, "Error sending msg : %d\n", status); - } - priv->testmode_trace.num_chunks = - DIV_ROUND_UP(priv->testmode_trace.buff_size, - DUMP_CHUNK_SIZE); - break; - - case IWL_TM_CMD_APP2DEV_END_TRACE: - iwl_trace_cleanup(priv); - break; - default: - IWL_ERR(priv, "Unknown testmode mem command ID\n"); - return -ENOSYS; - } - return status; - -nla_put_failure: - kfree_skb(skb); - if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) == - IWL_TM_CMD_APP2DEV_BEGIN_TRACE) - iwl_trace_cleanup(priv); - return -EMSGSIZE; -} - -static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int idx, length; - - if (priv->testmode_trace.trace_enabled && - priv->testmode_trace.trace_addr) { - idx = cb->args[4]; - if (idx >= priv->testmode_trace.num_chunks) - return -ENOENT; - length = DUMP_CHUNK_SIZE; - if (((idx + 1) == priv->testmode_trace.num_chunks) && - (priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE)) - length = priv->testmode_trace.buff_size % - DUMP_CHUNK_SIZE; - - if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length, - priv->testmode_trace.trace_addr + - (DUMP_CHUNK_SIZE * idx))) - goto nla_put_failure; - idx++; - cb->args[4] = idx; - return 0; - } else - return -EFAULT; - - nla_put_failure: - return -ENOBUFS; -} - /* * This function handles the user application switch ucode ownership. * @@ -777,10 +326,10 @@ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]); if (owner == IWL_OWNERSHIP_DRIVER) { priv->ucode_owner = owner; - priv->pre_rx_handler = NULL; + iwl_test_enable_notifications(&priv->tst, false); } else if (owner == IWL_OWNERSHIP_TM) { - priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; priv->ucode_owner = owner; + iwl_test_enable_notifications(&priv->tst, true); } else { IWL_ERR(priv, "Invalid owner\n"); return -EINVAL; @@ -788,180 +337,6 @@ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) return 0; } -static int iwl_testmode_indirect_read(struct iwl_priv *priv, u32 addr, u32 size) -{ - struct iwl_trans *trans = priv->trans; - unsigned long flags; - int i; - - if (size & 0x3) - return -EINVAL; - priv->testmode_mem.buff_size = size; - priv->testmode_mem.buff_addr = - kmalloc(priv->testmode_mem.buff_size, GFP_KERNEL); - if (priv->testmode_mem.buff_addr == NULL) - return -ENOMEM; - - /* Hard-coded periphery absolute address */ - if (IWL_TM_ABS_PRPH_START <= addr && - addr < IWL_TM_ABS_PRPH_START + PRPH_END) { - spin_lock_irqsave(&trans->reg_lock, flags); - iwl_grab_nic_access(trans); - iwl_write32(trans, HBUS_TARG_PRPH_RADDR, - addr | (3 << 24)); - for (i = 0; i < size; i += 4) - *(u32 *)(priv->testmode_mem.buff_addr + i) = - iwl_read32(trans, HBUS_TARG_PRPH_RDAT); - iwl_release_nic_access(trans); - spin_unlock_irqrestore(&trans->reg_lock, flags); - } else { /* target memory (SRAM) */ - _iwl_read_targ_mem_words(trans, addr, - priv->testmode_mem.buff_addr, - priv->testmode_mem.buff_size / 4); - } - - priv->testmode_mem.num_chunks = - DIV_ROUND_UP(priv->testmode_mem.buff_size, DUMP_CHUNK_SIZE); - priv->testmode_mem.read_in_progress = true; - return 0; - -} - -static int iwl_testmode_indirect_write(struct iwl_priv *priv, u32 addr, - u32 size, unsigned char *buf) -{ - struct iwl_trans *trans = priv->trans; - u32 val, i; - unsigned long flags; - - if (IWL_TM_ABS_PRPH_START <= addr && - addr < IWL_TM_ABS_PRPH_START + PRPH_END) { - /* Periphery writes can be 1-3 bytes long, or DWORDs */ - if (size < 4) { - memcpy(&val, buf, size); - spin_lock_irqsave(&trans->reg_lock, flags); - iwl_grab_nic_access(trans); - iwl_write32(trans, HBUS_TARG_PRPH_WADDR, - (addr & 0x0000FFFF) | - ((size - 1) << 24)); - iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); - iwl_release_nic_access(trans); - /* needed after consecutive writes w/o read */ - mmiowb(); - spin_unlock_irqrestore(&trans->reg_lock, flags); - } else { - if (size % 4) - return -EINVAL; - for (i = 0; i < size; i += 4) - iwl_write_prph(trans, addr+i, - *(u32 *)(buf+i)); - } - } else if (iwlagn_hw_valid_rtc_data_addr(addr) || - (IWLAGN_RTC_INST_LOWER_BOUND <= addr && - addr < IWLAGN_RTC_INST_UPPER_BOUND)) { - _iwl_write_targ_mem_words(trans, addr, buf, size/4); - } else - return -EINVAL; - return 0; -} - -/* - * This function handles the user application commands for SRAM data dump - * - * It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and - * IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading - * - * Several error will be retured, -EBUSY if the SRAM data retrieved by - * previous command has not been delivered to userspace, or -ENOMSG if - * the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE) - * are missing, or -ENOMEM if the buffer allocation fails. - * - * Otherwise 0 is replied indicating the success of the SRAM reading. - * - * @hw: ieee80211_hw object that represents the device - * @tb: gnl message fields from the user space - */ -static int iwl_testmode_indirect_mem(struct ieee80211_hw *hw, - struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - u32 addr, size, cmd; - unsigned char *buf; - - /* Both read and write should be blocked, for atomicity */ - if (priv->testmode_mem.read_in_progress) - return -EBUSY; - - cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); - if (!tb[IWL_TM_ATTR_MEM_ADDR]) { - IWL_ERR(priv, "Error finding memory offset address\n"); - return -ENOMSG; - } - addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]); - if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) { - IWL_ERR(priv, "Error finding size for memory reading\n"); - return -ENOMSG; - } - size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]); - - if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) - return iwl_testmode_indirect_read(priv, addr, size); - else { - if (!tb[IWL_TM_ATTR_BUFFER_DUMP]) - return -EINVAL; - buf = (unsigned char *) nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]); - return iwl_testmode_indirect_write(priv, addr, size, buf); - } -} - -static int iwl_testmode_buffer_dump(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - int idx, length; - - if (priv->testmode_mem.read_in_progress) { - idx = cb->args[4]; - if (idx >= priv->testmode_mem.num_chunks) { - iwl_mem_cleanup(priv); - return -ENOENT; - } - length = DUMP_CHUNK_SIZE; - if (((idx + 1) == priv->testmode_mem.num_chunks) && - (priv->testmode_mem.buff_size % DUMP_CHUNK_SIZE)) - length = priv->testmode_mem.buff_size % - DUMP_CHUNK_SIZE; - - if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length, - priv->testmode_mem.buff_addr + - (DUMP_CHUNK_SIZE * idx))) - goto nla_put_failure; - idx++; - cb->args[4] = idx; - return 0; - } else - return -EFAULT; - - nla_put_failure: - return -ENOBUFS; -} - -static int iwl_testmode_notifications(struct ieee80211_hw *hw, - struct nlattr **tb) -{ - struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - bool enable; - - enable = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]); - if (enable) - priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; - else - priv->pre_rx_handler = NULL; - return 0; -} - - /* The testmode gnl message handler that takes the gnl message from the * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then * invoke the corresponding handlers. @@ -987,32 +362,27 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int result; - result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, - iwl_testmode_gnl_msg_policy); - if (result != 0) { - IWL_ERR(priv, "Error parsing the gnl message : %d\n", result); + result = iwl_test_parse(&priv->tst, tb, data, len); + if (result) return result; - } - /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ - if (!tb[IWL_TM_ATTR_COMMAND]) { - IWL_ERR(priv, "Missing testmode command type\n"); - return -ENOMSG; - } /* in case multiple accesses to the device happens */ mutex_lock(&priv->mutex); - switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { case IWL_TM_CMD_APP2DEV_UCODE: - IWL_DEBUG_INFO(priv, "testmode cmd to uCode\n"); - result = iwl_testmode_ucode(hw, tb); - break; case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - IWL_DEBUG_INFO(priv, "testmode cmd to register\n"); - result = iwl_testmode_reg(hw, tb); + case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: + case IWL_TM_CMD_APP2DEV_END_TRACE: + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: + case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: + case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: + case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: + result = iwl_test_handle_cmd(&priv->tst, hw, tb); break; + case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: @@ -1020,45 +390,25 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) case IWL_TM_CMD_APP2DEV_GET_EEPROM: case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: - case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: - case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: case IWL_TM_CMD_APP2DEV_GET_FW_INFO: IWL_DEBUG_INFO(priv, "testmode cmd to driver\n"); result = iwl_testmode_driver(hw, tb); break; - case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - case IWL_TM_CMD_APP2DEV_END_TRACE: - case IWL_TM_CMD_APP2DEV_READ_TRACE: - IWL_DEBUG_INFO(priv, "testmode uCode trace cmd to driver\n"); - result = iwl_testmode_trace(hw, tb); - break; - case IWL_TM_CMD_APP2DEV_OWNERSHIP: IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n"); result = iwl_testmode_ownership(hw, tb); break; - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - IWL_DEBUG_INFO(priv, "testmode indirect memory cmd " - "to driver\n"); - result = iwl_testmode_indirect_mem(hw, tb); - break; - - case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - IWL_DEBUG_INFO(priv, "testmode notifications cmd " - "to driver\n"); - result = iwl_testmode_notifications(hw, tb); - break; - default: IWL_ERR(priv, "Unknown testmode command\n"); result = -ENOSYS; break; } - mutex_unlock(&priv->mutex); + + if (result) + IWL_ERR(priv, "Test cmd failed result=%d\n", result); return result; } @@ -1066,7 +416,6 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, struct netlink_callback *cb, void *data, int len) { - struct nlattr *tb[IWL_TM_ATTR_MAX]; struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int result; u32 cmd; @@ -1075,39 +424,19 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, /* offset by 1 since commands start at 0 */ cmd = cb->args[3] - 1; } else { - result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, - iwl_testmode_gnl_msg_policy); - if (result) { - IWL_ERR(priv, - "Error parsing the gnl message : %d\n", result); + struct nlattr *tb[IWL_TM_ATTR_MAX]; + + result = iwl_test_parse(&priv->tst, tb, data, len); + if (result) return result; - } - /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ - if (!tb[IWL_TM_ATTR_COMMAND]) { - IWL_ERR(priv, "Missing testmode command type\n"); - return -ENOMSG; - } cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); cb->args[3] = cmd + 1; } /* in case multiple accesses to the device happens */ mutex_lock(&priv->mutex); - switch (cmd) { - case IWL_TM_CMD_APP2DEV_READ_TRACE: - IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n"); - result = iwl_testmode_trace_dump(hw, skb, cb); - break; - case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: - IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n"); - result = iwl_testmode_buffer_dump(hw, skb, cb); - break; - default: - result = -EINVAL; - break; - } - + result = iwl_test_dump(&priv->tst, cmd, skb, cb); mutex_unlock(&priv->mutex); return result; } diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.h b/drivers/net/wireless/iwlwifi/dvm/testmode.h deleted file mode 100644 index 6ba211b09426..000000000000 --- a/drivers/net/wireless/iwlwifi/dvm/testmode.h +++ /dev/null @@ -1,309 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - *****************************************************************************/ -#ifndef __IWL_TESTMODE_H__ -#define __IWL_TESTMODE_H__ - -#include - - -/* - * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and - * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX). - * The command ID is carried with IWL_TM_ATTR_COMMAND. - * - * @IWL_TM_CMD_APP2DEV_UCODE: - * commands from user application to the uCode, - * the actual uCode host command ID is carried with - * IWL_TM_ATTR_UCODE_CMD_ID - * - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: - * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: - * commands from user applicaiton to access register - * - * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name - * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image - * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration - * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image - * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data - * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS - * commands fom user space for pure driver level operations - * - * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE: - * @IWL_TM_CMD_APP2DEV_END_TRACE: - * @IWL_TM_CMD_APP2DEV_READ_TRACE: - * commands fom user space for uCode trace operations - * - * @IWL_TM_CMD_DEV2APP_SYNC_RSP: - * commands from kernel space to carry the synchronous response - * to user application - * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT: - * commands from kernel space to multicast the spontaneous messages - * to user application, or reply of host commands - * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: - * commands from kernel space to carry the eeprom response - * to user application - * - * @IWL_TM_CMD_APP2DEV_OWNERSHIP: - * commands from user application to own change the ownership of the uCode - * if application has the ownership, the only host command from - * testmode will deliver to uCode. Default owner is driver - * - * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image - * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version - * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device - * @IWL_TM_CMD_APP2DEV_GET_FW_INFO: - * retrieve information of existing loaded uCode image - * - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: - * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - * Commands to read/write data from periphery or SRAM memory ranges. - * Fore reading, a READ command is sent from the userspace and the data - * is returned when the user calls a DUMP command. - * For writing, only a WRITE command is used. - * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS: - * Command to enable/disable notifications (currently RX packets) from the - * driver to userspace. - */ -enum iwl_tm_cmd_t { - IWL_TM_CMD_APP2DEV_UCODE = 1, - IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, - IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, - IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, - IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, - IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, - IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, - IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8, - IWL_TM_CMD_APP2DEV_GET_EEPROM = 9, - IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10, - IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11, - IWL_TM_CMD_APP2DEV_END_TRACE = 12, - IWL_TM_CMD_APP2DEV_READ_TRACE = 13, - IWL_TM_CMD_DEV2APP_SYNC_RSP = 14, - IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, - IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, - IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, - RESERVED_18 = 18, - RESERVED_19 = 19, - RESERVED_20 = 20, - RESERVED_21 = 21, - IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, - IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, - IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, - IWL_TM_CMD_APP2DEV_GET_FW_INFO = 25, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27, - IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28, - IWL_TM_CMD_APP2DEV_NOTIFICATIONS = 29, - IWL_TM_CMD_MAX = 30, -}; - -/* - * Atrribute filed in testmode command - * See enum iwl_tm_cmd_t. - * - * @IWL_TM_ATTR_NOT_APPLICABLE: - * The attribute is not applicable or invalid - * @IWL_TM_ATTR_COMMAND: - * From user space to kernel space: - * the command either destines to ucode, driver, or register; - * From kernel space to user space: - * the command either carries synchronous response, - * or the spontaneous message multicast from the device; - * - * @IWL_TM_ATTR_UCODE_CMD_ID: - * @IWL_TM_ATTR_UCODE_CMD_DATA: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE, - * The mandatory fields are : - * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID; - * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload - * to the ucode - * - * @IWL_TM_ATTR_REG_OFFSET: - * @IWL_TM_ATTR_REG_VALUE8: - * @IWL_TM_ATTR_REG_VALUE32: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX, - * The mandatory fields are: - * IWL_TM_ATTR_REG_OFFSET for the offset of the target register; - * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value - * - * @IWL_TM_ATTR_SYNC_RSP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP, - * The mandatory fields are: - * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user - * application command - * - * @IWL_TM_ATTR_UCODE_RX_PKT: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT, - * The mandatory fields are: - * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user - * application - * - * @IWL_TM_ATTR_EEPROM: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM, - * The mandatory fields are: - * IWL_TM_ATTR_EEPROM for the data content responging to the user - * application - * - * @IWL_TM_ATTR_TRACE_ADDR: - * @IWL_TM_ATTR_TRACE_SIZE: - * @IWL_TM_ATTR_TRACE_DUMP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE, - * The mandatory fields are: - * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address - * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size - * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump - * - * @IWL_TM_ATTR_FIXRATE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ, - * The mandatory fields are: - * IWL_TM_ATTR_FIXRATE for the fixed rate - * - * @IWL_TM_ATTR_UCODE_OWNER: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, - * The mandatory fields are: - * IWL_TM_ATTR_UCODE_OWNER for the new owner - * - * @IWL_TM_ATTR_MEM_ADDR: - * @IWL_TM_ATTR_BUFFER_SIZE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ - * or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE. - * The mandatory fields are: - * IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write - * IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write. - * - * @IWL_TM_ATTR_BUFFER_DUMP: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP, - * IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read. - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE, - * this attribute contains the data to write. - * - * @IWL_TM_ATTR_FW_VERSION: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, - * IWL_TM_ATTR_FW_VERSION for the uCode version - * - * @IWL_TM_ATTR_DEVICE_ID: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, - * IWL_TM_ATTR_DEVICE_ID for the device ID information - * - * @IWL_TM_ATTR_FW_TYPE: - * @IWL_TM_ATTR_FW_INST_SIZE: - * @IWL_TM_ATTR_FW_DATA_SIZE: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO, - * The mandatory fields are: - * IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...) - * IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section - * IWL_TM_ATTR_FW_DATA_SIZE for the size of data section - * - * @IWL_TM_ATTR_UCODE_CMD_SKB: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag - * indicates that the user wants to receive the response of the command - * in a reply SKB. If it's not present, the response is not returned. - * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS: - * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this - * flag enables (if present) or disables (if not) the forwarding - * to userspace. - */ -enum iwl_tm_attr_t { - IWL_TM_ATTR_NOT_APPLICABLE = 0, - IWL_TM_ATTR_COMMAND = 1, - IWL_TM_ATTR_UCODE_CMD_ID = 2, - IWL_TM_ATTR_UCODE_CMD_DATA = 3, - IWL_TM_ATTR_REG_OFFSET = 4, - IWL_TM_ATTR_REG_VALUE8 = 5, - IWL_TM_ATTR_REG_VALUE32 = 6, - IWL_TM_ATTR_SYNC_RSP = 7, - IWL_TM_ATTR_UCODE_RX_PKT = 8, - IWL_TM_ATTR_EEPROM = 9, - IWL_TM_ATTR_TRACE_ADDR = 10, - IWL_TM_ATTR_TRACE_SIZE = 11, - IWL_TM_ATTR_TRACE_DUMP = 12, - IWL_TM_ATTR_FIXRATE = 13, - IWL_TM_ATTR_UCODE_OWNER = 14, - IWL_TM_ATTR_MEM_ADDR = 15, - IWL_TM_ATTR_BUFFER_SIZE = 16, - IWL_TM_ATTR_BUFFER_DUMP = 17, - IWL_TM_ATTR_FW_VERSION = 18, - IWL_TM_ATTR_DEVICE_ID = 19, - IWL_TM_ATTR_FW_TYPE = 20, - IWL_TM_ATTR_FW_INST_SIZE = 21, - IWL_TM_ATTR_FW_DATA_SIZE = 22, - IWL_TM_ATTR_UCODE_CMD_SKB = 23, - IWL_TM_ATTR_ENABLE_NOTIFICATION = 24, - IWL_TM_ATTR_MAX = 25, -}; - -/* uCode trace buffer */ -#define TRACE_BUFF_SIZE_MAX 0x200000 -#define TRACE_BUFF_SIZE_MIN 0x20000 -#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN -#define TRACE_BUFF_PADD 0x2000 - -/* Maximum data size of each dump it packet */ -#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) - -/* Address offset of data segment in SRAM */ -#define SRAM_DATA_SEG_OFFSET 0x800000 - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c new file mode 100644 index 000000000000..76e18630f35d --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-test.c @@ -0,0 +1,825 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#include +#include "iwl-io.h" +#include "iwl-fh.h" +#include "iwl-prph.h" +#include "iwl-trans.h" +#include "iwl-test.h" +#include "iwl-csr.h" +#include "iwl-testmode.h" + +/* + * Periphery registers absolute lower bound. This is used in order to + * differentiate registery access through HBUS_TARG_PRPH_* and + * HBUS_TARG_MEM_* accesses. + */ +#define IWL_ABS_PRPH_START (0xA00000) + +/* + * The TLVs used in the gnl message policy between the kernel module and + * user space application. iwl_testmode_gnl_msg_policy is to be carried + * through the NL80211_CMD_TESTMODE channel regulated by nl80211. + * See iwl-testmode.h + */ +static +struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { + [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, }, + [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, }, + [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, }, + [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, }, + [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, }, + [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, }, + [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, + + [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, }, + [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, + [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, + [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, }, + + [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, }, +}; + +static inline void iwl_test_trace_clear(struct iwl_test *tst) +{ + memset(&tst->trace, 0, sizeof(struct iwl_test_trace)); +} + +static void iwl_test_trace_stop(struct iwl_test *tst) +{ + if (!tst->trace.enabled) + return; + + if (tst->trace.cpu_addr && tst->trace.dma_addr) + dma_free_coherent(tst->trans->dev, + tst->trace.tsize, + tst->trace.cpu_addr, + tst->trace.dma_addr); + + iwl_test_trace_clear(tst); +} + +static inline void iwl_test_mem_clear(struct iwl_test *tst) +{ + memset(&tst->mem, 0, sizeof(struct iwl_test_mem)); +} + +static inline void iwl_test_mem_stop(struct iwl_test *tst) +{ + if (!tst->mem.in_read) + return; + + iwl_test_mem_clear(tst); +} + +/* + * Initializes the test object + * During the lifetime of the test object it is assumed that the transport is + * started. The test object should be stopped before the transport is stopped. + */ +void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans, + struct iwl_test_ops *ops) +{ + tst->trans = trans; + tst->ops = ops; + + iwl_test_trace_clear(tst); + iwl_test_mem_clear(tst); +} +EXPORT_SYMBOL_GPL(iwl_test_init); + +/* + * Stop the test object + */ +void iwl_test_free(struct iwl_test *tst) +{ + iwl_test_mem_stop(tst); + iwl_test_trace_stop(tst); +} +EXPORT_SYMBOL_GPL(iwl_test_free); + +/* + * This function handles the user application commands to the fw. The fw + * commands are sent in a synchronuous manner. In case that the user requested + * to get commands response, it is send to the user. + */ +static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb) +{ + struct iwl_host_cmd cmd; + struct iwl_rx_packet *pkt; + struct sk_buff *skb; + void *reply_buf; + u32 reply_len; + int ret; + bool cmd_want_skb; + + memset(&cmd, 0, sizeof(struct iwl_host_cmd)); + + if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] || + !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) { + IWL_ERR(tst->trans, "Missing fw command mandatory fields\n"); + return -ENOMSG; + } + + cmd.flags = CMD_ON_DEMAND | CMD_SYNC; + cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]); + if (cmd_want_skb) + cmd.flags |= CMD_WANT_SKB; + + cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]); + cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); + cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]); + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n", + cmd.id, cmd.flags, cmd.len[0]); + + ret = tst->ops->send_cmd(tst->trans->op_mode, &cmd); + if (ret) { + IWL_ERR(tst->trans, "Failed to send hcmd\n"); + return ret; + } + if (!cmd_want_skb) + return ret; + + /* Handling return of SKB to the user */ + pkt = cmd.resp_pkt; + if (!pkt) { + IWL_ERR(tst->trans, "HCMD received a null response packet\n"); + return ret; + } + + reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20); + reply_buf = kmalloc(reply_len, GFP_KERNEL); + if (!skb || !reply_buf) { + kfree_skb(skb); + kfree(reply_buf); + return -ENOMEM; + } + + /* The reply is in a page, that we cannot send to user space. */ + memcpy(reply_buf, &(pkt->hdr), reply_len); + iwl_free_resp(&cmd); + + if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, + IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || + nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) + goto nla_put_failure; + return cfg80211_testmode_reply(skb); + +nla_put_failure: + IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n"); + kfree(reply_buf); + kfree_skb(skb); + return -ENOMSG; +} + +/* + * Handles the user application commands for register access. + */ +static int iwl_test_reg(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb) +{ + u32 ofs, val32, cmd; + u8 val8; + struct sk_buff *skb; + int status = 0; + struct iwl_trans *trans = tst->trans; + + if (!tb[IWL_TM_ATTR_REG_OFFSET]) { + IWL_ERR(trans, "Missing reg offset\n"); + return -ENOMSG; + } + + ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]); + IWL_DEBUG_INFO(trans, "test reg access cmd offset=0x%x\n", ofs); + + cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); + + /* + * Allow access only to FH/CSR/HBUS in direct mode. + * Since we don't have the upper bounds for the CSR and HBUS segments, + * we will use only the upper bound of FH for sanity check. + */ + if (ofs >= FH_MEM_UPPER_BOUND) { + IWL_ERR(trans, "offset out of segment (0x0 - 0x%x)\n", + FH_MEM_UPPER_BOUND); + return -EINVAL; + } + + switch (cmd) { + case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + val32 = iwl_read_direct32(tst->trans, ofs); + IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_ERR(trans, "Memory allocation fail\n"); + return -ENOMEM; + } + if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(trans, "Error sending msg : %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + if (!tb[IWL_TM_ATTR_REG_VALUE32]) { + IWL_ERR(trans, "Missing value to write\n"); + return -ENOMSG; + } else { + val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); + IWL_DEBUG_INFO(trans, "32b write val=0x%x\n", val32); + iwl_write_direct32(tst->trans, ofs, val32); + } + break; + + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + if (!tb[IWL_TM_ATTR_REG_VALUE8]) { + IWL_ERR(trans, "Missing value to write\n"); + return -ENOMSG; + } else { + val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]); + IWL_DEBUG_INFO(trans, "8b write val=0x%x\n", val8); + iwl_write8(tst->trans, ofs, val8); + } + break; + + default: + IWL_ERR(trans, "Unknown test register cmd ID\n"); + return -ENOMSG; + } + + return status; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +/* + * Handles the request to start FW tracing. Allocates of the trace buffer + * and sends a reply to user space with the address of the allocated buffer. + */ +static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb) +{ + struct sk_buff *skb; + int status = 0; + + if (tst->trace.enabled) + return -EBUSY; + + if (!tb[IWL_TM_ATTR_TRACE_SIZE]) + tst->trace.size = TRACE_BUFF_SIZE_DEF; + else + tst->trace.size = + nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]); + + if (!tst->trace.size) + return -EINVAL; + + if (tst->trace.size < TRACE_BUFF_SIZE_MIN || + tst->trace.size > TRACE_BUFF_SIZE_MAX) + return -EINVAL; + + tst->trace.tsize = tst->trace.size + TRACE_BUFF_PADD; + tst->trace.cpu_addr = dma_alloc_coherent(tst->trans->dev, + tst->trace.tsize, + &tst->trace.dma_addr, + GFP_KERNEL); + if (!tst->trace.cpu_addr) + return -ENOMEM; + + tst->trace.enabled = true; + tst->trace.trace_addr = (u8 *)PTR_ALIGN(tst->trace.cpu_addr, 0x100); + + memset(tst->trace.trace_addr, 0x03B, tst->trace.size); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, + sizeof(tst->trace.dma_addr) + 20); + + if (!skb) { + IWL_ERR(tst->trans, "Memory allocation fail\n"); + iwl_test_trace_stop(tst); + return -ENOMEM; + } + + if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR, + sizeof(tst->trace.dma_addr), + (u64 *)&tst->trace.dma_addr)) + goto nla_put_failure; + + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(tst->trans, "Error sending msg : %d\n", status); + + tst->trace.nchunks = DIV_ROUND_UP(tst->trace.size, + DUMP_CHUNK_SIZE); + + return status; + +nla_put_failure: + kfree_skb(skb); + if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) == + IWL_TM_CMD_APP2DEV_BEGIN_TRACE) + iwl_test_trace_stop(tst); + return -EMSGSIZE; +} + +/* + * Handles indirect read from the periphery or the SRAM. The read is performed + * to a temporary buffer. The user space application should later issue a dump + */ +static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size) +{ + struct iwl_trans *trans = tst->trans; + unsigned long flags; + int i; + + if (size & 0x3) + return -EINVAL; + + tst->mem.size = size; + tst->mem.addr = kmalloc(tst->mem.size, GFP_KERNEL); + if (tst->mem.addr == NULL) + return -ENOMEM; + + /* Hard-coded periphery absolute address */ + if (IWL_ABS_PRPH_START <= addr && + addr < IWL_ABS_PRPH_START + PRPH_END) { + spin_lock_irqsave(&trans->reg_lock, flags); + iwl_grab_nic_access(trans); + iwl_write32(trans, HBUS_TARG_PRPH_RADDR, + addr | (3 << 24)); + for (i = 0; i < size; i += 4) + *(u32 *)(tst->mem.addr + i) = + iwl_read32(trans, HBUS_TARG_PRPH_RDAT); + iwl_release_nic_access(trans); + spin_unlock_irqrestore(&trans->reg_lock, flags); + } else { /* target memory (SRAM) */ + _iwl_read_targ_mem_words(trans, addr, + tst->mem.addr, + tst->mem.size / 4); + } + + tst->mem.nchunks = + DIV_ROUND_UP(tst->mem.size, DUMP_CHUNK_SIZE); + tst->mem.in_read = true; + return 0; + +} + +/* + * Handles indirect write to the periphery or SRAM. The is performed to a + * temporary buffer. + */ +static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr, + u32 size, unsigned char *buf) +{ + struct iwl_trans *trans = tst->trans; + u32 val, i; + unsigned long flags; + + if (IWL_ABS_PRPH_START <= addr && + addr < IWL_ABS_PRPH_START + PRPH_END) { + /* Periphery writes can be 1-3 bytes long, or DWORDs */ + if (size < 4) { + memcpy(&val, buf, size); + spin_lock_irqsave(&trans->reg_lock, flags); + iwl_grab_nic_access(trans); + iwl_write32(trans, HBUS_TARG_PRPH_WADDR, + (addr & 0x0000FFFF) | + ((size - 1) << 24)); + iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); + iwl_release_nic_access(trans); + /* needed after consecutive writes w/o read */ + mmiowb(); + spin_unlock_irqrestore(&trans->reg_lock, flags); + } else { + if (size % 4) + return -EINVAL; + for (i = 0; i < size; i += 4) + iwl_write_prph(trans, addr+i, + *(u32 *)(buf+i)); + } + } else if (tst->ops->valid_hw_addr(addr)) { + _iwl_write_targ_mem_words(trans, addr, buf, size/4); + } else { + return -EINVAL; + } + return 0; +} + +/* + * Handles the user application commands for indirect read/write + * to/from the periphery or the SRAM. + */ +static int iwl_test_indirect_mem(struct iwl_test *tst, struct nlattr **tb) +{ + u32 addr, size, cmd; + unsigned char *buf; + + /* Both read and write should be blocked, for atomicity */ + if (tst->mem.in_read) + return -EBUSY; + + cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]); + if (!tb[IWL_TM_ATTR_MEM_ADDR]) { + IWL_ERR(tst->trans, "Error finding memory offset address\n"); + return -ENOMSG; + } + addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]); + if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) { + IWL_ERR(tst->trans, "Error finding size for memory reading\n"); + return -ENOMSG; + } + size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]); + + if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) { + return iwl_test_indirect_read(tst, addr, size); + } else { + if (!tb[IWL_TM_ATTR_BUFFER_DUMP]) + return -EINVAL; + buf = (unsigned char *)nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]); + return iwl_test_indirect_write(tst, addr, size, buf); + } +} + +/* + * Enable notifications to user space + */ +static int iwl_test_notifications(struct iwl_test *tst, + struct nlattr **tb) +{ + tst->notify = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]); + return 0; +} + +/* + * Handles the request to get the device id + */ +static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb) +{ + u32 devid = tst->trans->hw_id; + struct sk_buff *skb; + int status; + + IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_ERR(tst->trans, "Memory allocation fail\n"); + return -ENOMEM; + } + + if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) + goto nla_put_failure; + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(tst->trans, "Error sending msg : %d\n", status); + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +/* + * Handles the request to get the FW version + */ +static int iwl_test_get_fw_ver(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb) +{ + struct sk_buff *skb; + int status; + u32 ver = tst->ops->get_fw_ver(tst->trans->op_mode); + + IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_ERR(tst->trans, "Memory allocation fail\n"); + return -ENOMEM; + } + + if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver)) + goto nla_put_failure; + + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_ERR(tst->trans, "Error sending msg : %d\n", status); + + return 0; + +nla_put_failure: + kfree_skb(skb); + return -EMSGSIZE; +} + +/* + * Parse the netlink message and validate that the IWL_TM_ATTR_CMD exists + */ +int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, + void *data, int len) +{ + int result; + + result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len, + iwl_testmode_gnl_msg_policy); + if (result) { + IWL_ERR(tst->trans, "Fail parse gnl msg: %d\n", result); + return result; + } + + /* IWL_TM_ATTR_COMMAND is absolutely mandatory */ + if (!tb[IWL_TM_ATTR_COMMAND]) { + IWL_ERR(tst->trans, "Missing testmode command type\n"); + return -ENOMSG; + } + return 0; +} +EXPORT_SYMBOL_GPL(iwl_test_parse); + +/* + * Handle test commands. + * Returns 1 for unknown commands (not handled by the test object); negative + * value in case of error. + */ +int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb) +{ + int result; + + switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { + case IWL_TM_CMD_APP2DEV_UCODE: + IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n"); + result = iwl_test_fw_cmd(tst, hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + IWL_DEBUG_INFO(tst->trans, "test cmd to register\n"); + result = iwl_test_reg(tst, hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: + IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n"); + result = iwl_test_trace_begin(tst, hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_END_TRACE: + iwl_test_trace_stop(tst); + result = 0; + break; + + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: + IWL_DEBUG_INFO(tst->trans, "test indirect memory cmd\n"); + result = iwl_test_indirect_mem(tst, tb); + break; + + case IWL_TM_CMD_APP2DEV_NOTIFICATIONS: + IWL_DEBUG_INFO(tst->trans, "test notifications cmd\n"); + result = iwl_test_notifications(tst, tb); + break; + + case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: + IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n"); + result = iwl_test_get_fw_ver(tst, hw, tb); + break; + + case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: + IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n"); + result = iwl_test_get_dev_id(tst, hw, tb); + break; + + default: + IWL_DEBUG_INFO(tst->trans, "Unknown test command\n"); + result = 1; + break; + } + return result; +} +EXPORT_SYMBOL_GPL(iwl_test_handle_cmd); + +static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb, + struct netlink_callback *cb) +{ + int idx, length; + + if (!tst->trace.enabled || !tst->trace.trace_addr) + return -EFAULT; + + idx = cb->args[4]; + if (idx >= tst->trace.nchunks) + return -ENOENT; + + length = DUMP_CHUNK_SIZE; + if (((idx + 1) == tst->trace.nchunks) && + (tst->trace.size % DUMP_CHUNK_SIZE)) + length = tst->trace.size % + DUMP_CHUNK_SIZE; + + if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length, + tst->trace.trace_addr + (DUMP_CHUNK_SIZE * idx))) + goto nla_put_failure; + + cb->args[4] = ++idx; + return 0; + + nla_put_failure: + return -ENOBUFS; +} + +static int iwl_test_buffer_dump(struct iwl_test *tst, struct sk_buff *skb, + struct netlink_callback *cb) +{ + int idx, length; + + if (!tst->mem.in_read) + return -EFAULT; + + idx = cb->args[4]; + if (idx >= tst->mem.nchunks) { + iwl_test_mem_stop(tst); + return -ENOENT; + } + + length = DUMP_CHUNK_SIZE; + if (((idx + 1) == tst->mem.nchunks) && + (tst->mem.size % DUMP_CHUNK_SIZE)) + length = tst->mem.size % DUMP_CHUNK_SIZE; + + if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length, + tst->mem.addr + (DUMP_CHUNK_SIZE * idx))) + goto nla_put_failure; + + cb->args[4] = ++idx; + return 0; + + nla_put_failure: + return -ENOBUFS; +} + +/* + * Handle dump commands. + * Returns 1 for unknown commands (not handled by the test object); negative + * value in case of error. + */ +int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, + struct netlink_callback *cb) +{ + int result; + + switch (cmd) { + case IWL_TM_CMD_APP2DEV_READ_TRACE: + IWL_DEBUG_INFO(tst->trans, "uCode trace cmd\n"); + result = iwl_test_trace_dump(tst, skb, cb); + break; + + case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: + IWL_DEBUG_INFO(tst->trans, "testmode sram dump cmd\n"); + result = iwl_test_buffer_dump(tst, skb, cb); + break; + + default: + result = 1; + break; + } + return result; +} +EXPORT_SYMBOL_GPL(iwl_test_dump); + +/* + * Multicast a spontaneous messages from the device to the user space. + */ +static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw, + struct iwl_rx_cmd_buffer *rxb) +{ + struct sk_buff *skb; + struct iwl_rx_packet *data; + int length; + + data = rxb_addr(rxb); + length = le32_to_cpu(data->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + + /* the length doesn't include len_n_flags field, so add it manually */ + length += sizeof(__le32); + + skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length, + GFP_ATOMIC); + if (skb == NULL) { + IWL_ERR(tst->trans, "Out of memory for message to user\n"); + return; + } + + if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, + IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || + nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data)) + goto nla_put_failure; + + cfg80211_testmode_event(skb, GFP_ATOMIC); + return; + +nla_put_failure: + kfree_skb(skb); + IWL_ERR(tst->trans, "Ouch, overran buffer, check allocation!\n"); +} + +/* + * Called whenever a Rx frames is recevied from the device. If notifications to + * the user space are requested, sends the frames to the user. + */ +void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw, + struct iwl_rx_cmd_buffer *rxb) +{ + if (tst->notify) + iwl_test_send_rx(tst, hw, rxb); +} +EXPORT_SYMBOL_GPL(iwl_test_rx); diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h new file mode 100644 index 000000000000..994615344955 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-test.h @@ -0,0 +1,125 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __IWL_TEST_H__ +#define __IWL_TEST_H__ + +#include +#include "iwl-trans.h" + +struct iwl_test_trace { + u32 size; + u32 tsize; + u32 nchunks; + u8 *cpu_addr; + u8 *trace_addr; + dma_addr_t dma_addr; + bool enabled; +}; + +struct iwl_test_mem { + u32 size; + u32 nchunks; + u8 *addr; + bool in_read; +}; + +struct iwl_test_ops { + int (*send_cmd)(struct iwl_op_mode *op_modes, + struct iwl_host_cmd *cmd); + bool (*valid_hw_addr)(u32 addr); + u32 (*get_fw_ver)(struct iwl_op_mode *op_mode); +}; + +struct iwl_test { + struct iwl_trans *trans; + struct iwl_test_ops *ops; + struct iwl_test_trace trace; + struct iwl_test_mem mem; + bool notify; +}; + +void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans, + struct iwl_test_ops *ops); + +void iwl_test_free(struct iwl_test *tst); + +int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, + void *data, int len); + +int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, + struct nlattr **tb); + +int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, + struct netlink_callback *cb); + +void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw, + struct iwl_rx_cmd_buffer *rxb); + +static inline void iwl_test_enable_notifications(struct iwl_test *tst, + bool enable) +{ + tst->notify = enable; +} + +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h new file mode 100644 index 000000000000..6ba211b09426 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-testmode.h @@ -0,0 +1,309 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __IWL_TESTMODE_H__ +#define __IWL_TESTMODE_H__ + +#include + + +/* + * Commands from user space to kernel space(IWL_TM_CMD_ID_APP2DEV_XX) and + * from and kernel space to user space(IWL_TM_CMD_ID_DEV2APP_XX). + * The command ID is carried with IWL_TM_ATTR_COMMAND. + * + * @IWL_TM_CMD_APP2DEV_UCODE: + * commands from user application to the uCode, + * the actual uCode host command ID is carried with + * IWL_TM_ATTR_UCODE_CMD_ID + * + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + * commands from user applicaiton to access register + * + * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name + * @IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: load initial uCode image + * @IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: perform calibration + * @IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: load runtime uCode image + * @IWL_TM_CMD_APP2DEV_GET_EEPROM: request EEPROM data + * @IWL_TM_CMD_APP2DEV_FIXRATE_REQ: set fix MCS + * commands fom user space for pure driver level operations + * + * @IWL_TM_CMD_APP2DEV_BEGIN_TRACE: + * @IWL_TM_CMD_APP2DEV_END_TRACE: + * @IWL_TM_CMD_APP2DEV_READ_TRACE: + * commands fom user space for uCode trace operations + * + * @IWL_TM_CMD_DEV2APP_SYNC_RSP: + * commands from kernel space to carry the synchronous response + * to user application + * @IWL_TM_CMD_DEV2APP_UCODE_RX_PKT: + * commands from kernel space to multicast the spontaneous messages + * to user application, or reply of host commands + * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: + * commands from kernel space to carry the eeprom response + * to user application + * + * @IWL_TM_CMD_APP2DEV_OWNERSHIP: + * commands from user application to own change the ownership of the uCode + * if application has the ownership, the only host command from + * testmode will deliver to uCode. Default owner is driver + * + * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Wake On Wireless LAN uCode image + * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version + * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device + * @IWL_TM_CMD_APP2DEV_GET_FW_INFO: + * retrieve information of existing loaded uCode image + * + * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ: + * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP: + * @IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: + * Commands to read/write data from periphery or SRAM memory ranges. + * Fore reading, a READ command is sent from the userspace and the data + * is returned when the user calls a DUMP command. + * For writing, only a WRITE command is used. + * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS: + * Command to enable/disable notifications (currently RX packets) from the + * driver to userspace. + */ +enum iwl_tm_cmd_t { + IWL_TM_CMD_APP2DEV_UCODE = 1, + IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, + IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, + IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, + IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, + IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, + IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, + IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW = 8, + IWL_TM_CMD_APP2DEV_GET_EEPROM = 9, + IWL_TM_CMD_APP2DEV_FIXRATE_REQ = 10, + IWL_TM_CMD_APP2DEV_BEGIN_TRACE = 11, + IWL_TM_CMD_APP2DEV_END_TRACE = 12, + IWL_TM_CMD_APP2DEV_READ_TRACE = 13, + IWL_TM_CMD_DEV2APP_SYNC_RSP = 14, + IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, + IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, + IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, + RESERVED_18 = 18, + RESERVED_19 = 19, + RESERVED_20 = 20, + RESERVED_21 = 21, + IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, + IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, + IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, + IWL_TM_CMD_APP2DEV_GET_FW_INFO = 25, + IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26, + IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27, + IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28, + IWL_TM_CMD_APP2DEV_NOTIFICATIONS = 29, + IWL_TM_CMD_MAX = 30, +}; + +/* + * Atrribute filed in testmode command + * See enum iwl_tm_cmd_t. + * + * @IWL_TM_ATTR_NOT_APPLICABLE: + * The attribute is not applicable or invalid + * @IWL_TM_ATTR_COMMAND: + * From user space to kernel space: + * the command either destines to ucode, driver, or register; + * From kernel space to user space: + * the command either carries synchronous response, + * or the spontaneous message multicast from the device; + * + * @IWL_TM_ATTR_UCODE_CMD_ID: + * @IWL_TM_ATTR_UCODE_CMD_DATA: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE, + * The mandatory fields are : + * IWL_TM_ATTR_UCODE_CMD_ID for recognizable command ID; + * IWL_TM_ATTR_UCODE_CMD_DATA for the actual command payload + * to the ucode + * + * @IWL_TM_ATTR_REG_OFFSET: + * @IWL_TM_ATTR_REG_VALUE8: + * @IWL_TM_ATTR_REG_VALUE32: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_XXX, + * The mandatory fields are: + * IWL_TM_ATTR_REG_OFFSET for the offset of the target register; + * IWL_TM_ATTR_REG_VALUE8 or IWL_TM_ATTR_REG_VALUE32 for value + * + * @IWL_TM_ATTR_SYNC_RSP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_SYNC_RSP, + * The mandatory fields are: + * IWL_TM_ATTR_SYNC_RSP for the data content responding to the user + * application command + * + * @IWL_TM_ATTR_UCODE_RX_PKT: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_UCODE_RX_PKT, + * The mandatory fields are: + * IWL_TM_ATTR_UCODE_RX_PKT for the data content multicast to the user + * application + * + * @IWL_TM_ATTR_EEPROM: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_DEV2APP_EEPROM, + * The mandatory fields are: + * IWL_TM_ATTR_EEPROM for the data content responging to the user + * application + * + * @IWL_TM_ATTR_TRACE_ADDR: + * @IWL_TM_ATTR_TRACE_SIZE: + * @IWL_TM_ATTR_TRACE_DUMP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_XXX_TRACE, + * The mandatory fields are: + * IWL_TM_ATTR_MEM_TRACE_ADDR for the trace address + * IWL_TM_ATTR_MEM_TRACE_SIZE for the trace buffer size + * IWL_TM_ATTR_MEM_TRACE_DUMP for the trace dump + * + * @IWL_TM_ATTR_FIXRATE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_FIXRATE_REQ, + * The mandatory fields are: + * IWL_TM_ATTR_FIXRATE for the fixed rate + * + * @IWL_TM_ATTR_UCODE_OWNER: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, + * The mandatory fields are: + * IWL_TM_ATTR_UCODE_OWNER for the new owner + * + * @IWL_TM_ATTR_MEM_ADDR: + * @IWL_TM_ATTR_BUFFER_SIZE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ + * or IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE. + * The mandatory fields are: + * IWL_TM_ATTR_MEM_ADDR for the address in SRAM/periphery to read/write + * IWL_TM_ATTR_BUFFER_SIZE for the buffer size of data to read/write. + * + * @IWL_TM_ATTR_BUFFER_DUMP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP, + * IWL_TM_ATTR_BUFFER_DUMP is used for the data that was read. + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE, + * this attribute contains the data to write. + * + * @IWL_TM_ATTR_FW_VERSION: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, + * IWL_TM_ATTR_FW_VERSION for the uCode version + * + * @IWL_TM_ATTR_DEVICE_ID: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, + * IWL_TM_ATTR_DEVICE_ID for the device ID information + * + * @IWL_TM_ATTR_FW_TYPE: + * @IWL_TM_ATTR_FW_INST_SIZE: + * @IWL_TM_ATTR_FW_DATA_SIZE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_INFO, + * The mandatory fields are: + * IWL_TM_ATTR_FW_TYPE for the uCode type (INIT/RUNTIME/...) + * IWL_TM_ATTR_FW_INST_SIZE for the size of instruction section + * IWL_TM_ATTR_FW_DATA_SIZE for the size of data section + * + * @IWL_TM_ATTR_UCODE_CMD_SKB: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag + * indicates that the user wants to receive the response of the command + * in a reply SKB. If it's not present, the response is not returned. + * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this + * flag enables (if present) or disables (if not) the forwarding + * to userspace. + */ +enum iwl_tm_attr_t { + IWL_TM_ATTR_NOT_APPLICABLE = 0, + IWL_TM_ATTR_COMMAND = 1, + IWL_TM_ATTR_UCODE_CMD_ID = 2, + IWL_TM_ATTR_UCODE_CMD_DATA = 3, + IWL_TM_ATTR_REG_OFFSET = 4, + IWL_TM_ATTR_REG_VALUE8 = 5, + IWL_TM_ATTR_REG_VALUE32 = 6, + IWL_TM_ATTR_SYNC_RSP = 7, + IWL_TM_ATTR_UCODE_RX_PKT = 8, + IWL_TM_ATTR_EEPROM = 9, + IWL_TM_ATTR_TRACE_ADDR = 10, + IWL_TM_ATTR_TRACE_SIZE = 11, + IWL_TM_ATTR_TRACE_DUMP = 12, + IWL_TM_ATTR_FIXRATE = 13, + IWL_TM_ATTR_UCODE_OWNER = 14, + IWL_TM_ATTR_MEM_ADDR = 15, + IWL_TM_ATTR_BUFFER_SIZE = 16, + IWL_TM_ATTR_BUFFER_DUMP = 17, + IWL_TM_ATTR_FW_VERSION = 18, + IWL_TM_ATTR_DEVICE_ID = 19, + IWL_TM_ATTR_FW_TYPE = 20, + IWL_TM_ATTR_FW_INST_SIZE = 21, + IWL_TM_ATTR_FW_DATA_SIZE = 22, + IWL_TM_ATTR_UCODE_CMD_SKB = 23, + IWL_TM_ATTR_ENABLE_NOTIFICATION = 24, + IWL_TM_ATTR_MAX = 25, +}; + +/* uCode trace buffer */ +#define TRACE_BUFF_SIZE_MAX 0x200000 +#define TRACE_BUFF_SIZE_MIN 0x20000 +#define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN +#define TRACE_BUFF_PADD 0x2000 + +/* Maximum data size of each dump it packet */ +#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) + +/* Address offset of data segment in SRAM */ +#define SRAM_DATA_SEG_OFFSET 0x800000 + +#endif -- cgit v1.2.3 From c76fe6d19b8beffe792c390c0bd215d193512f1e Mon Sep 17 00:00:00 2001 From: Ilan Peer Date: Mon, 4 Jun 2012 19:39:30 +0300 Subject: iwlwifi: decouple testmode and iwl-test The iwl-test flows were based on the cfg80211 testmode APIs. To remove this coupling, the op mode (during the initialization of the iwl_test object) is responsible to set the callbacks that should be used by iwl-test to allocate skbs for events and replies and to send events and replies. The current op modes implement these callbacks based on the cfg80211 testmode APIs. Reviewed-by: Emmanuel Grumbach Signed-off-by: Ilan Peer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/rx.c | 2 +- drivers/net/wireless/iwlwifi/dvm/testmode.c | 31 +++++++- drivers/net/wireless/iwlwifi/iwl-test.c | 109 ++++++++++++++++++---------- drivers/net/wireless/iwlwifi/iwl-test.h | 44 ++++++++++- 4 files changed, 141 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index afdacb25f344..c1f7a18e08dd 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -1143,7 +1143,7 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, * Note that if the ownership flag != IWL_OWNERSHIP_TM the flow * continues. */ - iwl_test_rx(&priv->tst, priv->hw, rxb); + iwl_test_rx(&priv->tst, rxb); #endif if (priv->ucode_owner != IWL_OWNERSHIP_TM) { diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c index aa9518f13e89..57b918ce3b5f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/testmode.c +++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c @@ -103,10 +103,39 @@ static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode) return priv->fw->ucode_ver; } +static struct sk_buff* +iwl_testmode_alloc_reply(struct iwl_op_mode *op_mode, int len) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + return cfg80211_testmode_alloc_reply_skb(priv->hw->wiphy, len); +} + +static int iwl_testmode_reply(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + return cfg80211_testmode_reply(skb); +} + +static struct sk_buff *iwl_testmode_alloc_event(struct iwl_op_mode *op_mode, + int len) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + return cfg80211_testmode_alloc_event_skb(priv->hw->wiphy, len, + GFP_ATOMIC); +} + +static void iwl_testmode_event(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + return cfg80211_testmode_event(skb, GFP_ATOMIC); +} + static struct iwl_test_ops tst_ops = { .send_cmd = iwl_testmode_send_cmd, .valid_hw_addr = iwl_testmode_valid_hw_addr, .get_fw_ver = iwl_testmode_get_fw_ver, + .alloc_reply = iwl_testmode_alloc_reply, + .reply = iwl_testmode_reply, + .alloc_event = iwl_testmode_alloc_event, + .event = iwl_testmode_event, }; void iwl_testmode_init(struct iwl_priv *priv) @@ -380,7 +409,7 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE: - result = iwl_test_handle_cmd(&priv->tst, hw, tb); + result = iwl_test_handle_cmd(&priv->tst, tb); break; case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c index 76e18630f35d..7a264aee2534 100644 --- a/drivers/net/wireless/iwlwifi/iwl-test.c +++ b/drivers/net/wireless/iwlwifi/iwl-test.c @@ -61,7 +61,9 @@ * *****************************************************************************/ +#include #include + #include "iwl-io.h" #include "iwl-fh.h" #include "iwl-prph.h" @@ -178,13 +180,51 @@ void iwl_test_free(struct iwl_test *tst) } EXPORT_SYMBOL_GPL(iwl_test_free); +static inline int iwl_test_send_cmd(struct iwl_test *tst, + struct iwl_host_cmd *cmd) +{ + return tst->ops->send_cmd(tst->trans->op_mode, cmd); +} + +static inline bool iwl_test_valid_hw_addr(struct iwl_test *tst, u32 addr) +{ + return tst->ops->valid_hw_addr(addr); +} + +static inline u32 iwl_test_fw_ver(struct iwl_test *tst) +{ + return tst->ops->get_fw_ver(tst->trans->op_mode); +} + +static inline struct sk_buff* +iwl_test_alloc_reply(struct iwl_test *tst, int len) +{ + return tst->ops->alloc_reply(tst->trans->op_mode, len); +} + +static inline int iwl_test_reply(struct iwl_test *tst, struct sk_buff *skb) +{ + return tst->ops->reply(tst->trans->op_mode, skb); +} + +static inline struct sk_buff* +iwl_test_alloc_event(struct iwl_test *tst, int len) +{ + return tst->ops->alloc_event(tst->trans->op_mode, len); +} + +static inline void +iwl_test_event(struct iwl_test *tst, struct sk_buff *skb) +{ + return tst->ops->event(tst->trans->op_mode, skb); +} + /* * This function handles the user application commands to the fw. The fw * commands are sent in a synchronuous manner. In case that the user requested * to get commands response, it is send to the user. */ -static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb) +static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb) { struct iwl_host_cmd cmd; struct iwl_rx_packet *pkt; @@ -214,7 +254,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n", cmd.id, cmd.flags, cmd.len[0]); - ret = tst->ops->send_cmd(tst->trans->op_mode, &cmd); + ret = iwl_test_send_cmd(tst, &cmd); if (ret) { IWL_ERR(tst->trans, "Failed to send hcmd\n"); return ret; @@ -230,7 +270,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, } reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20); + skb = iwl_test_alloc_reply(tst, reply_len + 20); reply_buf = kmalloc(reply_len, GFP_KERNEL); if (!skb || !reply_buf) { kfree_skb(skb); @@ -246,7 +286,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) || nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf)) goto nla_put_failure; - return cfg80211_testmode_reply(skb); + return iwl_test_reply(tst, skb); nla_put_failure: IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n"); @@ -258,8 +298,7 @@ nla_put_failure: /* * Handles the user application commands for register access. */ -static int iwl_test_reg(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb) +static int iwl_test_reg(struct iwl_test *tst, struct nlattr **tb) { u32 ofs, val32, cmd; u8 val8; @@ -293,14 +332,14 @@ static int iwl_test_reg(struct iwl_test *tst, struct ieee80211_hw *hw, val32 = iwl_read_direct32(tst->trans, ofs); IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + skb = iwl_test_alloc_reply(tst, 20); if (!skb) { IWL_ERR(trans, "Memory allocation fail\n"); return -ENOMEM; } if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32)) goto nla_put_failure; - status = cfg80211_testmode_reply(skb); + status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(trans, "Error sending msg : %d\n", status); break; @@ -343,8 +382,7 @@ nla_put_failure: * Handles the request to start FW tracing. Allocates of the trace buffer * and sends a reply to user space with the address of the allocated buffer. */ -static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb) +static int iwl_test_trace_begin(struct iwl_test *tst, struct nlattr **tb) { struct sk_buff *skb; int status = 0; @@ -378,9 +416,7 @@ static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw, memset(tst->trace.trace_addr, 0x03B, tst->trace.size); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - sizeof(tst->trace.dma_addr) + 20); - + skb = iwl_test_alloc_reply(tst, sizeof(tst->trace.dma_addr) + 20); if (!skb) { IWL_ERR(tst->trans, "Memory allocation fail\n"); iwl_test_trace_stop(tst); @@ -392,7 +428,7 @@ static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw, (u64 *)&tst->trace.dma_addr)) goto nla_put_failure; - status = cfg80211_testmode_reply(skb); + status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(tst->trans, "Error sending msg : %d\n", status); @@ -485,7 +521,7 @@ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr, iwl_write_prph(trans, addr+i, *(u32 *)(buf+i)); } - } else if (tst->ops->valid_hw_addr(addr)) { + } else if (iwl_test_valid_hw_addr(tst, addr)) { _iwl_write_targ_mem_words(trans, addr, buf, size/4); } else { return -EINVAL; @@ -541,8 +577,7 @@ static int iwl_test_notifications(struct iwl_test *tst, /* * Handles the request to get the device id */ -static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb) +static int iwl_test_get_dev_id(struct iwl_test *tst, struct nlattr **tb) { u32 devid = tst->trans->hw_id; struct sk_buff *skb; @@ -550,7 +585,7 @@ static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw, IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + skb = iwl_test_alloc_reply(tst, 20); if (!skb) { IWL_ERR(tst->trans, "Memory allocation fail\n"); return -ENOMEM; @@ -558,7 +593,7 @@ static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw, if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid)) goto nla_put_failure; - status = cfg80211_testmode_reply(skb); + status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(tst->trans, "Error sending msg : %d\n", status); @@ -572,16 +607,15 @@ nla_put_failure: /* * Handles the request to get the FW version */ -static int iwl_test_get_fw_ver(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb) +static int iwl_test_get_fw_ver(struct iwl_test *tst, struct nlattr **tb) { struct sk_buff *skb; int status; - u32 ver = tst->ops->get_fw_ver(tst->trans->op_mode); + u32 ver = iwl_test_fw_ver(tst); IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver); - skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + skb = iwl_test_alloc_reply(tst, 20); if (!skb) { IWL_ERR(tst->trans, "Memory allocation fail\n"); return -ENOMEM; @@ -590,7 +624,7 @@ static int iwl_test_get_fw_ver(struct iwl_test *tst, struct ieee80211_hw *hw, if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver)) goto nla_put_failure; - status = cfg80211_testmode_reply(skb); + status = iwl_test_reply(tst, skb); if (status < 0) IWL_ERR(tst->trans, "Error sending msg : %d\n", status); @@ -630,27 +664,26 @@ EXPORT_SYMBOL_GPL(iwl_test_parse); * Returns 1 for unknown commands (not handled by the test object); negative * value in case of error. */ -int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb) +int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb) { int result; switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { case IWL_TM_CMD_APP2DEV_UCODE: IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n"); - result = iwl_test_fw_cmd(tst, hw, tb); + result = iwl_test_fw_cmd(tst, tb); break; case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: IWL_DEBUG_INFO(tst->trans, "test cmd to register\n"); - result = iwl_test_reg(tst, hw, tb); + result = iwl_test_reg(tst, tb); break; case IWL_TM_CMD_APP2DEV_BEGIN_TRACE: IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n"); - result = iwl_test_trace_begin(tst, hw, tb); + result = iwl_test_trace_begin(tst, tb); break; case IWL_TM_CMD_APP2DEV_END_TRACE: @@ -671,12 +704,12 @@ int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n"); - result = iwl_test_get_fw_ver(tst, hw, tb); + result = iwl_test_get_fw_ver(tst, tb); break; case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n"); - result = iwl_test_get_dev_id(tst, hw, tb); + result = iwl_test_get_dev_id(tst, tb); break; default: @@ -779,7 +812,7 @@ EXPORT_SYMBOL_GPL(iwl_test_dump); /* * Multicast a spontaneous messages from the device to the user space. */ -static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw, +static void iwl_test_send_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb) { struct sk_buff *skb; @@ -792,8 +825,7 @@ static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw, /* the length doesn't include len_n_flags field, so add it manually */ length += sizeof(__le32); - skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length, - GFP_ATOMIC); + skb = iwl_test_alloc_event(tst, length + 20); if (skb == NULL) { IWL_ERR(tst->trans, "Out of memory for message to user\n"); return; @@ -804,7 +836,7 @@ static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw, nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data)) goto nla_put_failure; - cfg80211_testmode_event(skb, GFP_ATOMIC); + iwl_test_event(tst, skb); return; nla_put_failure: @@ -816,10 +848,9 @@ nla_put_failure: * Called whenever a Rx frames is recevied from the device. If notifications to * the user space are requested, sends the frames to the user. */ -void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw, - struct iwl_rx_cmd_buffer *rxb) +void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb) { if (tst->notify) - iwl_test_send_rx(tst, hw, rxb); + iwl_test_send_rx(tst, rxb); } EXPORT_SYMBOL_GPL(iwl_test_rx); diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h index 994615344955..e13ffa8acc02 100644 --- a/drivers/net/wireless/iwlwifi/iwl-test.h +++ b/drivers/net/wireless/iwlwifi/iwl-test.h @@ -84,11 +84,49 @@ struct iwl_test_mem { bool in_read; }; +/* + * struct iwl_test_ops: callback to the op mode + * + * The structure defines the callbacks that the op_mode should handle, + * inorder to handle logic that is out of the scope of iwl_test. The + * op_mode must set all the callbacks. + + * @send_cmd: handler that is used by the test object to request the + * op_mode to send a command to the fw. + * + * @valid_hw_addr: handler that is used by the test object to request the + * op_mode to check if the given address is a valid address. + * + * @get_fw_ver: handler used to get the FW version. + * + * @alloc_reply: handler used by the test object to request the op_mode + * to allocate an skb for sending a reply to the user, and initialize + * the skb. It is assumed that the test object only fills the required + * attributes. + * + * @reply: handler used by the test object to request the op_mode to reply + * to a request. The skb is an skb previously allocated by the the + * alloc_reply callback. + I + * @alloc_event: handler used by the test object to request the op_mode + * to allocate an skb for sending an event, and initialize + * the skb. It is assumed that the test object only fills the required + * attributes. + * + * @reply: handler used by the test object to request the op_mode to send + * an event. The skb is an skb previously allocated by the the + * alloc_event callback. + */ struct iwl_test_ops { int (*send_cmd)(struct iwl_op_mode *op_modes, struct iwl_host_cmd *cmd); bool (*valid_hw_addr)(u32 addr); u32 (*get_fw_ver)(struct iwl_op_mode *op_mode); + + struct sk_buff *(*alloc_reply)(struct iwl_op_mode *op_mode, int len); + int (*reply)(struct iwl_op_mode *op_mode, struct sk_buff *skb); + struct sk_buff* (*alloc_event)(struct iwl_op_mode *op_mode, int len); + void (*event)(struct iwl_op_mode *op_mode, struct sk_buff *skb); }; struct iwl_test { @@ -107,14 +145,12 @@ void iwl_test_free(struct iwl_test *tst); int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb, void *data, int len); -int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw, - struct nlattr **tb); +int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb); int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb, struct netlink_callback *cb); -void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw, - struct iwl_rx_cmd_buffer *rxb); +void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb); static inline void iwl_test_enable_notifications(struct iwl_test *tst, bool enable) -- cgit v1.2.3 From f1ff32e8a8672d4d59283fb7f61f1431c2b9e434 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 30 May 2012 01:58:39 -0700 Subject: ath6kl: Add missing newline terminations Messages without newlines can be interleaved. Avoid this by adding terminations. Signed-off-by: Joe Perches Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 6f20998beceb..d2129030fd73 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1001,7 +1001,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, WMI_FRAME_PROBE_REQ, request->ie, request->ie_len); if (ret) { - ath6kl_err("failed to set Probe Request appie for scan"); + ath6kl_err("failed to set Probe Request appie for scan\n"); return ret; } @@ -3288,7 +3288,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, WMI_FRAME_PROBE_REQ, request->ie, request->ie_len); if (ret) { - ath6kl_warn("Failed to set probe request IE for scheduled scan: %d", + ath6kl_warn("Failed to set probe request IE for scheduled scan: %d\n", ret); return ret; } -- cgit v1.2.3 From d154f32ebe3ffe9dea6ed0a91767883b1e7a6bc0 Mon Sep 17 00:00:00 2001 From: Etay Luz Date: Wed, 30 May 2012 11:35:08 +0300 Subject: ath6kl: support changing dtim period on AP mode This patch adds support for dtim_period configuration in beacon. kvalo: add a comment about ignoring the error, use vif_idx, add \n to the warning message Signed-off-by: Etay Luz Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 8 +++++++- drivers/net/wireless/ath/ath6kl/wmi.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 5 +++++ 3 files changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index d2129030fd73..f00d377343d9 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2783,9 +2783,15 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, /* TODO: * info->interval - * info->dtim_period */ + ret = ath6kl_wmi_ap_set_dtim_cmd(ar->wmi, vif->fw_vif_idx, + info->dtim_period); + + /* ignore error, just print a warning and continue normally */ + if (ret) + ath6kl_warn("Failed to set dtim_period in beacon: %d\n", ret); + if (info->beacon.head == NULL) return -EINVAL; mgmt = (struct ieee80211_mgmt *) info->beacon.head; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 43bce9c8a86f..a6caa673e8ad 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -752,6 +752,22 @@ int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid) NO_SYNC_WMIFLAG); } +int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period) +{ + struct sk_buff *skb; + struct set_dtim_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct set_dtim_cmd *) skb->data; + + cmd->dtim_period = cpu_to_le32(dtim_period); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_AP_SET_DTIM_CMDID, NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode) { struct sk_buff *skb; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 7c94fe3e9e6d..43339aca585d 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -1617,6 +1617,10 @@ struct roam_ctrl_cmd { u8 roam_ctrl; } __packed; +struct set_dtim_cmd { + __le32 dtim_period; +} __packed; + /* BSS INFO HDR version 2.0 */ struct wmi_bss_info_hdr2 { __le16 ch; /* frequency in MHz */ @@ -2589,6 +2593,7 @@ int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); +int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period); int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); int ath6kl_wmi_set_roam_mode_cmd(struct wmi *wmi, enum wmi_roam_mode mode); int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); -- cgit v1.2.3 From 0faf745872f6d00afb318185e8fb181587974b5a Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 30 May 2012 12:27:11 +0530 Subject: ath6kl: Fix race in aggregation reorder logic There are many places where tid data are accessed without the lock (rxtid->lock), this can lead to a race condition when the timeout handler for aggregatin reorder and the receive function are getting executed at the same time. Fix this race, but still there are races which can not be fixed without rewriting the whole aggregation reorder logic, for now fix the obvious ones. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 12 +++++++++--- drivers/net/wireless/ath/ath6kl/txrx.c | 12 +++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 17a44fad859b..12441f7d9036 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -273,9 +273,15 @@ struct rxtid { struct sk_buff_head q; /* - * FIXME: No clue what this should protect. Apparently it should - * protect some of the fields above but they are also accessed - * without taking the lock. + * lock mainly protects seq_next and hold_q. Movement of seq_next + * needs to be protected between aggr_timeout() and + * aggr_process_recv_frm(). hold_q will be holding the pending + * reorder frames and it's access should also be protected. + * Some of the other fields like hold_q_sz, win_sz and aggr are + * initialized/reset when receiving addba/delba req, also while + * deleting aggr state all the pending buffers are flushed before + * resetting these fields, so there should not be any race in accessing + * these fields. */ spinlock_t lock; }; diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 67206aedea6c..974c51053a71 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1036,6 +1036,7 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, rxtid = &agg_conn->rx_tid[tid]; stats = &agg_conn->stat[tid]; + spin_lock_bh(&rxtid->lock); idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); /* @@ -1054,8 +1055,6 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, seq_end = seq_no ? seq_no : rxtid->seq_next; idx_end = AGGR_WIN_IDX(seq_end, rxtid->hold_q_sz); - spin_lock_bh(&rxtid->lock); - do { node = &rxtid->hold_q[idx]; if ((order == 1) && (!node->skb)) @@ -1127,11 +1126,13 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, ((end > extended_end) && (cur > extended_end) && (cur < end))) { aggr_deque_frms(agg_conn, tid, 0, 0); + spin_lock_bh(&rxtid->lock); if (cur >= rxtid->hold_q_sz - 1) rxtid->seq_next = cur - (rxtid->hold_q_sz - 1); else rxtid->seq_next = ATH6KL_MAX_SEQ_NO - (rxtid->hold_q_sz - 2 - cur); + spin_unlock_bh(&rxtid->lock); } else { /* * Dequeue only those frames that are outside the @@ -1186,7 +1187,8 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, if (agg_conn->timer_scheduled) rxtid->progress = true; - else + else { + spin_lock_bh(&rxtid->lock); for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { if (rxtid->hold_q[idx].skb) { /* @@ -1204,6 +1206,8 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, break; } } + spin_unlock_bh(&rxtid->lock); + } return is_queued; } @@ -1626,6 +1630,7 @@ static void aggr_timeout(unsigned long arg) rxtid = &aggr_conn->rx_tid[i]; if (rxtid->aggr && rxtid->hold_q) { + spin_lock_bh(&rxtid->lock); for (j = 0; j < rxtid->hold_q_sz; j++) { if (rxtid->hold_q[j].skb) { aggr_conn->timer_scheduled = true; @@ -1634,6 +1639,7 @@ static void aggr_timeout(unsigned long arg) break; } } + spin_unlock_bh(&rxtid->lock); if (j >= rxtid->hold_q_sz) rxtid->timer_mon = false; -- cgit v1.2.3 From 7940bad50859026c0f11d2b7766571175b5e55c3 Mon Sep 17 00:00:00 2001 From: Vasanthakumar Thiagarajan Date: Wed, 30 May 2012 12:27:12 +0530 Subject: ath6kl: Fix unstable downlink throughput There is frequent downlink throughput drop to 0 when operating at the signal level between -42dBm to -53dBm. This has been root caused to the delay in releasing pending a-mpdu subframes in reorder buffer. Right now the timeout value is 400ms, there is also a race condition where timeout handler can be delayed to run at an extra timeout interval. This patch reduces the timout interval to reasonable 100ms and makes sure releasing pending frames are not skipped in the timeout handler by removing the flag (rxtid->progress) which can delay the timeout logic. Reported-by: Yu Yanzhi Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 3 +-- drivers/net/wireless/ath/ath6kl/txrx.c | 42 +++++++++++++++------------------- 2 files changed, 19 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 12441f7d9036..b3eee10cf016 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -215,7 +215,7 @@ enum ath6kl_hw_flags { #define AGGR_NUM_OF_FREE_NETBUFS 16 -#define AGGR_RX_TIMEOUT 400 /* in ms */ +#define AGGR_RX_TIMEOUT 100 /* in ms */ #define WMI_TIMEOUT (2 * HZ) @@ -264,7 +264,6 @@ struct skb_hold_q { struct rxtid { bool aggr; - bool progress; bool timer_mon; u16 win_sz; u16 seq_next; diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 974c51053a71..7dfa0fd86d7b 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1186,28 +1186,25 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, aggr_deque_frms(agg_conn, tid, 0, 1); if (agg_conn->timer_scheduled) - rxtid->progress = true; - else { - spin_lock_bh(&rxtid->lock); - for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { - if (rxtid->hold_q[idx].skb) { - /* - * There is a frame in the queue and no - * timer so start a timer to ensure that - * the frame doesn't remain stuck - * forever. - */ - agg_conn->timer_scheduled = true; - mod_timer(&agg_conn->timer, - (jiffies + - HZ * (AGGR_RX_TIMEOUT) / 1000)); - rxtid->progress = false; - rxtid->timer_mon = true; - break; - } + return is_queued; + + spin_lock_bh(&rxtid->lock); + for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { + if (rxtid->hold_q[idx].skb) { + /* + * There is a frame in the queue and no + * timer so start a timer to ensure that + * the frame doesn't remain stuck + * forever. + */ + agg_conn->timer_scheduled = true; + mod_timer(&agg_conn->timer, + (jiffies + (HZ * AGGR_RX_TIMEOUT) / 1000)); + rxtid->timer_mon = true; + break; } - spin_unlock_bh(&rxtid->lock); } + spin_unlock_bh(&rxtid->lock); return is_queued; } @@ -1612,7 +1609,7 @@ static void aggr_timeout(unsigned long arg) rxtid = &aggr_conn->rx_tid[i]; stats = &aggr_conn->stat[i]; - if (!rxtid->aggr || !rxtid->timer_mon || rxtid->progress) + if (!rxtid->aggr || !rxtid->timer_mon) continue; stats->num_timeouts++; @@ -1635,7 +1632,6 @@ static void aggr_timeout(unsigned long arg) if (rxtid->hold_q[j].skb) { aggr_conn->timer_scheduled = true; rxtid->timer_mon = true; - rxtid->progress = false; break; } } @@ -1666,7 +1662,6 @@ static void aggr_delete_tid_state(struct aggr_info_conn *aggr_conn, u8 tid) aggr_deque_frms(aggr_conn, tid, 0, 0); rxtid->aggr = false; - rxtid->progress = false; rxtid->timer_mon = false; rxtid->win_sz = 0; rxtid->seq_next = 0; @@ -1745,7 +1740,6 @@ void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info, for (i = 0; i < NUM_OF_TIDS; i++) { rxtid = &aggr_conn->rx_tid[i]; rxtid->aggr = false; - rxtid->progress = false; rxtid->timer_mon = false; skb_queue_head_init(&rxtid->q); spin_lock_init(&rxtid->lock); -- cgit v1.2.3 From b5b6f6a9a07ac230d54a85a9fb9e691c85f2eb0a Mon Sep 17 00:00:00 2001 From: Naveen Singh Date: Thu, 7 Jun 2012 00:44:02 -0700 Subject: ath6kl: use firmware version from FW IE Need to have different FW versioning for different FW binaries. This is handled by appending different meta data in firmware binaries. kvalo: add an empty line before a debug message, use '0' instead of '0x00', fix indentation Signed-off-by: Naveen Singh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 8 ++++++++ drivers/net/wireless/ath/ath6kl/main.c | 22 +++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index daf24ee9d28a..62e25ccd1016 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -943,6 +943,14 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) } switch (ie_id) { + case ATH6KL_FW_IE_FW_VERSION: + strlcpy(ar->wiphy->fw_version, data, + sizeof(ar->wiphy->fw_version)); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "found fw version %s\n", + ar->wiphy->fw_version); + break; case ATH6KL_FW_IE_OTP_IMAGE: ath6kl_dbg(ATH6KL_DBG_BOOT, "found otp image ie (%zd B)\n", ie_len); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 3e6768ae80af..45621baca24a 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -554,20 +554,24 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver, struct ath6kl *ar = devt; memcpy(ar->mac_addr, datap, ETH_ALEN); - ath6kl_dbg(ATH6KL_DBG_TRC, "%s: mac addr = %pM\n", - __func__, ar->mac_addr); + + ath6kl_dbg(ATH6KL_DBG_BOOT, + "ready event mac addr %pM sw_ver 0x%x abi_ver 0x%x cap 0x%x\n", + ar->mac_addr, sw_ver, abi_ver, cap); ar->version.wlan_ver = sw_ver; ar->version.abi_ver = abi_ver; ar->hw.cap = cap; - snprintf(ar->wiphy->fw_version, - sizeof(ar->wiphy->fw_version), - "%u.%u.%u.%u", - (ar->version.wlan_ver & 0xf0000000) >> 28, - (ar->version.wlan_ver & 0x0f000000) >> 24, - (ar->version.wlan_ver & 0x00ff0000) >> 16, - (ar->version.wlan_ver & 0x0000ffff)); + if (strlen(ar->wiphy->fw_version) == 0) { + snprintf(ar->wiphy->fw_version, + sizeof(ar->wiphy->fw_version), + "%u.%u.%u.%u", + (ar->version.wlan_ver & 0xf0000000) >> 28, + (ar->version.wlan_ver & 0x0f000000) >> 24, + (ar->version.wlan_ver & 0x00ff0000) >> 16, + (ar->version.wlan_ver & 0x0000ffff)); + } /* indicate to the waiting thread that the ready event was received */ set_bit(WMI_READY, &ar->flag); -- cgit v1.2.3 From 109cdbc223f6e2d6c80f8371f22415b50c18a366 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:19 -0600 Subject: PCI: remove pci_bus_find_ext_capability() (unused) pci_bus_find_ext_capability() is unused, and this patch removes it. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..de9386da2eb2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -329,49 +329,6 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) } EXPORT_SYMBOL_GPL(pci_find_ext_capability); -/** - * pci_bus_find_ext_capability - find an extended capability - * @bus: the PCI bus to query - * @devfn: PCI device to query - * @cap: capability code - * - * Like pci_find_ext_capability() but works for pci devices that do not have a - * pci_dev structure set up yet. - * - * Returns the address of the requested capability structure within the - * device's PCI configuration space or 0 in case the device does not - * support it. - */ -int pci_bus_find_ext_capability(struct pci_bus *bus, unsigned int devfn, - int cap) -{ - u32 header; - int ttl; - int pos = PCI_CFG_SPACE_SIZE; - - /* minimum 8 bytes per capability */ - ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - return 0; - if (header == 0xffffffff || header == 0) - return 0; - - while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CFG_SPACE_SIZE) - break; - - if (!pci_bus_read_config_dword(bus, devfn, pos, &header)) - break; - } - - return 0; -} - static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) { int rc, ttl = PCI_FIND_CAP_TTL; -- cgit v1.2.3 From 533b6608b73669309becd90f11f939b60bb221be Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:34 -0600 Subject: PCI: remove pci_max_busnr() (was already commented out) pci_max_busnr() has been commented out for years (since 54c762fe62), and this patch removes it completely. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index de9386da2eb2..2cc53acad26a 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -136,30 +136,6 @@ void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) EXPORT_SYMBOL_GPL(pci_ioremap_bar); #endif -#if 0 -/** - * pci_max_busnr - returns maximum PCI bus number - * - * Returns the highest PCI bus number present in the system global list of - * PCI buses. - */ -unsigned char __devinit -pci_max_busnr(void) -{ - struct pci_bus *bus = NULL; - unsigned char max, n; - - max = 0; - while ((bus = pci_find_next_bus(bus)) != NULL) { - n = pci_bus_max_busnr(bus); - if(n > max) - max = n; - } - return max; -} - -#endif /* 0 */ - #define PCI_FIND_CAP_TTL 48 static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, -- cgit v1.2.3 From 505cf30b7f4ef64c6db36f34adbe4a7ad9081fd3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 18 May 2012 16:52:40 -0600 Subject: PCI/AER: use pci_is_pcie() instead of obsolete pci_dev.is_pcie Use pci_is_pcie() instead of looking at obsolete is_pcie field in struct pci_dev. CC: Huang Ying CC: Kenji Kaneshige Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aer/aerdrv_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 275bf158ffa7..124f20ff11b2 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -59,7 +59,7 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data) p = (struct acpi_hest_aer_common *)(hest_hdr + 1); if (p->flags & ACPI_HEST_GLOBAL) { - if ((info->pci_dev->is_pcie && + if ((pci_is_pcie(info->pci_dev) && info->pci_dev->pcie_type == pcie_type) || bridge) ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); } else -- cgit v1.2.3 From dec195dc41bd678a5626cca5d6c14ee338b5df77 Mon Sep 17 00:00:00 2001 From: "hdoyu@nvidia.com" Date: Mon, 14 May 2012 06:21:18 +0000 Subject: amba: tegra-ahb: Remove empty *_remove() Remove unnecessary empty function. Signed-off-by: Hiroshi DOYU Signed-off-by: Stephen Warren --- drivers/amba/tegra-ahb.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/amba/tegra-ahb.c b/drivers/amba/tegra-ahb.c index aa0b1f160528..0b6f0b28a487 100644 --- a/drivers/amba/tegra-ahb.c +++ b/drivers/amba/tegra-ahb.c @@ -264,11 +264,6 @@ static int __devinit tegra_ahb_probe(struct platform_device *pdev) return 0; } -static int __devexit tegra_ahb_remove(struct platform_device *pdev) -{ - return 0; -} - static const struct of_device_id tegra_ahb_of_match[] __devinitconst = { { .compatible = "nvidia,tegra30-ahb", }, { .compatible = "nvidia,tegra20-ahb", }, @@ -277,7 +272,6 @@ static const struct of_device_id tegra_ahb_of_match[] __devinitconst = { static struct platform_driver tegra_ahb_driver = { .probe = tegra_ahb_probe, - .remove = __devexit_p(tegra_ahb_remove), .driver = { .name = DRV_NAME, .owner = THIS_MODULE, -- cgit v1.2.3 From c11bd557903395f436d63bb6dfb0f4b86ea365d5 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 21 May 2012 16:43:42 -0600 Subject: mmc: tegra: use bus-width property instead of support-8bit Update the driver to parse the new unified bus-width property introduced in commit 7f21779 "mmc: dt: Consolidate DT bindings", instead of the legacy support-8bit property. Signed-off-by: Stephen Warren Acked-by: Chris Ball --- drivers/mmc/host/sdhci-tegra.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b38d8a78f6a0..6e5338a071ce 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -223,6 +223,7 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( { struct tegra_sdhci_platform_data *plat; struct device_node *np = pdev->dev.of_node; + u32 bus_width; if (!np) return NULL; @@ -236,7 +237,9 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0); - if (of_find_property(np, "support-8bit", NULL)) + + if (of_property_read_u32(np, "bus-width", &bus_width) == 0 && + bus_width == 8) plat->is_8bit = 1; return plat; -- cgit v1.2.3 From 6f786aad108ba5479d93410fb47e0f51a79f34b1 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:36 +0530 Subject: i2c: tegra: Add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: w.sang@pengutronix.de Cc: linux-i2c@vger.kernel.org Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren --- drivers/i2c/busses/i2c-tegra.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 8b2e555a9563..3da7ee3eb505 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -341,7 +341,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) u32 val; int err = 0; - clk_enable(i2c_dev->clk); + clk_prepare_enable(i2c_dev->clk); tegra_periph_reset_assert(i2c_dev->clk); udelay(2); @@ -372,7 +372,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) if (tegra_i2c_flush_fifos(i2c_dev)) err = -ETIMEDOUT; - clk_disable(i2c_dev->clk); + clk_disable_unprepare(i2c_dev->clk); if (i2c_dev->irq_disabled) { i2c_dev->irq_disabled = 0; @@ -546,14 +546,14 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (i2c_dev->is_suspended) return -EBUSY; - clk_enable(i2c_dev->clk); + clk_prepare_enable(i2c_dev->clk); for (i = 0; i < num; i++) { int stop = (i == (num - 1)) ? 1 : 0; ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], stop); if (ret) break; } - clk_disable(i2c_dev->clk); + clk_disable_unprepare(i2c_dev->clk); return ret ?: i; } @@ -666,7 +666,7 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev) goto err_free; } - clk_enable(i2c_dev->i2c_clk); + clk_prepare_enable(i2c_dev->i2c_clk); i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); i2c_dev->adapter.owner = THIS_MODULE; -- cgit v1.2.3 From 1e674bc6689e9aca5bd4355d1f87093636ed1c02 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:37 +0530 Subject: mmc: tegra: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: cjb@laptop.org Cc: linux-mmc@vger.kernel.org Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren --- drivers/mmc/host/sdhci-tegra.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b38d8a78f6a0..278965695f00 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -334,7 +334,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) rc = PTR_ERR(clk); goto err_clk_get; } - clk_enable(clk); + clk_prepare_enable(clk); pltfm_host->clk = clk; host->mmc->pm_caps = plat->pm_flags; @@ -349,7 +349,7 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) return 0; err_add_host: - clk_disable(pltfm_host->clk); + clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); err_clk_get: if (gpio_is_valid(plat->wp_gpio)) @@ -390,7 +390,7 @@ static int __devexit sdhci_tegra_remove(struct platform_device *pdev) if (gpio_is_valid(plat->power_gpio)) gpio_free(plat->power_gpio); - clk_disable(pltfm_host->clk); + clk_disable_unprepare(pltfm_host->clk); clk_put(pltfm_host->clk); sdhci_pltfm_free(pdev); -- cgit v1.2.3 From 20de12ccf900d71eee1344adc7ae0dcd7db9b4ab Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:38 +0530 Subject: USB: ehci-tegra: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: stern@rowland.harvard.edu Cc: gregkh@linuxfoundation.org Cc: linux-usb@vger.kernel.org Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren --- drivers/usb/host/ehci-tegra.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 68548236ec42..ab8a3bf628e3 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -46,8 +46,8 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd) { struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); - clk_enable(tegra->emc_clk); - clk_enable(tegra->clk); + clk_prepare_enable(tegra->emc_clk); + clk_prepare_enable(tegra->clk); tegra_usb_phy_power_on(tegra->phy); tegra->host_resumed = 1; } @@ -58,8 +58,8 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) tegra->host_resumed = 0; tegra_usb_phy_power_off(tegra->phy); - clk_disable(tegra->clk); - clk_disable(tegra->emc_clk); + clk_disable_unprepare(tegra->clk); + clk_disable_unprepare(tegra->emc_clk); } static int tegra_ehci_internal_port_reset( @@ -671,7 +671,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_clk; } - err = clk_enable(tegra->clk); + err = clk_prepare_enable(tegra->clk); if (err) goto fail_clken; @@ -682,7 +682,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) goto fail_emc_clk; } - clk_enable(tegra->emc_clk); + clk_prepare_enable(tegra->emc_clk); clk_set_rate(tegra->emc_clk, 400000000); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -782,10 +782,10 @@ fail: fail_phy: iounmap(hcd->regs); fail_io: - clk_disable(tegra->emc_clk); + clk_disable_unprepare(tegra->emc_clk); clk_put(tegra->emc_clk); fail_emc_clk: - clk_disable(tegra->clk); + clk_disable_unprepare(tegra->clk); fail_clken: clk_put(tegra->clk); fail_clk: @@ -820,10 +820,10 @@ static int tegra_ehci_remove(struct platform_device *pdev) tegra_usb_phy_close(tegra->phy); iounmap(hcd->regs); - clk_disable(tegra->clk); + clk_disable_unprepare(tegra->clk); clk_put(tegra->clk); - clk_disable(tegra->emc_clk); + clk_disable_unprepare(tegra->emc_clk); clk_put(tegra->emc_clk); kfree(tegra); -- cgit v1.2.3 From 38e8b7d977786b0059c02744e4b96bc1e206d891 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Wed, 6 Jun 2012 21:12:40 -0700 Subject: mwifiex: shorten per channel scan time Currently the scan time per channel for active scanning is set to 200ms. It takes quite a while to finsh scanning on all channels, especially with a dual band configuration. Change the per channel scan time settings to the following values: passive scan: 110ms active scan: 30ms specific scan: 30ms Above settings have been tested on x86 and arm platforms. Signed-off-by: Bing Zhao Signed-off-by: Amitkumar Karwar Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index bd3b0bf94b9e..cbad00d7eb11 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -79,9 +79,9 @@ enum { #define SCAN_BEACON_ENTRY_PAD 6 -#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 200 -#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 200 -#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 110 +#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110 +#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30 +#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30 #define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) -- cgit v1.2.3 From 3249ba7376caa93af387d8e6b5e41b290934f88c Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 6 Jun 2012 21:12:41 -0700 Subject: mwifiex: fix simultaneous scan and Tx traffic problem If scan operation is started when Tx traffic is already running, driver locks Tx queue until it gets completed. With this logic there is a delay for Tx packets. This patch implements new approach to give Tx path higher priority in this case. Driver internally sends multiple synchronous scan commands to firmware when scan is requested by user. Now we will make sure that Tx queue is empty everytime before sending next scan command. If Tx queue isn't empty scan command will be postponsed by 20msec. This rule will be followed until Tx queue becomes empty or timeout of 1 second happens. In case of timeout scan operation will be aborted. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 65 +++++++++++++++++++++++++++++++++++++ drivers/net/wireless/mwifiex/main.c | 4 +-- drivers/net/wireless/mwifiex/main.h | 5 +++ drivers/net/wireless/mwifiex/scan.c | 25 +++++++++----- 4 files changed, 89 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index c1cb004db913..0f18ef6a30c8 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -57,6 +57,68 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) return 0; } +static void scan_delay_timer_fn(unsigned long data) +{ + struct mwifiex_private *priv = (struct mwifiex_private *)data; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + + if (!mwifiex_wmm_lists_empty(adapter)) { + if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { + /* + * Abort scan operation by cancelling all pending scan + * command + */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, + list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, + flags); + + if (priv->user_scan_cfg) { + dev_dbg(priv->adapter->dev, + "info: %s: scan aborted\n", __func__); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + kfree(priv->user_scan_cfg); + priv->user_scan_cfg = NULL; + } + } else { + /* + * Tx data queue is still not empty, delay scan + * operation further by 20msec. + */ + mod_timer(&priv->scan_delay_timer, jiffies + + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); + adapter->scan_delay_cnt++; + } + } else { + /* + * Tx data queue is empty. Get scan command from scan_pending_q + * and put to cmd_pending_q to resume scan operation + */ + adapter->scan_delay_cnt = 0; + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + } +} + /* * This function initializes the private structure and sets default * values to the members. @@ -136,6 +198,9 @@ static int mwifiex_init_priv(struct mwifiex_private *priv) priv->scan_block = false; + setup_timer(&priv->scan_delay_timer, scan_delay_timer_fn, + (unsigned long)priv); + return mwifiex_add_bss_prio_tbl(priv); } diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 3192855c31c0..0f06f07a70e6 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -244,8 +244,8 @@ process_start: } } - if (!adapter->scan_processing && !adapter->data_sent && - !mwifiex_wmm_lists_empty(adapter)) { + if ((!adapter->scan_processing || adapter->scan_delay_cnt) && + !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter)) { mwifiex_wmm_process_tx(adapter); if (adapter->hs_activated) { adapter->is_hs_configured = false; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index cbad00d7eb11..5b32221077c4 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -87,6 +87,9 @@ enum { #define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) +#define MWIFIEX_MAX_SCAN_DELAY_CNT 50 +#define MWIFIEX_SCAN_DELAY_MSEC 20 + #define RSN_GTK_OUI_OFFSET 2 #define MWIFIEX_OUI_NOT_PRESENT 0 @@ -482,6 +485,7 @@ struct mwifiex_private { u16 proberesp_idx; u16 assocresp_idx; u16 rsn_idx; + struct timer_list scan_delay_timer; }; enum mwifiex_ba_status { @@ -686,6 +690,7 @@ struct mwifiex_adapter { struct completion fw_load; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; u16 max_mgmt_ie_index; + u8 scan_delay_cnt; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 74f045715723..ea2f1bdef8a2 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1772,14 +1772,23 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, priv->user_scan_cfg = NULL; } } else { - /* Get scan command from scan_pending_q and put to - cmd_pending_q */ - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + if (!mwifiex_wmm_lists_empty(adapter)) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + adapter->scan_delay_cnt = 1; + mod_timer(&priv->scan_delay_timer, jiffies + + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); + } else { + /* Get scan command from scan_pending_q and put to + cmd_pending_q */ + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + } } done: -- cgit v1.2.3 From 658f37b736ce335edcdf1425228e8652ec6adf24 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 6 Jun 2012 21:12:42 -0700 Subject: mwifiex: scan less channels per scan command to improve Tx traffic Currently 4 channels are scanned per scan command. if scan request is issued by user during Tx traffic, radio will be out of channel for "4 * per_chan_scan_time" for each scan command and will not be able to receive Rx packets. This adds delay in data traffic. We can minimize it by reducing number of channels scanned per scan command in this scenario. We can not always scan 1 channel per scan command due to limitation of number of command buffers. So we add code to decide number of channels scanned per scan command in associated state. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/scan.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index ea2f1bdef8a2..efaf26ccd6ba 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -28,7 +28,10 @@ /* The maximum number of channels the firmware can scan per command */ #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 -#define MWIFIEX_CHANNELS_PER_SCAN_CMD 4 +#define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD 4 +#define MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD 15 +#define MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD 27 +#define MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD 35 /* Memory needed to store a max sized Channel List TLV for a firmware scan */ #define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ @@ -471,7 +474,7 @@ mwifiex_is_network_compatible(struct mwifiex_private *priv, * This routine is used for any scan that is not provided with a * specific channel list to scan. */ -static void +static int mwifiex_scan_create_channel_list(struct mwifiex_private *priv, const struct mwifiex_user_scan_cfg *user_scan_in, @@ -528,6 +531,7 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, } } + return chan_idx; } /* @@ -727,6 +731,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, u32 num_probes; u32 ssid_len; u32 chan_idx; + u32 chan_num; u32 scan_type; u16 scan_dur; u8 channel; @@ -850,7 +855,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, if (*filtered_scan) *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; else - *max_chan_per_scan = MWIFIEX_CHANNELS_PER_SCAN_CMD; + *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; /* If the input config or adapter has the number of Probes set, add tlv */ @@ -962,13 +967,28 @@ mwifiex_config_scan(struct mwifiex_private *priv, dev_dbg(adapter->dev, "info: Scan: Scanning current channel only\n"); } - + chan_num = chan_idx; } else { dev_dbg(adapter->dev, "info: Scan: Creating full region channel list\n"); - mwifiex_scan_create_channel_list(priv, user_scan_in, - scan_chan_list, - *filtered_scan); + chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, + scan_chan_list, + *filtered_scan); + } + + /* + * In associated state we will reduce the number of channels scanned per + * scan command to avoid any traffic delay/loss. This number is decided + * based on total number of channels to be scanned due to constraints + * of command buffers. + */ + if (priv->media_connected) { + if (chan_num < MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD) + *max_chan_per_scan = 1; + else if (chan_num < MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD) + *max_chan_per_scan = 2; + else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD) + *max_chan_per_scan = 3; } } -- cgit v1.2.3 From cba63e99e58231ecd665b0eddc9b73c65fe9c17c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Fri, 8 Jun 2012 13:24:55 +0530 Subject: ath9k_hw: Initvals update for AR9462 MSI is enabled by default for most of the 4th generation chips. Add this for AR9462 - this fixes PowerSave operation, the chip was not entering Network-Sleep mode earlier. With proper powering down of the MAC now, power consumption in associated state is reduced considerably. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index a10ece0cb6cd..4a93e1534c1d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -1007,6 +1007,7 @@ static const u32 ar9462_2p0_radio_core[][2] = { static const u32 ar9462_2p0_soc_preamble[][2] = { /* Addr allmodes */ + {0x000040a4 ,0x00a0c1c9}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, -- cgit v1.2.3 From 7f0d9f430dc99303558adc30a75eef10c43f7bec Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Sun, 10 Jun 2012 22:11:56 +0200 Subject: ssb: add missing PCI ID for b/g/n single band BCM4322 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 14e4:432c is found on some bcm63xx devices. The device is working fine with b43. Reported-by: Álvaro Fernández Rojas Signed-off-by: Jonas Gorski Signed-off-by: John W. Linville --- drivers/ssb/b43_pci_bridge.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/ssb/b43_pci_bridge.c b/drivers/ssb/b43_pci_bridge.c index f551e5376147..266aa1648a02 100644 --- a/drivers/ssb/b43_pci_bridge.c +++ b/drivers/ssb/b43_pci_bridge.c @@ -36,6 +36,7 @@ static const struct pci_device_id b43_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4328) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4329) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x432b) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x432c) }, { 0, }, }; MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl); -- cgit v1.2.3 From 8d242488ce4627dd7e6333caab56df11ea25e239 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 9 Jun 2012 07:49:07 +0000 Subject: phy: Use pr_ Use a more current logging style. Add pr_fmt and missing newlines. Remove embedded prefixes. Neaten phy_print_status to avoid using KERN_CONT. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- drivers/net/phy/dp83640.c | 23 +++++++++++++---------- drivers/net/phy/fixed.c | 4 ++-- drivers/net/phy/mdio_bus.c | 5 ++++- drivers/net/phy/national.c | 8 ++++++-- drivers/net/phy/phy.c | 35 +++++++++++++++++------------------ drivers/net/phy/phy_device.c | 7 +++++-- drivers/net/phy/spi_ks8995.c | 4 +++- 7 files changed, 50 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index 940b29022d0c..b0da0226661f 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -17,6 +17,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -453,16 +456,16 @@ static void enable_status_frames(struct phy_device *phydev, bool on) ext_write(0, phydev, PAGE6, PSF_CFG1, ver); if (!phydev->attached_dev) { - pr_warning("dp83640: expected to find an attached netdevice\n"); + pr_warn("expected to find an attached netdevice\n"); return; } if (on) { if (dev_mc_add(phydev->attached_dev, status_frame_dst)) - pr_warning("dp83640: failed to add mc address\n"); + pr_warn("failed to add mc address\n"); } else { if (dev_mc_del(phydev->attached_dev, status_frame_dst)) - pr_warning("dp83640: failed to delete mc address\n"); + pr_warn("failed to delete mc address\n"); } } @@ -582,9 +585,9 @@ static void recalibrate(struct dp83640_clock *clock) * read out and correct offsets */ val = ext_read(master, PAGE4, PTP_STS); - pr_info("master PTP_STS 0x%04hx", val); + pr_info("master PTP_STS 0x%04hx\n", val); val = ext_read(master, PAGE4, PTP_ESTS); - pr_info("master PTP_ESTS 0x%04hx", val); + pr_info("master PTP_ESTS 0x%04hx\n", val); event_ts.ns_lo = ext_read(master, PAGE4, PTP_EDATA); event_ts.ns_hi = ext_read(master, PAGE4, PTP_EDATA); event_ts.sec_lo = ext_read(master, PAGE4, PTP_EDATA); @@ -594,9 +597,9 @@ static void recalibrate(struct dp83640_clock *clock) list_for_each(this, &clock->phylist) { tmp = list_entry(this, struct dp83640_private, list); val = ext_read(tmp->phydev, PAGE4, PTP_STS); - pr_info("slave PTP_STS 0x%04hx", val); + pr_info("slave PTP_STS 0x%04hx\n", val); val = ext_read(tmp->phydev, PAGE4, PTP_ESTS); - pr_info("slave PTP_ESTS 0x%04hx", val); + pr_info("slave PTP_ESTS 0x%04hx\n", val); event_ts.ns_lo = ext_read(tmp->phydev, PAGE4, PTP_EDATA); event_ts.ns_hi = ext_read(tmp->phydev, PAGE4, PTP_EDATA); event_ts.sec_lo = ext_read(tmp->phydev, PAGE4, PTP_EDATA); @@ -686,7 +689,7 @@ static void decode_rxts(struct dp83640_private *dp83640, prune_rx_ts(dp83640); if (list_empty(&dp83640->rxpool)) { - pr_debug("dp83640: rx timestamp pool is empty\n"); + pr_debug("rx timestamp pool is empty\n"); goto out; } rxts = list_first_entry(&dp83640->rxpool, struct rxts, list); @@ -709,7 +712,7 @@ static void decode_txts(struct dp83640_private *dp83640, skb = skb_dequeue(&dp83640->tx_queue); if (!skb) { - pr_debug("dp83640: have timestamp but tx_queue empty\n"); + pr_debug("have timestamp but tx_queue empty\n"); return; } ns = phy2txts(phy_txts); @@ -847,7 +850,7 @@ static void dp83640_free_clocks(void) list_for_each_safe(this, next, &phyter_clocks) { clock = list_entry(this, struct dp83640_clock, list); if (!list_empty(&clock->phylist)) { - pr_warning("phy list non-empty while unloading"); + pr_warn("phy list non-empty while unloading\n"); BUG(); } list_del(&clock->list); diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 633680d0828e..ba55adfc7aae 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -70,7 +70,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) lpa |= LPA_10FULL; break; default: - printk(KERN_WARNING "fixed phy: unknown speed\n"); + pr_warn("fixed phy: unknown speed\n"); return -EINVAL; } } else { @@ -90,7 +90,7 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) lpa |= LPA_10HALF; break; default: - printk(KERN_WARNING "fixed phy: unknown speed\n"); + pr_warn("fixed phy: unknown speed\n"); return -EINVAL; } } diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 683ef1ce5519..d4a4992b4935 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -13,6 +13,9 @@ * option) any later version. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -148,7 +151,7 @@ int mdiobus_register(struct mii_bus *bus) err = device_register(&bus->dev); if (err) { - printk(KERN_ERR "mii_bus %s failed to register\n", bus->id); + pr_err("mii_bus %s failed to register\n", bus->id); return -EINVAL; } diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c index 04bb8fcc0cb5..9a5f234d95b0 100644 --- a/drivers/net/phy/national.c +++ b/drivers/net/phy/national.c @@ -15,6 +15,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -22,6 +24,8 @@ #include #include +#define DEBUG + /* DP83865 phy identifier values */ #define DP83865_PHY_ID 0x20005c7a @@ -112,8 +116,8 @@ static void ns_10_base_t_hdx_loopack(struct phy_device *phydev, int disable) ns_exp_write(phydev, 0x1c0, ns_exp_read(phydev, 0x1c0) & 0xfffe); - printk(KERN_DEBUG "DP83865 PHY: 10BASE-T HDX loopback %s\n", - (ns_exp_read(phydev, 0x1c0) & 0x0001) ? "off" : "on"); + pr_debug("10BASE-T HDX loopback %s\n", + (ns_exp_read(phydev, 0x1c0) & 0x0001) ? "off" : "on"); } static int ns_config_init(struct phy_device *phydev) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 3cbda0851f83..2e1c23731ded 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -15,6 +15,9 @@ * option) any later version. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -44,18 +47,16 @@ */ void phy_print_status(struct phy_device *phydev) { - pr_info("PHY: %s - Link is %s", dev_name(&phydev->dev), - phydev->link ? "Up" : "Down"); if (phydev->link) - printk(KERN_CONT " - %d/%s", phydev->speed, - DUPLEX_FULL == phydev->duplex ? - "Full" : "Half"); - - printk(KERN_CONT "\n"); + pr_info("%s - Link is Up - %d/%s\n", + dev_name(&phydev->dev), + phydev->speed, + DUPLEX_FULL == phydev->duplex ? "Full" : "Half"); + else + pr_info("%s - Link is Down\n", dev_name(&phydev->dev)); } EXPORT_SYMBOL(phy_print_status); - /** * phy_clear_interrupt - Ack the phy device's interrupt * @phydev: the phy_device struct @@ -482,9 +483,8 @@ static void phy_force_reduction(struct phy_device *phydev) phydev->speed = settings[idx].speed; phydev->duplex = settings[idx].duplex; - pr_info("Trying %d/%s\n", phydev->speed, - DUPLEX_FULL == phydev->duplex ? - "FULL" : "HALF"); + pr_info("Trying %d/%s\n", + phydev->speed, DUPLEX_FULL == phydev->duplex ? "FULL" : "HALF"); } @@ -598,9 +598,8 @@ int phy_start_interrupts(struct phy_device *phydev) IRQF_SHARED, "phy_interrupt", phydev) < 0) { - printk(KERN_WARNING "%s: Can't get IRQ %d (PHY)\n", - phydev->bus->name, - phydev->irq); + pr_warn("%s: Can't get IRQ %d (PHY)\n", + phydev->bus->name, phydev->irq); phydev->irq = PHY_POLL; return 0; } @@ -838,10 +837,10 @@ void phy_state_machine(struct work_struct *work) phydev->autoneg = AUTONEG_DISABLE; - pr_info("Trying %d/%s\n", phydev->speed, - DUPLEX_FULL == - phydev->duplex ? - "FULL" : "HALF"); + pr_info("Trying %d/%s\n", + phydev->speed, + DUPLEX_FULL == phydev->duplex ? + "FULL" : "HALF"); } break; case PHY_NOLINK: diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index de86a5582224..18ab0daf4490 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -14,6 +14,9 @@ * option) any later version. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -975,8 +978,8 @@ int phy_driver_register(struct phy_driver *new_driver) retval = driver_register(&new_driver->driver); if (retval) { - printk(KERN_ERR "%s: Error %d in registering driver\n", - new_driver->name, retval); + pr_err("%s: Error %d in registering driver\n", + new_driver->name, retval); return retval; } diff --git a/drivers/net/phy/spi_ks8995.c b/drivers/net/phy/spi_ks8995.c index 4eb98bc52a0a..1c3abce78b6a 100644 --- a/drivers/net/phy/spi_ks8995.c +++ b/drivers/net/phy/spi_ks8995.c @@ -11,6 +11,8 @@ * by the Free Software Foundation. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -356,7 +358,7 @@ static struct spi_driver ks8995_driver = { static int __init ks8995_init(void) { - printk(KERN_INFO DRV_DESC " version " DRV_VERSION"\n"); + pr_info(DRV_DESC " version " DRV_VERSION "\n"); return spi_register_driver(&ks8995_driver); } -- cgit v1.2.3 From 12ea6cad1c7d046e21decc18b0e2170c6794dc51 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:26:55 +0000 Subject: PCI: add PCI DMA source ID quirk DMA transactions are tagged with the source ID of the device making the request. Occasionally hardware screws this up and uses the source ID of a different device (often the wrong function number of a multifunction device). A specific Ricoh multifunction device is a prime example of this problem and included in this patch. Given a pci_dev, this function returns the pci_dev to use as the source ID for DMA. When hardware works correctly, this returns the input device. For the components of the Ricoh multifunction device, it returns the pci_dev for function 0. This will be used by IOMMU drivers for determining the boundaries of IOMMU groups as multiple devices using the same source ID must be contained within the same group. This can also be used by existing streaming DMA paths for the same purpose. [bhelgaas: fold in pci_dev_get() for !CONFIG_PCI] Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..acd3956b44bd 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3179,3 +3179,54 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe) return -ENOTTY; } + +static struct pci_dev *pci_func_0_dma_source(struct pci_dev *dev) +{ + if (!PCI_FUNC(dev->devfn)) + return pci_dev_get(dev); + + return pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); +} + +static const struct pci_dev_dma_source { + u16 vendor; + u16 device; + struct pci_dev *(*dma_source)(struct pci_dev *dev); +} pci_dev_dma_source[] = { + /* + * https://bugzilla.redhat.com/show_bug.cgi?id=605888 + * + * Some Ricoh devices use the function 0 source ID for DMA on + * other functions of a multifunction device. The DMA devices + * is therefore function 0, which will have implications of the + * iommu grouping of these devices. + */ + { PCI_VENDOR_ID_RICOH, 0xe822, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe230, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe832, pci_func_0_dma_source }, + { PCI_VENDOR_ID_RICOH, 0xe476, pci_func_0_dma_source }, + { 0 } +}; + +/* + * IOMMUs with isolation capabilities need to be programmed with the + * correct source ID of a device. In most cases, the source ID matches + * the device doing the DMA, but sometimes hardware is broken and will + * tag the DMA as being sourced from a different device. This function + * allows that translation. Note that the reference count of the + * returned device is incremented on all paths. + */ +struct pci_dev *pci_get_dma_source(struct pci_dev *dev) +{ + const struct pci_dev_dma_source *i; + + for (i = pci_dev_dma_source; i->dma_source; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) + return i->dma_source(dev); + } + + return pci_dev_get(dev); +} -- cgit v1.2.3 From c32823f82b42abc1f08b365085862fd1d57c0b61 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:25 -0600 Subject: PCI: make pci_ltr_supported() static The PCI Express Latency Tolerance Reporting (LTR) feature's pci_ltr_supported() routine is currently only used within drivers/pci/pci.c so make it static. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..847e0c35cdb7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2169,7 +2169,7 @@ EXPORT_SYMBOL(pci_disable_obff); * RETURNS: * True if @dev supports latency tolerance reporting, false otherwise. */ -bool pci_ltr_supported(struct pci_dev *dev) +static bool pci_ltr_supported(struct pci_dev *dev) { int pos; u32 cap; @@ -2185,7 +2185,6 @@ bool pci_ltr_supported(struct pci_dev *dev) return cap & PCI_EXP_DEVCAP2_LTR; } -EXPORT_SYMBOL(pci_ltr_supported); /** * pci_enable_ltr - enable latency tolerance reporting -- cgit v1.2.3 From cb97ae3485955401d637bd269b0d24d3cd3fd3ec Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:31 -0600 Subject: PCI: remove redundant checking in PCI Express capability routines There are a number of redundant pci_is_pcie() checks in various PCI Express capabilities related routines like the following: if (!pci_is_pcie(dev)) return false; pos = pci_pcie_cap(dev); if (!pos) return false; The current pci_is_pcie() implementation is merely: static inline bool pci_is_pcie(struct pci_dev *dev) { return !!pci_pcie_cap(dev); } so we can just drop the pci_is_pcie() test in such cases. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 847e0c35cdb7..766bb13bb0a3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1994,7 +1994,7 @@ void pci_enable_ari(struct pci_dev *dev) return; bridge = dev->bus->self; - if (!bridge || !pci_is_pcie(bridge)) + if (!bridge) return; pos = pci_pcie_cap(bridge); @@ -2054,9 +2054,6 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - if (!pci_is_pcie(dev)) - return; - pos = pci_pcie_cap(dev); if (!pos) return; @@ -2096,9 +2093,6 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) u16 ctrl; int ret; - if (!pci_is_pcie(dev)) - return -ENOTSUPP; - pos = pci_pcie_cap(dev); if (!pos) return -ENOTSUPP; @@ -2149,9 +2143,6 @@ void pci_disable_obff(struct pci_dev *dev) int pos; u16 ctrl; - if (!pci_is_pcie(dev)) - return; - pos = pci_pcie_cap(dev); if (!pos) return; @@ -2174,9 +2165,6 @@ static bool pci_ltr_supported(struct pci_dev *dev) int pos; u32 cap; - if (!pci_is_pcie(dev)) - return false; - pos = pci_pcie_cap(dev); if (!pos) return false; -- cgit v1.2.3 From c463b8cb9350cf1230cefe467a1cf279140a5437 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:37 -0600 Subject: PCI: add pci_pcie_cap2() check for PCIe feature capabilities >= v2 This patch resolves potential issues when accessing PCI Express Capability structures. The makeup of the capability varies substantially between v1 and v2: Version 1 of the PCI Express Capability (defined by PCI Express 1.0 and 1.1 base) neither requires the endpoint to implement the entire PCIe capability structure nor specifies default values of registers that are not implemented by the device. Version 2 of the PCI Express Capability (defined by PCIe 1.1 Capability Structure Expansion ECN, PCIe 2.0, 2.1, and 3.0) added additional registers to the structure and requires all registers to be either implemented or hardwired to 0. Due to the differences in the capability structures, code dealing with capability features must be careful not to access the additional registers introduced with v2 unless the device is specifically known to be a v2 capable device. Otherwise, attempts to access non-existant registers will occur. This is a subtle issue that is hard to track down when it occurs (and it has - see commit 864d296cf94). To try and help mitigate such occurrences, this patch introduces pci_pcie_cap2() which is similar to pci_pcie_cap() but also checks that the PCIe capability version is >= 2. pci_pcie_cap2() should be used for qualifying PCIe capability features introduced after v1. Suggested by Don Dutile. Acked-by: Donald Dutile Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 65 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 766bb13bb0a3..985df63aa59f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -277,6 +277,38 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap) return pos; } +/** + * pci_pcie_cap2 - query for devices' PCI_CAP_ID_EXP v2 capability structure + * @dev: PCI device to check + * + * Like pci_pcie_cap() but also checks that the PCIe capability version is + * >= 2. Note that v1 capability structures could be sparse in that not + * all register fields were required. v2 requires the entire structure to + * be present size wise, while still allowing for non-implemented registers + * to exist but they must be hardwired to 0. + * + * Due to the differences in the versions of capability structures, one + * must be careful not to try and access non-existant registers that may + * exist in early versions - v1 - of Express devices. + * + * Returns the offset of the PCIe capability structure as long as the + * capability version is >= 2; otherwise 0 is returned. + */ +static int pci_pcie_cap2(struct pci_dev *dev) +{ + u16 flags; + int pos; + + pos = pci_pcie_cap(dev); + if (pos) { + pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags); + if ((flags & PCI_EXP_FLAGS_VERS) < 2) + pos = 0; + } + + return pos; +} + /** * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query @@ -1983,7 +2015,7 @@ void pci_enable_ari(struct pci_dev *dev) { int pos; u32 cap; - u16 flags, ctrl; + u16 ctrl; struct pci_dev *bridge; if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn) @@ -1997,15 +2029,11 @@ void pci_enable_ari(struct pci_dev *dev) if (!bridge) return; - pos = pci_pcie_cap(bridge); + /* ARI is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(bridge); if (!pos) return; - /* ARI is a PCIe v2 feature */ - pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, &flags); - if ((flags & PCI_EXP_FLAGS_VERS) < 2) - return; - pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap); if (!(cap & PCI_EXP_DEVCAP2_ARI)) return; @@ -2018,7 +2046,7 @@ void pci_enable_ari(struct pci_dev *dev) } /** - * pci_enable_ido - enable ID-based ordering on a device + * pci_enable_ido - enable ID-based Ordering on a device * @dev: the PCI device * @type: which types of IDO to enable * @@ -2031,7 +2059,8 @@ void pci_enable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2054,7 +2083,8 @@ void pci_disable_ido(struct pci_dev *dev, unsigned long type) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* ID-based Ordering is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2093,7 +2123,8 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) u16 ctrl; int ret; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2143,7 +2174,8 @@ void pci_disable_obff(struct pci_dev *dev) int pos; u16 ctrl; - pos = pci_pcie_cap(dev); + /* OBFF is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; @@ -2165,7 +2197,8 @@ static bool pci_ltr_supported(struct pci_dev *dev) int pos; u32 cap; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return false; @@ -2193,7 +2226,8 @@ int pci_enable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return -ENOTSUPP; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return -ENOTSUPP; @@ -2228,7 +2262,8 @@ void pci_disable_ltr(struct pci_dev *dev) if (!pci_ltr_supported(dev)) return; - pos = pci_pcie_cap(dev); + /* LTR is a PCIe cap v2 feature */ + pos = pci_pcie_cap2(dev); if (!pos) return; -- cgit v1.2.3 From 9cb604ed45a31419bab3877472691a5da15a3c47 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Fri, 1 Jun 2012 15:16:43 -0600 Subject: PCI: remove redundant capabilities checking in pci_{save, restore}_pcie_state Unlike PCI Express v1's Capabilities Structure, v2's requires the entire structure to be implemented. In v2 structures, register fields that are not implemented are present but hardwired to 0x0. These may include: Link Capabilities, Status, and Control; Slot Capabilities, Status, and Control; Root Capabilities, Status, and Control; and all of the '2' (Device, Link, and Slot) Capabilities, Status, and Control registers. This patch removes the redundant capability checks corresponding to the Link 2's and Slot 2's, Capabilities, Status, and Control registers as they will be present if Device Capabilities 2's registers are (which explains why the macros for each of the three are identical). Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 985df63aa59f..fe26df7cf5cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -854,12 +854,6 @@ EXPORT_SYMBOL(pci_choose_state); ((flags & PCI_EXP_FLAGS_VERS) > 1 || \ (type == PCI_EXP_TYPE_ROOT_PORT || \ type == PCI_EXP_TYPE_RC_EC)) -#define pcie_cap_has_devctl2(type, flags) \ - ((flags & PCI_EXP_FLAGS_VERS) > 1) -#define pcie_cap_has_lnkctl2(type, flags) \ - ((flags & PCI_EXP_FLAGS_VERS) > 1) -#define pcie_cap_has_sltctl2(type, flags) \ - ((flags & PCI_EXP_FLAGS_VERS) > 1) static struct pci_cap_saved_state *pci_find_saved_cap( struct pci_dev *pci_dev, char cap) @@ -902,13 +896,14 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); if (pcie_cap_has_rtctl(dev->pcie_type, flags)) pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); - if (pcie_cap_has_devctl2(dev->pcie_type, flags)) - pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]); - if (pcie_cap_has_lnkctl2(dev->pcie_type, flags)) - pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]); - if (pcie_cap_has_sltctl2(dev->pcie_type, flags)) - pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]); + pos = pci_pcie_cap2(dev); + if (!pos) + return 0; + + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]); + pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]); return 0; } @@ -935,12 +930,14 @@ static void pci_restore_pcie_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]); if (pcie_cap_has_rtctl(dev->pcie_type, flags)) pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]); - if (pcie_cap_has_devctl2(dev->pcie_type, flags)) - pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]); - if (pcie_cap_has_lnkctl2(dev->pcie_type, flags)) - pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]); - if (pcie_cap_has_sltctl2(dev->pcie_type, flags)) - pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]); + + pos = pci_pcie_cap2(dev); + if (!pos) + return; + + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]); + pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]); } -- cgit v1.2.3 From 34ddeb035d704eafdcdb3cbc781894300136c3c4 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Tue, 12 Jun 2012 11:20:19 +0800 Subject: ACPI, APEI, Avoid too much error reporting in runtime This patch fixed the following bug. https://bugzilla.kernel.org/show_bug.cgi?id=43282 This is caused by a firmware bug checking (checking generic address register provided by firmware) in runtime. The checking should be done in address mapping time instead of runtime to avoid too much error reporting in runtime. Reported-by: Pawel Sikora Signed-off-by: Huang Ying Tested-by: Jean Delvare Cc: stable@vger.kernel.org Signed-off-by: Len Brown --- drivers/acpi/apei/apei-base.c | 17 +++++++++++++++-- drivers/acpi/apei/apei-internal.h | 9 +++++++++ drivers/acpi/apei/ghes.c | 6 +++--- 3 files changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 5577762daee1..6686b1eaf13e 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx, u8 ins = entry->instruction; if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) - return acpi_os_map_generic_address(&entry->register_region); + return apei_map_generic_address(&entry->register_region); return 0; } @@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx, u8 ins = entry->instruction; if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) - acpi_os_unmap_generic_address(&entry->register_region); + apei_unmap_generic_address(&entry->register_region); return 0; } @@ -606,6 +606,19 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, return 0; } +int apei_map_generic_address(struct acpi_generic_address *reg) +{ + int rc; + u32 access_bit_width; + u64 address; + + rc = apei_check_gar(reg, &address, &access_bit_width); + if (rc) + return rc; + return acpi_os_map_generic_address(reg); +} +EXPORT_SYMBOL_GPL(apei_map_generic_address); + /* read GAR in interrupt (including NMI) or process context */ int apei_read(u64 *val, struct acpi_generic_address *reg) { diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index cca240a33038..f220d642136e 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h @@ -7,6 +7,8 @@ #define APEI_INTERNAL_H #include +#include +#include struct apei_exec_context; @@ -68,6 +70,13 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio /* IP has been set in instruction function */ #define APEI_EXEC_SET_IP 1 +int apei_map_generic_address(struct acpi_generic_address *reg); + +static inline void apei_unmap_generic_address(struct acpi_generic_address *reg) +{ + acpi_os_unmap_generic_address(reg); +} + int apei_read(u64 *val, struct acpi_generic_address *reg); int apei_write(u64 val, struct acpi_generic_address *reg); diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 9b3cac0abecc..1599566ed1fe 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -301,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) if (!ghes) return ERR_PTR(-ENOMEM); ghes->generic = generic; - rc = acpi_os_map_generic_address(&generic->error_status_address); + rc = apei_map_generic_address(&generic->error_status_address); if (rc) goto err_free; error_block_length = generic->error_block_length; @@ -321,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) return ghes; err_unmap: - acpi_os_unmap_generic_address(&generic->error_status_address); + apei_unmap_generic_address(&generic->error_status_address); err_free: kfree(ghes); return ERR_PTR(rc); @@ -330,7 +330,7 @@ err_free: static void ghes_fini(struct ghes *ghes) { kfree(ghes->estatus); - acpi_os_unmap_generic_address(&ghes->generic->error_status_address); + apei_unmap_generic_address(&ghes->generic->error_status_address); } enum { -- cgit v1.2.3 From 9102d8715e5c10db37d81ab75285a2f6746360b2 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 12 Jun 2012 10:44:55 +0200 Subject: dmaengine: at_hdmac: trivial: fix comment in header Not all Atmel SoCs were pointed out in header comment which was bringing confusion. Remove the truncated list of supported devices, replace by the only one that is not supported. Reported-by: Elen Song Signed-off-by: Nicolas Ferre Signed-off-by: Vinod Koul --- drivers/dma/at_hdmac.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 7292aa87b2dd..bb1601305d25 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -9,10 +9,9 @@ * (at your option) any later version. * * - * This supports the Atmel AHB DMA Controller, - * - * The driver has currently been tested with the Atmel AT91SAM9RL - * and AT91SAM9G45 series. + * This supports the Atmel AHB DMA Controller found in several Atmel SoCs. + * The only Atmel DMA Controller that is not covered by this driver is the one + * found on AT91SAM9263. */ #include -- cgit v1.2.3 From 6878c32e5cc0e40980abe51d1f02fb453e27493e Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 25 May 2012 17:34:51 -0400 Subject: xen/blkfront: Add WARN to deal with misbehaving backends. Part of the ring structure is the 'id' field which is under control of the frontend. The frontend stamps it with "some" value (this some in this implementation being a value less than BLK_RING_SIZE), and when it gets a response expects said value to be in the response structure. We have a check for the id field when spolling new requests but not when de-spolling responses. We also add an extra check in add_id_to_freelist to make sure that the 'struct request' was not NULL - as we cannot pass a NULL to __blk_end_request_all, otherwise that crashes (and all the operations that the response is dealing with end up with __blk_end_request_all). Lastly we also print the name of the operation that failed. [v1: s/BUG/WARN/ suggested by Stefano] [v2: Add extra check in add_id_to_freelist] [v3: Redid op_name per Jan's suggestion] [v4: add const * and add WARN on failure returns] Acked-by: Jan Beulich Acked-by: Stefano Stabellini Signed-off-by: Konrad Rzeszutek Wilk --- drivers/block/xen-blkfront.c | 58 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 60eed4bdd2e4..e4fb3374dcd2 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -141,14 +141,36 @@ static int get_id_from_freelist(struct blkfront_info *info) return free; } -static void add_id_to_freelist(struct blkfront_info *info, +static int add_id_to_freelist(struct blkfront_info *info, unsigned long id) { + if (info->shadow[id].req.u.rw.id != id) + return -EINVAL; + if (info->shadow[id].request == NULL) + return -EINVAL; info->shadow[id].req.u.rw.id = info->shadow_free; info->shadow[id].request = NULL; info->shadow_free = id; + return 0; } +static const char *op_name(int op) +{ + static const char *const names[] = { + [BLKIF_OP_READ] = "read", + [BLKIF_OP_WRITE] = "write", + [BLKIF_OP_WRITE_BARRIER] = "barrier", + [BLKIF_OP_FLUSH_DISKCACHE] = "flush", + [BLKIF_OP_DISCARD] = "discard" }; + + if (op < 0 || op >= ARRAY_SIZE(names)) + return "unknown"; + + if (!names[op]) + return "reserved"; + + return names[op]; +} static int xlbd_reserve_minors(unsigned int minor, unsigned int nr) { unsigned int end = minor + nr; @@ -746,20 +768,36 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) bret = RING_GET_RESPONSE(&info->ring, i); id = bret->id; + /* + * The backend has messed up and given us an id that we would + * never have given to it (we stamp it up to BLK_RING_SIZE - + * look in get_id_from_freelist. + */ + if (id >= BLK_RING_SIZE) { + WARN(1, "%s: response to %s has incorrect id (%ld)\n", + info->gd->disk_name, op_name(bret->operation), id); + /* We can't safely get the 'struct request' as + * the id is busted. */ + continue; + } req = info->shadow[id].request; if (bret->operation != BLKIF_OP_DISCARD) blkif_completion(&info->shadow[id]); - add_id_to_freelist(info, id); + if (add_id_to_freelist(info, id)) { + WARN(1, "%s: response to %s (id %ld) couldn't be recycled!\n", + info->gd->disk_name, op_name(bret->operation), id); + continue; + } error = (bret->status == BLKIF_RSP_OKAY) ? 0 : -EIO; switch (bret->operation) { case BLKIF_OP_DISCARD: if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { struct request_queue *rq = info->rq; - printk(KERN_WARNING "blkfront: %s: discard op failed\n", - info->gd->disk_name); + printk(KERN_WARNING "blkfront: %s: %s op failed\n", + info->gd->disk_name, op_name(bret->operation)); error = -EOPNOTSUPP; info->feature_discard = 0; info->feature_secdiscard = 0; @@ -771,18 +809,14 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) case BLKIF_OP_FLUSH_DISKCACHE: case BLKIF_OP_WRITE_BARRIER: if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { - printk(KERN_WARNING "blkfront: %s: write %s op failed\n", - info->flush_op == BLKIF_OP_WRITE_BARRIER ? - "barrier" : "flush disk cache", - info->gd->disk_name); + printk(KERN_WARNING "blkfront: %s: %s op failed\n", + info->gd->disk_name, op_name(bret->operation)); error = -EOPNOTSUPP; } if (unlikely(bret->status == BLKIF_RSP_ERROR && info->shadow[id].req.u.rw.nr_segments == 0)) { - printk(KERN_WARNING "blkfront: %s: empty write %s op failed\n", - info->flush_op == BLKIF_OP_WRITE_BARRIER ? - "barrier" : "flush disk cache", - info->gd->disk_name); + printk(KERN_WARNING "blkfront: %s: empty %s op failed\n", + info->gd->disk_name, op_name(bret->operation)); error = -EOPNOTSUPP; } if (unlikely(error)) { -- cgit v1.2.3 From 4eccc579795290a58e2262fa4e9d083d7672e699 Mon Sep 17 00:00:00 2001 From: Lars Ellenberg Date: Fri, 8 Jun 2012 13:18:51 +0200 Subject: drbd: fix access of unallocated pages and kernel panic BUG: unable to handle kernel NULL pointer dereference at (null) ... [] ? _drbd_bm_set_bits+0x151/0x240 [drbd] [] ? receive_bitmap+0x4f8/0xbc0 [drbd] This fixes an off-by-one error in the receive_bitmap() path, if run-length encoded bitmap transfer is enabled. If the bitmap is an exact multiple of PAGE_SIZE, which means the visible capacity of the drbd device is an exact multiple of 128 MiB (for 4k page size), and bitmap compression (use-rle) is enabled (which became default with 8.4), and the very last bit is dirty and reported in an rle comressed bitmap packet, we ended up trying to kmap_atomic a page pointer that does not exist (bitmap->bm_pages[last index + 1]). bug introduced by: Date: Fri Jul 24 15:33:24 2009 +0200 set bits: optimize for complete last word, fix off-by-one-word corner case made effective by: Date: Thu Dec 16 00:32:38 2010 +0100 drbd: get rid of unused debug code Long time ago, we had paranoia code in the bitmap that allocated one extra word, assigned a magic value, and checked on every occasion that the magic value was still unchanged. That debug code is unused, the extra long word complicates code a bit. Get rid of it. No-one triggered this bug in the last few years, because a large subset of our userbase is unaffected: * typically the last few blocks of a device are not modified frequently, and remain unset * use-rle was disabled by default in drbd < 8.4 * those with slightly "odd" device sizes, or * drbd internal meta data (which will skew the device size slightly, thus makes it harder to have a bug relevant device size) Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_bitmap.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index b5c5ff53cb57..fcb956bb4b4c 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c @@ -1475,10 +1475,17 @@ void _drbd_bm_set_bits(struct drbd_conf *mdev, const unsigned long s, const unsi first_word = 0; spin_lock_irq(&b->bm_lock); } - /* last page (respectively only page, for first page == last page) */ last_word = MLPP(el >> LN2_BPL); - bm_set_full_words_within_one_page(mdev->bitmap, last_page, first_word, last_word); + + /* consider bitmap->bm_bits = 32768, bitmap->bm_number_of_pages = 1. (or multiples). + * ==> e = 32767, el = 32768, last_page = 2, + * and now last_word = 0. + * We do not want to touch last_page in this case, + * as we did not allocate it, it is not present in bitmap->bm_pages. + */ + if (last_word) + bm_set_full_words_within_one_page(mdev->bitmap, last_page, first_word, last_word); /* possibly trailing bits. * example: (e & 63) == 63, el will be e+1. -- cgit v1.2.3 From 1ed25b269e3dd5ecc64f17beef9ea21745c39ca6 Mon Sep 17 00:00:00 2001 From: Lars Ellenberg Date: Fri, 8 Jun 2012 14:09:54 +0200 Subject: drbd: fix list corruption by failing but already aborted reads If a read is aborted due to force-detach of a supposedly unresponsive local backing device, and retried on the peer, it can happen that the local request later still completes (hopefully with an error). As it may already have been completed to upper layers meanwhile, it must not be retried again now. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_req.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 9c5c84946b05..773f4e2d3c1b 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -472,12 +472,17 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what, req->rq_state |= RQ_LOCAL_COMPLETED; req->rq_state &= ~RQ_LOCAL_PENDING; - D_ASSERT(!(req->rq_state & RQ_NET_MASK)); + if (req->rq_state & RQ_LOCAL_ABORTED) { + _req_may_be_done(req, m); + break; + } __drbd_chk_io_error(mdev, false); goto_queue_for_net_read: + D_ASSERT(!(req->rq_state & RQ_NET_MASK)); + /* no point in retrying if there is no good remote data, * or we have no connection. */ if (mdev->state.pdsk != D_UP_TO_DATE) { -- cgit v1.2.3 From 0d5934e3c258fc5decc4103600c597086fd95a52 Mon Sep 17 00:00:00 2001 From: Lars Ellenberg Date: Fri, 8 Jun 2012 14:17:36 +0200 Subject: drbd: fix null pointer dereference with on-congestion policy when diskless We must not look at mdev->actlog, unless we have a get_ldev() reference. It also does not make much sense to try to disconnect or pull-ahead of the peer, if we don't have good local data. Only even consider congestion policies, if our local disk is D_UP_TO_DATE. Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_req.c | 59 ++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 773f4e2d3c1b..8e93a6ac9bb6 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -770,6 +770,40 @@ static int drbd_may_do_local_read(struct drbd_conf *mdev, sector_t sector, int s return 0 == drbd_bm_count_bits(mdev, sbnr, ebnr); } +static void maybe_pull_ahead(struct drbd_conf *mdev) +{ + int congested = 0; + + /* If I don't even have good local storage, we can not reasonably try + * to pull ahead of the peer. We also need the local reference to make + * sure mdev->act_log is there. + * Note: caller has to make sure that net_conf is there. + */ + if (!get_ldev_if_state(mdev, D_UP_TO_DATE)) + return; + + if (mdev->net_conf->cong_fill && + atomic_read(&mdev->ap_in_flight) >= mdev->net_conf->cong_fill) { + dev_info(DEV, "Congestion-fill threshold reached\n"); + congested = 1; + } + + if (mdev->act_log->used >= mdev->net_conf->cong_extents) { + dev_info(DEV, "Congestion-extents threshold reached\n"); + congested = 1; + } + + if (congested) { + queue_barrier(mdev); /* last barrier, after mirrored writes */ + + if (mdev->net_conf->on_congestion == OC_PULL_AHEAD) + _drbd_set_state(_NS(mdev, conn, C_AHEAD), 0, NULL); + else /*mdev->net_conf->on_congestion == OC_DISCONNECT */ + _drbd_set_state(_NS(mdev, conn, C_DISCONNECTING), 0, NULL); + } + put_ldev(mdev); +} + static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, unsigned long start_time) { const int rw = bio_rw(bio); @@ -977,29 +1011,8 @@ allocate_barrier: _req_mod(req, queue_for_send_oos); if (remote && - mdev->net_conf->on_congestion != OC_BLOCK && mdev->agreed_pro_version >= 96) { - int congested = 0; - - if (mdev->net_conf->cong_fill && - atomic_read(&mdev->ap_in_flight) >= mdev->net_conf->cong_fill) { - dev_info(DEV, "Congestion-fill threshold reached\n"); - congested = 1; - } - - if (mdev->act_log->used >= mdev->net_conf->cong_extents) { - dev_info(DEV, "Congestion-extents threshold reached\n"); - congested = 1; - } - - if (congested) { - queue_barrier(mdev); /* last barrier, after mirrored writes */ - - if (mdev->net_conf->on_congestion == OC_PULL_AHEAD) - _drbd_set_state(_NS(mdev, conn, C_AHEAD), 0, NULL); - else /*mdev->net_conf->on_congestion == OC_DISCONNECT */ - _drbd_set_state(_NS(mdev, conn, C_DISCONNECTING), 0, NULL); - } - } + mdev->net_conf->on_congestion != OC_BLOCK && mdev->agreed_pro_version >= 96) + maybe_pull_ahead(mdev); spin_unlock_irq(&mdev->req_lock); kfree(b); /* if someone else has beaten us to it... */ -- cgit v1.2.3 From b99ea85a3acff53151322a1c882f217375b1300e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Sun, 3 Jun 2012 20:48:19 +0200 Subject: PCI: move fixup hooks from __init to __devinit The fixups are executed once the pci-device is found which is during boot process so __init seems fine as long as the platform does not support hotplug. However it is possible to remove the PCI bus at run time and have it rediscovered again via "echo 1 > /sys/bus/pci/rescan" and this will call the fixups again. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..993cec88c5b4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -253,7 +253,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576, quirk_vsfx) * workaround applied too * [Info kindly provided by ALi] */ -static void __init quirk_alimagik(struct pci_dev *dev) +static void __devinit quirk_alimagik(struct pci_dev *dev) { if ((pci_pci_problems&PCIPCI_ALIMAGIK)==0) { dev_info(&dev->dev, "Limiting direct PCI/PCI transfers\n"); @@ -789,7 +789,7 @@ static void __devinit quirk_amd_ioapic(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, quirk_amd_ioapic); -static void __init quirk_ioapic_rmw(struct pci_dev *dev) +static void __devinit quirk_ioapic_rmw(struct pci_dev *dev) { if (dev->devfn == 0 && dev->bus->number == 0) sis_apic_bug = 1; @@ -801,7 +801,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw); * Some settings of MMRBC can lead to data corruption so block changes. * See AMD 8131 HyperTransport PCI-X Tunnel Revision Guide */ -static void __init quirk_amd_8131_mmrbc(struct pci_dev *dev) +static void __devinit quirk_amd_8131_mmrbc(struct pci_dev *dev) { if (dev->subordinate && dev->revision <= 0x12) { dev_info(&dev->dev, "AMD8131 rev %x detected; " @@ -1082,7 +1082,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB /* * Intel 82801CAM ICH3-M datasheet says IDE modes must be the same */ -static void __init quirk_ide_samemode(struct pci_dev *pdev) +static void __devinit quirk_ide_samemode(struct pci_dev *pdev) { u8 prog; @@ -1121,7 +1121,7 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID, /* This was originally an Alpha specific thing, but it really fits here. * The i82375 PCI/EISA bridge appears as non-classified. Fix that. */ -static void __init quirk_eisa_bridge(struct pci_dev *dev) +static void __devinit quirk_eisa_bridge(struct pci_dev *dev) { dev->class = PCI_CLASS_BRIDGE_EISA << 8; } @@ -1155,7 +1155,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82375, quirk_e */ static int asus_hides_smbus; -static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev) +static void __devinit asus_hides_smbus_hostbridge(struct pci_dev *dev) { if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK)) { if (dev->device == PCI_DEVICE_ID_INTEL_82845_HB) @@ -1538,7 +1538,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB3 #endif #ifdef CONFIG_X86_IO_APIC -static void __init quirk_alder_ioapic(struct pci_dev *pdev) +static void __devinit quirk_alder_ioapic(struct pci_dev *pdev) { int i; @@ -1777,7 +1777,7 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, qui * but the PIO transfers won't work if BAR0 falls at the odd 8 bytes. * Re-allocate the region if needed... */ -static void __init quirk_tc86c001_ide(struct pci_dev *dev) +static void __devinit quirk_tc86c001_ide(struct pci_dev *dev) { struct resource *r = &dev->resource[0]; @@ -2169,7 +2169,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLX, 0x8624, quirk_tile_plx_gen1); * aware of it. Instead of setting the flag on all busses in the * machine, simply disable MSI globally. */ -static void __init quirk_disable_all_msi(struct pci_dev *dev) +static void __devinit quirk_disable_all_msi(struct pci_dev *dev) { pci_no_msi(); dev_warn(&dev->dev, "MSI quirk detected; MSI disabled\n"); -- cgit v1.2.3 From ad805758c0eb25bce7b2e3b298d63dc62a1bc71c Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:07 +0000 Subject: PCI: add ACS validation utility In a PCI environment, transactions aren't always required to reach the root bus before being re-routed. Intermediate switches between an endpoint and the root bus can redirect DMA back downstream before things like IOMMUs have a chance to intervene. Legacy PCI is always susceptible to this as it operates on a shared bus. PCIe added a new capability to describe and control this behavior, Access Control Services, or ACS. The utility function pci_acs_enabled() allows us to test the ACS capabilities of an individual devices against a set of flags while pci_acs_path_enabled() tests a complete path from a given downstream device up to the specified upstream device. We also include the ability to add device specific tests as it's likely we'll see devices that do not implement ACS, but want to indicate support for various capabilities in this space. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/quirks.c | 33 +++++++++++++++++++++++++ 2 files changed, 102 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..1ccf7d49f522 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2359,6 +2359,75 @@ void pci_enable_acs(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl); } +/** + * pci_acs_enabled - test ACS against required flags for a given device + * @pdev: device to test + * @acs_flags: required PCI ACS flags + * + * Return true if the device supports the provided flags. Automatically + * filters out flags that are not implemented on multifunction devices. + */ +bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags) +{ + int pos, ret; + u16 ctrl; + + ret = pci_dev_specific_acs_enabled(pdev, acs_flags); + if (ret >= 0) + return ret > 0; + + if (!pci_is_pcie(pdev)) + return false; + + /* Filter out flags not applicable to multifunction */ + if (pdev->multifunction) + acs_flags &= (PCI_ACS_RR | PCI_ACS_CR | + PCI_ACS_EC | PCI_ACS_DT); + + if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM || + pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || + pdev->multifunction) { + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS); + if (!pos) + return false; + + pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + if ((ctrl & acs_flags) != acs_flags) + return false; + } + + return true; +} + +/** + * pci_acs_path_enable - test ACS flags from start to end in a hierarchy + * @start: starting downstream device + * @end: ending upstream device or NULL to search to the root bus + * @acs_flags: required flags + * + * Walk up a device tree from start to end testing PCI ACS support. If + * any step along the way does not support the required flags, return false. + */ +bool pci_acs_path_enabled(struct pci_dev *start, + struct pci_dev *end, u16 acs_flags) +{ + struct pci_dev *pdev, *parent = start; + + do { + pdev = parent; + + if (!pci_acs_enabled(pdev, acs_flags)) + return false; + + if (pci_is_root_bus(pdev->bus)) + return (end == NULL); + + parent = pdev->bus->self; + } while (pdev != end); + + return true; +} + /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index acd3956b44bd..27e2c8f4ec73 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3230,3 +3230,36 @@ struct pci_dev *pci_get_dma_source(struct pci_dev *dev) return pci_dev_get(dev); } + +static const struct pci_dev_acs_enabled { + u16 vendor; + u16 device; + int (*acs_enabled)(struct pci_dev *dev, u16 acs_flags); +} pci_dev_acs_enabled[] = { + { 0 } +}; + +int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) +{ + const struct pci_dev_acs_enabled *i; + int ret; + + /* + * Allow devices that do not expose standard PCIe ACS capabilities + * or control to indicate their support here. Multi-function express + * devices which do not allow internal peer-to-peer between functions, + * but do not implement PCIe ACS may wish to return true here. + */ + for (i = pci_dev_acs_enabled; i->acs_enabled; i++) { + if ((i->vendor == dev->vendor || + i->vendor == (u16)PCI_ANY_ID) && + (i->device == dev->device || + i->device == (u16)PCI_ANY_ID)) { + ret = i->acs_enabled(dev, acs_flags); + if (ret >= 0) + return ret; + } + } + + return -ENOTTY; +} -- cgit v1.2.3 From c63587d7f5b9db84e71daf5962dc0394eb657da2 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:19 +0000 Subject: PCI: export pci_user functions for use by other drivers VFIO PCI support will make use of these for user-initiated PCI config accesses. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/access.c | 6 ++++-- drivers/pci/pci.h | 7 ------- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 2a581642c237..ba91a7e17519 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -162,7 +162,8 @@ int pci_user_read_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ -} +} \ +EXPORT_SYMBOL_GPL(pci_user_read_config_##size); /* Returns 0 on success, negative values indicate error. */ #define PCI_USER_WRITE_CONFIG(size,type) \ @@ -181,7 +182,8 @@ int pci_user_write_config_##size \ if (ret > 0) \ ret = -EINVAL; \ return ret; \ -} +} \ +EXPORT_SYMBOL_GPL(pci_user_write_config_##size); PCI_USER_READ_CONFIG(byte, u8) PCI_USER_READ_CONFIG(word, u16) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..f2dcc46bdece 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -86,13 +86,6 @@ static inline bool pci_is_bridge(struct pci_dev *pci_dev) return !!(pci_dev->subordinate); } -extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); -extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); -extern int pci_user_read_config_dword(struct pci_dev *dev, int where, u32 *val); -extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val); -extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); -extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); - struct pci_vpd_ops { ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); -- cgit v1.2.3 From a6961651408afa9387d6df43c4a1dc4fd35dcb1b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 11 Jun 2012 05:27:33 +0000 Subject: PCI: create common pcibios_err_to_errno For returning errors out to non-PCI code. Re-name xen's version. Acked-by: Konrad Rzeszutek Wilk Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/xen/xen-pciback/conf_space.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 30d7be026c18..46ae0f9f02ad 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c @@ -124,7 +124,7 @@ static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, return val; } -static int pcibios_err_to_errno(int err) +static int xen_pcibios_err_to_errno(int err) { switch (err) { case PCIBIOS_SUCCESSFUL: @@ -202,7 +202,7 @@ out: pci_name(dev), size, offset, value); *ret_val = value; - return pcibios_err_to_errno(err); + return xen_pcibios_err_to_errno(err); } int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) @@ -290,7 +290,7 @@ int xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) } } - return pcibios_err_to_errno(err); + return xen_pcibios_err_to_errno(err); } void xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) -- cgit v1.2.3 From f762470b522014ce4ebda3ecac74eb6658da7d9d Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:39 +0530 Subject: Input: tegra-kbc - add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren Acked-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 4ffe64d53107..2c1c9ed1bd9f 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -492,7 +492,7 @@ static int tegra_kbc_start(struct tegra_kbc *kbc) unsigned int debounce_cnt; u32 val = 0; - clk_enable(kbc->clk); + clk_prepare_enable(kbc->clk); /* Reset the KBC controller to clear all previous status.*/ tegra_periph_reset_assert(kbc->clk); @@ -556,7 +556,7 @@ static void tegra_kbc_stop(struct tegra_kbc *kbc) disable_irq(kbc->irq); del_timer_sync(&kbc->timer); - clk_disable(kbc->clk); + clk_disable_unprepare(kbc->clk); } static int tegra_kbc_open(struct input_dev *dev) -- cgit v1.2.3 From cb53417efc726f3c243eaaeb9fce1d15bda05521 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:40 +0530 Subject: spi/tegra: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: grant.likely@secretlab.ca cc: spi-devel-general@lists.sourceforge.net Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren --- drivers/spi/spi-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c index ae6d78a3e912..7f99ff3553a6 100644 --- a/drivers/spi/spi-tegra.c +++ b/drivers/spi/spi-tegra.c @@ -261,7 +261,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi, clk_set_rate(tspi->clk, speed); if (tspi->cur_speed == 0) - clk_enable(tspi->clk); + clk_prepare_enable(tspi->clk); tspi->cur_speed = speed; @@ -373,7 +373,7 @@ static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) spi = m->state; spi_tegra_start_message(spi, m); } else { - clk_disable(tspi->clk); + clk_disable_unprepare(tspi->clk); tspi->cur_speed = 0; } } -- cgit v1.2.3 From 61c3b1971ad4c71c99919da949ac2043dfac8ee7 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:41 +0530 Subject: staging: nvec: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Cc: gregkh@linuxfoundation.org Cc: ac100@lists.launchpad.net Cc: devel@driverdev.osuosl.org Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren Acked-by: Marc Dietrich --- drivers/staging/nvec/nvec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 3c60088871e0..9356886f489b 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -675,7 +675,7 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec) { u32 val; - clk_enable(nvec->i2c_clk); + clk_prepare_enable(nvec->i2c_clk); tegra_periph_reset_assert(nvec->i2c_clk); udelay(2); @@ -695,14 +695,14 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec) enable_irq(nvec->irq); - clk_disable(nvec->i2c_clk); + clk_disable_unprepare(nvec->i2c_clk); } static void nvec_disable_i2c_slave(struct nvec_chip *nvec) { disable_irq(nvec->irq); writel(I2C_SL_NEWSL | I2C_SL_NACK, nvec->base + I2C_SL_CNFG); - clk_disable(nvec->i2c_clk); + clk_disable_unprepare(nvec->i2c_clk); } static void nvec_power_off(void) @@ -812,7 +812,7 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev) tegra_init_i2c_slave(nvec); - clk_enable(i2c_clk); + clk_prepare_enable(i2c_clk); /* enable event reporting */ -- cgit v1.2.3 From 0df1ed4b12840f1d17b373ee5c05877c375ddc3e Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Tue, 5 Jun 2012 09:59:43 +0530 Subject: crypto: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Signed-off-by: Prashant Gaikwad Signed-off-by: Stephen Warren Acked-by: Herbert Xu --- drivers/crypto/tegra-aes.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/crypto/tegra-aes.c b/drivers/crypto/tegra-aes.c index 422a9766c7c9..ac236f6724f4 100644 --- a/drivers/crypto/tegra-aes.c +++ b/drivers/crypto/tegra-aes.c @@ -572,7 +572,7 @@ static void aes_workqueue_handler(struct work_struct *work) struct tegra_aes_dev *dd = aes_dev; int ret; - ret = clk_enable(dd->aes_clk); + ret = clk_prepare_enable(dd->aes_clk); if (ret) BUG_ON("clock enable failed"); @@ -581,7 +581,7 @@ static void aes_workqueue_handler(struct work_struct *work) ret = tegra_aes_handle_req(dd); } while (!ret); - clk_disable(dd->aes_clk); + clk_disable_unprepare(dd->aes_clk); } static irqreturn_t aes_irq(int irq, void *dev_id) @@ -673,7 +673,7 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata, /* take mutex to access the aes hw */ mutex_lock(&aes_lock); - ret = clk_enable(dd->aes_clk); + ret = clk_prepare_enable(dd->aes_clk); if (ret) return ret; @@ -700,7 +700,7 @@ static int tegra_aes_get_random(struct crypto_rng *tfm, u8 *rdata, } out: - clk_disable(dd->aes_clk); + clk_disable_unprepare(dd->aes_clk); mutex_unlock(&aes_lock); dev_dbg(dd->dev, "%s: done\n", __func__); @@ -758,7 +758,7 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed, dd->flags = FLAGS_ENCRYPT | FLAGS_RNG; - ret = clk_enable(dd->aes_clk); + ret = clk_prepare_enable(dd->aes_clk); if (ret) return ret; @@ -788,7 +788,7 @@ static int tegra_aes_rng_reset(struct crypto_rng *tfm, u8 *seed, memcpy(dd->dt, dt, DEFAULT_RNG_BLK_SZ); out: - clk_disable(dd->aes_clk); + clk_disable_unprepare(dd->aes_clk); mutex_unlock(&aes_lock); dev_dbg(dd->dev, "%s: done\n", __func__); -- cgit v1.2.3 From cbb2d5e459f4e10b3f1d11c23c31aa5d7c21e34c Mon Sep 17 00:00:00 2001 From: Rajiv Andrade Date: Tue, 24 Apr 2012 11:19:58 -0300 Subject: MAINTAINERS: TPM maintainers' contacts update Usual contact update, Debora Velarde role resign, and the new co-maintainer inclusion, Kent Yoder. He's accepted to contribute more actively to this driver's maintainership given the current maintainer's slight career change that will affect his contribution time. [Replacing Debora Velarde by Kent Yoder] Signed-off-by: Rajiv Andrade --- drivers/char/tpm/tpm_infineon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 76da32e11f18..3251a44e8ceb 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -4,8 +4,8 @@ * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module * Specifications at www.trustedcomputinggroup.org * - * Copyright (C) 2005, Marcel Selhorst - * Sirrix AG - security technologies, http://www.sirrix.com and + * Copyright (C) 2005, Marcel Selhorst + * Sirrix AG - security technologies and * Applied Data Security Group, Ruhr-University Bochum, Germany * Project-Homepage: http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ * @@ -671,7 +671,7 @@ static void __exit cleanup_inf(void) module_init(init_inf); module_exit(cleanup_inf); -MODULE_AUTHOR("Marcel Selhorst "); +MODULE_AUTHOR("Marcel Selhorst "); MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); MODULE_VERSION("1.9.2"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 24ebe6670de3d1f0dca11c9eb372134c7ab05503 Mon Sep 17 00:00:00 2001 From: Rajiv Andrade Date: Tue, 24 Apr 2012 17:38:17 -0300 Subject: TPM: chip disabled state erronously being reported as error tpm_do_selftest() attempts to read a PCR in order to decide if one can rely on the TPM being used or not. The function that's used by __tpm_pcr_read() does not expect the TPM to be disabled or deactivated, and if so, reports an error. It's fine if the TPM returns this error when trying to use it for the first time after a power cycle, but it's definitely not if it already returned success for a previous attempt to read one of its PCRs. The tpm_do_selftest() was modified so that the driver only reports this return code as an error when it really is. Reported-and-tested-by: Paul Bolle Cc: Stable Signed-off-by: Rajiv Andrade --- drivers/char/tpm/tpm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index ad7c7320dd1b..08427abf5fa5 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -827,10 +827,10 @@ EXPORT_SYMBOL_GPL(tpm_pcr_extend); int tpm_do_selftest(struct tpm_chip *chip) { int rc; - u8 digest[TPM_DIGEST_SIZE]; unsigned int loops; unsigned int delay_msec = 1000; unsigned long duration; + struct tpm_cmd_t cmd; duration = tpm_calc_ordinal_duration(chip, TPM_ORD_CONTINUE_SELFTEST); @@ -845,7 +845,15 @@ int tpm_do_selftest(struct tpm_chip *chip) return rc; do { - rc = __tpm_pcr_read(chip, 0, digest); + /* Attempt to read a PCR value */ + cmd.header.in = pcrread_header; + cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0); + rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE); + + if (rc < TPM_HEADER_SIZE) + return -EFAULT; + + rc = be32_to_cpu(cmd.header.out.return_code); if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) { dev_info(chip->dev, "TPM is disabled/deactivated (0x%X)\n", rc); -- cgit v1.2.3 From dad79cb89254ce646906846b0f0cf7995d626710 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Wed, 16 May 2012 16:24:28 +0800 Subject: TPM: fix memleak when register hardware fails Adding proper kfree() before returning. Signed-off-by: Wanlong Gao Signed-off-by: Rajiv Andrade --- drivers/char/tpm/tpm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 08427abf5fa5..dae254d53723 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1413,15 +1413,12 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, "unable to misc_register %s, minor %d\n", chip->vendor.miscdev.name, chip->vendor.miscdev.minor); - put_device(chip->dev); - return NULL; + goto put_device; } if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { misc_deregister(&chip->vendor.miscdev); - put_device(chip->dev); - - return NULL; + goto put_device; } chip->bios_dir = tpm_bios_log_setup(devname); @@ -1433,6 +1430,8 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, return chip; +put_device: + put_device(chip->dev); out_free: kfree(chip); kfree(devname); -- cgit v1.2.3 From 3072928ff2457642af0da745b88f0420b4596c48 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Mon, 28 May 2012 18:42:45 +0200 Subject: tpm: check the chip reference before using it If a driver calls tpm_dev_vendor_release for a device already released then the driver will oops. Signed-off-by: Andi Shyti Signed-off-by: Peter Huewe Signed-off-by: Rajiv Andrade --- drivers/char/tpm/tpm.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index dae254d53723..d39b1f60ba23 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1330,6 +1330,9 @@ EXPORT_SYMBOL_GPL(tpm_pm_resume); void tpm_dev_vendor_release(struct tpm_chip *chip) { + if (!chip) + return; + if (chip->vendor.release) chip->vendor.release(chip->dev); @@ -1347,6 +1350,9 @@ void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); + if (!chip) + return; + tpm_dev_vendor_release(chip); chip->release(dev); -- cgit v1.2.3 From ef9d884dadd4c9f2c8432a6b82309ae4a133a35d Mon Sep 17 00:00:00 2001 From: "tom.leiming@gmail.com" Date: Mon, 11 Jun 2012 15:19:39 +0000 Subject: usbnet: remove usb_get/put_dev in .probe and .disconnect usb_device is parent device of usb_interface in the view of driver model, so its reference count is always held during .probe/.disconnect of usb_interface instance. This patch just removes the unnecessay usb_get/put_dev. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 9f58330f1312..022c1e72d004 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1307,7 +1307,6 @@ void usbnet_disconnect (struct usb_interface *intf) usb_free_urb(dev->interrupt); free_netdev(net); - usb_put_dev (xdev); } EXPORT_SYMBOL_GPL(usbnet_disconnect); @@ -1363,8 +1362,6 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) xdev = interface_to_usbdev (udev); interface = udev->cur_altsetting; - usb_get_dev (xdev); - status = -ENOMEM; // set up our own records @@ -1493,7 +1490,6 @@ out3: out1: free_netdev(net); out: - usb_put_dev(xdev); return status; } EXPORT_SYMBOL_GPL(usbnet_probe); -- cgit v1.2.3 From 5c2f05139ad9459979b3102846fa61347896d148 Mon Sep 17 00:00:00 2001 From: "tom.leiming@gmail.com" Date: Mon, 11 Jun 2012 15:19:40 +0000 Subject: usbnet:pegasus: remove usb_get/put_dev in .probe and .disconnect usb_device is parent device of usb_interface in the view of driver model, so its reference count is always held during .probe/.disconnect of usb_interface instance. This patch just removes the unnecessay usb_get/put_dev. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/pegasus.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 7023220456c5..a0b5807b30d4 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -1329,8 +1329,6 @@ static int pegasus_probe(struct usb_interface *intf, } pegasus_count++; - usb_get_dev(dev); - net = alloc_etherdev(sizeof(struct pegasus)); if (!net) goto out; @@ -1407,7 +1405,6 @@ out2: out1: free_netdev(net); out: - usb_put_dev(dev); pegasus_dec_workqueue(); return res; } @@ -1425,7 +1422,6 @@ static void pegasus_disconnect(struct usb_interface *intf) pegasus->flags |= PEGASUS_UNPLUG; cancel_delayed_work(&pegasus->carrier_check); unregister_netdev(pegasus->net); - usb_put_dev(interface_to_usbdev(intf)); unlink_all_urbs(pegasus); free_all_urbs(pegasus); free_skb_pool(pegasus); -- cgit v1.2.3 From 50e7d153910b0735aa517c3d1f3723a88f66eaba Mon Sep 17 00:00:00 2001 From: "tom.leiming@gmail.com" Date: Mon, 11 Jun 2012 15:19:41 +0000 Subject: usbnet:cdc-phonet: remove usb_get/put_dev in .probe and .disconnect usb_device is parent device of usb_interface in the view of driver model, so its reference count is always held during .probe/.disconnect of usb_interface instance. This patch just removes the unnecessay usb_get/put_dev. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/cdc-phonet.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index d848d4dd5754..187c144c5e5b 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -394,7 +394,7 @@ int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) SET_NETDEV_DEV(dev, &intf->dev); pnd->dev = dev; - pnd->usb = usb_get_dev(usbdev); + pnd->usb = usbdev; pnd->intf = intf; pnd->data_intf = data_intf; spin_lock_init(&pnd->tx_lock); @@ -440,7 +440,6 @@ out: static void usbpn_disconnect(struct usb_interface *intf) { struct usbpn_dev *pnd = usb_get_intfdata(intf); - struct usb_device *usb = pnd->usb; if (pnd->disconnected) return; @@ -449,7 +448,6 @@ static void usbpn_disconnect(struct usb_interface *intf) usb_driver_release_interface(&usbpn_driver, (pnd->intf == intf) ? pnd->data_intf : pnd->intf); unregister_netdev(pnd->dev); - usb_put_dev(usb); } static struct usb_driver usbpn_driver = { -- cgit v1.2.3 From 24ead29915352d4689a5972c65307a073b229fb4 Mon Sep 17 00:00:00 2001 From: "tom.leiming@gmail.com" Date: Mon, 11 Jun 2012 15:19:44 +0000 Subject: usbnet: remove declaration for intr_complete Remove declaration for intr_complete so that ctags may be happy to decrease duplicated symbols, also decrease one line code. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 72 +++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 022c1e72d004..205247efee62 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -180,7 +180,41 @@ int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress) } EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr); -static void intr_complete (struct urb *urb); +static void intr_complete (struct urb *urb) +{ + struct usbnet *dev = urb->context; + int status = urb->status; + + switch (status) { + /* success */ + case 0: + dev->driver_info->status(dev, urb); + break; + + /* software-driven interface shutdown */ + case -ENOENT: /* urb killed */ + case -ESHUTDOWN: /* hardware gone */ + netif_dbg(dev, ifdown, dev->net, + "intr shutdown, code %d\n", status); + return; + + /* NOTE: not throttling like RX/TX, since this endpoint + * already polls infrequently + */ + default: + netdev_dbg(dev->net, "intr status %d\n", status); + break; + } + + if (!netif_running (dev->net)) + return; + + memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); + status = usb_submit_urb (urb, GFP_ATOMIC); + if (status != 0) + netif_err(dev, timer, dev->net, + "intr resubmit --> %d\n", status); +} static int init_status (struct usbnet *dev, struct usb_interface *intf) { @@ -519,42 +553,6 @@ block: netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n"); } -static void intr_complete (struct urb *urb) -{ - struct usbnet *dev = urb->context; - int status = urb->status; - - switch (status) { - /* success */ - case 0: - dev->driver_info->status(dev, urb); - break; - - /* software-driven interface shutdown */ - case -ENOENT: /* urb killed */ - case -ESHUTDOWN: /* hardware gone */ - netif_dbg(dev, ifdown, dev->net, - "intr shutdown, code %d\n", status); - return; - - /* NOTE: not throttling like RX/TX, since this endpoint - * already polls infrequently - */ - default: - netdev_dbg(dev->net, "intr status %d\n", status); - break; - } - - if (!netif_running (dev->net)) - return; - - memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); - status = usb_submit_urb (urb, GFP_ATOMIC); - if (status != 0) - netif_err(dev, timer, dev->net, - "intr resubmit --> %d\n", status); -} - /*-------------------------------------------------------------------------*/ void usbnet_pause_rx(struct usbnet *dev) { -- cgit v1.2.3 From 072c0559e26bc35700b3a70dffc230f00d9262b8 Mon Sep 17 00:00:00 2001 From: "tom.leiming@gmail.com" Date: Mon, 11 Jun 2012 15:19:45 +0000 Subject: usbnet: don't initialize transfer buffer before submit status URB The line below in intr_complete isn't needed, memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); so just remove it. Signed-off-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/usbnet.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 205247efee62..ac2e4936b421 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -209,7 +209,6 @@ static void intr_complete (struct urb *urb) if (!netif_running (dev->net)) return; - memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); status = usb_submit_urb (urb, GFP_ATOMIC); if (status != 0) netif_err(dev, timer, dev->net, -- cgit v1.2.3 From de063b7040dcd9fbc9a1847fa44f0af13e19d6de Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 11 Jun 2012 19:23:07 +0000 Subject: bonding: remove packet cloning in recv_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloning all packets in input path have a significant cost. Use skb_header_pointer()/skb_copy_bits() instead of pskb_may_pull() so that recv_probe handlers (bond_3ad_lacpdu_recv / bond_arp_rcv / rlb_arp_recv ) dont touch input skb. bond_handle_frame() can avoid the skb_clone()/dev_kfree_skb() Signed-off-by: Eric Dumazet Cc: Jay Vosburgh Cc: Andy Gospodarek Cc: Jiri Bohac Cc: Nicolas de Pesloüan Cc: Maciej Żenczykowski Signed-off-by: Jay Vosburgh Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 11 +++++++---- drivers/net/bonding/bond_3ad.h | 4 ++-- drivers/net/bonding/bond_alb.c | 20 +++++--------------- drivers/net/bonding/bond_main.c | 37 ++++++++++++++++++++----------------- drivers/net/bonding/bonding.h | 4 ++-- 5 files changed, 36 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 3463b469e657..3031e0413114 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2460,18 +2460,21 @@ out: return NETDEV_TX_OK; } -int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, - struct slave *slave) +int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) { int ret = RX_HANDLER_ANOTHER; + struct lacpdu *lacpdu, _lacpdu; + if (skb->protocol != PKT_TYPE_LACPDU) return ret; - if (!pskb_may_pull(skb, sizeof(struct lacpdu))) + lacpdu = skb_header_pointer(skb, 0, sizeof(_lacpdu), &_lacpdu); + if (!lacpdu) return ret; read_lock(&bond->lock); - ret = bond_3ad_rx_indication((struct lacpdu *) skb->data, slave, skb->len); + ret = bond_3ad_rx_indication(lacpdu, slave, skb->len); read_unlock(&bond->lock); return ret; } diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h index 5ee7e3c45db7..0cfaa4afdece 100644 --- a/drivers/net/bonding/bond_3ad.h +++ b/drivers/net/bonding/bond_3ad.h @@ -274,8 +274,8 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave); void bond_3ad_handle_link_change(struct slave *slave, char link); int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev); -int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct bonding *bond, - struct slave *slave); +int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave); int bond_3ad_set_carrier(struct bonding *bond); void bond_3ad_update_lacp_rate(struct bonding *bond); #endif //__BOND_3AD_H__ diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 0f59c1564e53..ef3791a09ad8 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -342,27 +342,17 @@ static void rlb_update_entry_from_arp(struct bonding *bond, struct arp_pkt *arp) _unlock_rx_hashtbl_bh(bond); } -static int rlb_arp_recv(struct sk_buff *skb, struct bonding *bond, - struct slave *slave) +static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) { - struct arp_pkt *arp; + struct arp_pkt *arp, _arp; if (skb->protocol != cpu_to_be16(ETH_P_ARP)) goto out; - arp = (struct arp_pkt *) skb->data; - if (!arp) { - pr_debug("Packet has no ARP data\n"); + arp = skb_header_pointer(skb, 0, sizeof(_arp), &_arp); + if (!arp) goto out; - } - - if (!pskb_may_pull(skb, arp_hdr_len(bond->dev))) - goto out; - - if (skb->len < sizeof(struct arp_pkt)) { - pr_debug("Packet is too small to be an ARP\n"); - goto out; - } if (arp->op_code == htons(ARPOP_REPLY)) { /* update rx hash table for this ARP */ diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 2ee8cf9e8a3b..9e2301eef386 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1444,8 +1444,8 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) struct sk_buff *skb = *pskb; struct slave *slave; struct bonding *bond; - int (*recv_probe)(struct sk_buff *, struct bonding *, - struct slave *); + int (*recv_probe)(const struct sk_buff *, struct bonding *, + struct slave *); int ret = RX_HANDLER_ANOTHER; skb = skb_share_check(skb, GFP_ATOMIC); @@ -1462,15 +1462,10 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) recv_probe = ACCESS_ONCE(bond->recv_probe); if (recv_probe) { - struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC); - - if (likely(nskb)) { - ret = recv_probe(nskb, bond, slave); - dev_kfree_skb(nskb); - if (ret == RX_HANDLER_CONSUMED) { - consume_skb(skb); - return ret; - } + ret = recv_probe(skb, bond, slave); + if (ret == RX_HANDLER_CONSUMED) { + consume_skb(skb); + return ret; } } @@ -2737,25 +2732,31 @@ static void bond_validate_arp(struct bonding *bond, struct slave *slave, __be32 } } -static int bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, - struct slave *slave) +static int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, + struct slave *slave) { - struct arphdr *arp; + struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr; __be32 sip, tip; + int alen; if (skb->protocol != __cpu_to_be16(ETH_P_ARP)) return RX_HANDLER_ANOTHER; read_lock(&bond->lock); + alen = arp_hdr_len(bond->dev); pr_debug("bond_arp_rcv: bond %s skb->dev %s\n", bond->dev->name, skb->dev->name); - if (!pskb_may_pull(skb, arp_hdr_len(bond->dev))) - goto out_unlock; + if (alen > skb_headlen(skb)) { + arp = kmalloc(alen, GFP_ATOMIC); + if (!arp) + goto out_unlock; + if (skb_copy_bits(skb, 0, arp, alen) < 0) + goto out_unlock; + } - arp = arp_hdr(skb); if (arp->ar_hln != bond->dev->addr_len || skb->pkt_type == PACKET_OTHERHOST || skb->pkt_type == PACKET_LOOPBACK || @@ -2790,6 +2791,8 @@ static int bond_arp_rcv(struct sk_buff *skb, struct bonding *bond, out_unlock: read_unlock(&bond->lock); + if (arp != (struct arphdr *)skb->data) + kfree(arp); return RX_HANDLER_ANOTHER; } diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 4581aa5ccaba..f8af2fcd3d16 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -218,8 +218,8 @@ struct bonding { struct slave *primary_slave; bool force_primary; s32 slave_cnt; /* never change this value outside the attach/detach wrappers */ - int (*recv_probe)(struct sk_buff *, struct bonding *, - struct slave *); + int (*recv_probe)(const struct sk_buff *, struct bonding *, + struct slave *); rwlock_t lock; rwlock_t curr_slave_lock; u8 send_peer_notif; -- cgit v1.2.3 From 123df01e8e046d6065089e1bff29aa3fc48d4420 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 24 May 2012 19:24:18 +0900 Subject: sh: intc: Allocate subgroup virq backing desc directly. This switches to using irq_alloc_desc() directly for subgroup IRQs. We still need to call activate_irq() on these in order to make them requestable, at least up until these get moved in to their own irq domain.. Signed-off-by: Paul Mundt --- drivers/sh/intc/virq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c index 93cec21e788b..f30ac9354ff2 100644 --- a/drivers/sh/intc/virq.c +++ b/drivers/sh/intc/virq.c @@ -219,12 +219,14 @@ restart: if (radix_tree_deref_retry(entry)) goto restart; - irq = create_irq(); + irq = irq_alloc_desc(numa_node_id()); if (unlikely(irq < 0)) { pr_err("no more free IRQs, bailing..\n"); break; } + activate_irq(irq); + pr_info("Setting up a chained VIRQ from %d -> %d\n", irq, entry->pirq); -- cgit v1.2.3 From ff1ffb850b7ac6d0f1b085208a6630f7584e1015 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 6 Jun 2012 09:42:57 +0200 Subject: iwlwifi: fix dynamic loading Add locking to the dynamic loading code to prevent corrupting the list if multiple device ever init at the same time (which cannot happen for multiple PCI devices, but could happen when different busses init concurrently.) Also remove a device from the list when it stops so the list isn't left corrupted, including a fix from Don to not crash when it was never added. Reviewed-by: Donald H Fry Tested-by: Donald H Fry Reviewed-by: Emmanuel Grumbach Signed-off-by: Don Fry Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-drv.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index cdfdfaec395e..67c9668d2e37 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -131,6 +131,8 @@ struct iwl_drv { #define DVM_OP_MODE 0 #define MVM_OP_MODE 1 +/* Protects the table contents, i.e. the ops pointer & drv list */ +static struct mutex iwlwifi_opmode_table_mtx; static struct iwlwifi_opmode_table { const char *name; /* name: iwldvm, iwlmvm, etc */ const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ @@ -899,6 +901,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) release_firmware(ucode_raw); complete(&drv->request_firmware_complete); + mutex_lock(&iwlwifi_opmode_table_mtx); op = &iwlwifi_opmode_table[DVM_OP_MODE]; /* add this device to the list of devices using this op_mode */ @@ -910,6 +913,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) } else { request_module_nowait("%s", op->name); } + mutex_unlock(&iwlwifi_opmode_table_mtx); return; @@ -944,6 +948,7 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, drv->cfg = cfg; init_completion(&drv->request_firmware_complete); + INIT_LIST_HEAD(&drv->list); ret = iwl_request_firmware(drv, true); @@ -966,6 +971,16 @@ void iwl_drv_stop(struct iwl_drv *drv) iwl_dealloc_ucode(drv); + mutex_lock(&iwlwifi_opmode_table_mtx); + /* + * List is empty (this item wasn't added) + * when firmware loading failed -- in that + * case we can't remove it from any list. + */ + if (!list_empty(&drv->list)) + list_del(&drv->list); + mutex_unlock(&iwlwifi_opmode_table_mtx); + kfree(drv); } @@ -988,6 +1003,7 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) int i; struct iwl_drv *drv; + mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { if (strcmp(iwlwifi_opmode_table[i].name, name)) continue; @@ -995,8 +1011,10 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) drv->op_mode = ops->start(drv->trans, drv->cfg, &drv->fw); + mutex_unlock(&iwlwifi_opmode_table_mtx); return 0; } + mutex_unlock(&iwlwifi_opmode_table_mtx); return -EIO; } EXPORT_SYMBOL_GPL(iwl_opmode_register); @@ -1006,6 +1024,7 @@ void iwl_opmode_deregister(const char *name) int i; struct iwl_drv *drv; + mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { if (strcmp(iwlwifi_opmode_table[i].name, name)) continue; @@ -1018,8 +1037,10 @@ void iwl_opmode_deregister(const char *name) drv->op_mode = NULL; } } + mutex_unlock(&iwlwifi_opmode_table_mtx); return; } + mutex_unlock(&iwlwifi_opmode_table_mtx); } EXPORT_SYMBOL_GPL(iwl_opmode_deregister); @@ -1027,6 +1048,8 @@ static int __init iwl_drv_init(void) { int i; + mutex_init(&iwlwifi_opmode_table_mtx); + for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); -- cgit v1.2.3 From 7eb89baa261ea8f09d7f6ad3c4db270fd7f3d0bd Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 6 Jun 2012 08:11:33 +0300 Subject: iwlwifi: s/iwl_read_targ_mem_words/iwl_read_targ_mem_bytes This macro gets the bufsize in bytes. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-io.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 656ed317c6d3..e620af3d592d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -406,7 +406,7 @@ static void iwl_continuous_event_trace(struct iwl_priv *priv) base = priv->device_pointers.log_event_table; if (iwlagn_hw_valid_rtc_data_addr(base)) { - iwl_read_targ_mem_words(priv->trans, base, &read, sizeof(read)); + iwl_read_targ_mem_bytes(priv->trans, base, &read, sizeof(read)); capacity = read.capacity; mode = read.mode; num_wraps = read.wrap_counter; @@ -1671,7 +1671,7 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv) } /*TODO: Update dbgfs with ISR error stats obtained below */ - iwl_read_targ_mem_words(trans, base, &table, sizeof(table)); + iwl_read_targ_mem_bytes(trans, base, &table, sizeof(table)); if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { IWL_ERR(trans, "Start IWL Error Log Dump:\n"); diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 4a9a45f771ed..45a59c940f81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -79,7 +79,7 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask); void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr, void *buf, int words); -#define iwl_read_targ_mem_words(trans, addr, buf, bufsize) \ +#define iwl_read_targ_mem_bytes(trans, addr, buf, bufsize) \ do { \ BUILD_BUG_ON((bufsize) % sizeof(u32)); \ _iwl_read_targ_mem_words(trans, addr, buf, \ -- cgit v1.2.3 From e4b1681eafa62e2b34710f70ab3494c89cc03130 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 6 Jun 2012 08:18:40 +0300 Subject: iwlwifi: iwl_{read,write}_targ_mem_words takes dwords Change its name to better reflect this. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +- drivers/net/wireless/iwlwifi/iwl-io.c | 20 ++++++++++---------- drivers/net/wireless/iwlwifi/iwl-io.h | 12 ++++++------ drivers/net/wireless/iwlwifi/iwl-test.c | 8 ++++---- 4 files changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index f60be3993e81..7f49526222a8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -473,7 +473,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) } if (priv->wowlan_sram) - _iwl_read_targ_mem_words( + _iwl_read_targ_mem_dwords( priv->trans, 0x800000, priv->wowlan_sram, img->sec[IWL_UCODE_SECTION_DATA].len / 4); diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 5f2df70b73c1..66c873399aba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -298,8 +298,8 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask) } EXPORT_SYMBOL_GPL(iwl_clear_bits_prph); -void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr, - void *buf, int words) +void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr, + void *buf, int dwords) { unsigned long flags; int offs; @@ -308,26 +308,26 @@ void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr, spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr); - for (offs = 0; offs < words; offs++) + for (offs = 0; offs < dwords; offs++) vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); iwl_release_nic_access(trans); } spin_unlock_irqrestore(&trans->reg_lock, flags); } -EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_words); +EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_dwords); u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr) { u32 value; - _iwl_read_targ_mem_words(trans, addr, &value, 1); + _iwl_read_targ_mem_dwords(trans, addr, &value, 1); return value; } EXPORT_SYMBOL_GPL(iwl_read_targ_mem); -int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr, - void *buf, int words) +int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr, + void *buf, int dwords) { unsigned long flags; int offs, result = 0; @@ -336,7 +336,7 @@ int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr, spin_lock_irqsave(&trans->reg_lock, flags); if (likely(iwl_grab_nic_access(trans))) { iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr); - for (offs = 0; offs < words; offs++) + for (offs = 0; offs < dwords; offs++) iwl_write32(trans, HBUS_TARG_MEM_WDAT, vals[offs]); iwl_release_nic_access(trans); } else @@ -345,10 +345,10 @@ int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr, return result; } -EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_words); +EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_dwords); int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val) { - return _iwl_write_targ_mem_words(trans, addr, &val, 1); + return _iwl_write_targ_mem_dwords(trans, addr, &val, 1); } EXPORT_SYMBOL_GPL(iwl_write_targ_mem); diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 45a59c940f81..50d3819739d1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -76,18 +76,18 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 reg, u32 bits, u32 mask); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask); -void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr, - void *buf, int words); +void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr, + void *buf, int dwords); #define iwl_read_targ_mem_bytes(trans, addr, buf, bufsize) \ do { \ BUILD_BUG_ON((bufsize) % sizeof(u32)); \ - _iwl_read_targ_mem_words(trans, addr, buf, \ - (bufsize) / sizeof(u32));\ + _iwl_read_targ_mem_dwords(trans, addr, buf, \ + (bufsize) / sizeof(u32));\ } while (0) -int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr, - void *buf, int words); +int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr, + void *buf, int dwords); u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr); int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val); diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c index 7a264aee2534..81e8c7126d72 100644 --- a/drivers/net/wireless/iwlwifi/iwl-test.c +++ b/drivers/net/wireless/iwlwifi/iwl-test.c @@ -476,9 +476,9 @@ static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size) iwl_release_nic_access(trans); spin_unlock_irqrestore(&trans->reg_lock, flags); } else { /* target memory (SRAM) */ - _iwl_read_targ_mem_words(trans, addr, - tst->mem.addr, - tst->mem.size / 4); + _iwl_read_targ_mem_dwords(trans, addr, + tst->mem.addr, + tst->mem.size / 4); } tst->mem.nchunks = @@ -522,7 +522,7 @@ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr, *(u32 *)(buf+i)); } } else if (iwl_test_valid_hw_addr(tst, addr)) { - _iwl_write_targ_mem_words(trans, addr, buf, size/4); + _iwl_write_targ_mem_dwords(trans, addr, buf, size / 4); } else { return -EINVAL; } -- cgit v1.2.3 From f22d33289ae61c79aa27170f7b9e4b2b2e95281d Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 10 Jun 2012 19:36:18 +0300 Subject: iwlwifi: print more info when a queue is stuck Print some more info from the SCD's SRAM and dump the TRB from the FH. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-fh.h | 2 ++ drivers/net/wireless/iwlwifi/pcie/trans.c | 13 ++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 74bce97a8600..806046641747 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -421,6 +421,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) #define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98) +#define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4) + /* Instruct FH to increment the retry count of a packet when * it is brought from the memory to TX-FIFO */ diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 1eabb834e32a..1904fdaf3177 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -298,6 +298,10 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) struct iwl_tx_queue *txq = (void *)data; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); + u32 scd_sram_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_MEM_LOWER_BOUND + (16 * txq->q.id); + u8 buf[16]; + int i; spin_lock(&txq->lock); /* check if triggered erroneously */ @@ -307,7 +311,6 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) } spin_unlock(&txq->lock); - IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, jiffies_to_msecs(trans_pcie->wd_timeout)); IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", @@ -317,6 +320,14 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) & (TFD_QUEUE_SIZE_MAX - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id))); + iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + + iwl_print_hex_error(trans, buf, sizeof(buf)); + + for (i = 0; i < FH_TCSR_CHNL_NUM; i++) + IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, + iwl_read_direct32(trans, FH_TX_TRB_REG(i))); + iwl_op_mode_nic_error(trans->op_mode); } -- cgit v1.2.3 From 1ce8658c08f1c1baa72e3cdea43a9d715dad08a2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 4 Jun 2012 16:48:17 +0300 Subject: iwlwifi: don't configure a txq that is being disabled This is not needed, we just need to tell the SCD not to use that queue. We will reconfigure that queue when we will use it again. Clean up a bit the code on the way. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/internal.h | 4 -- drivers/net/wireless/iwlwifi/pcie/tx.c | 74 ++++++++++++---------------- 2 files changed, 31 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 94201c4d6227..6c7b35530dad 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -340,10 +340,6 @@ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt); void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); -void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); -void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - int tx_fifo_id, bool active); void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 35e82161ca43..4efdeb996e48 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -380,8 +380,8 @@ static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; } -static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid, - u16 txq_id) +static int iwl_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, + u16 txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 tbl_dw_addr; @@ -405,7 +405,7 @@ static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid, return 0; } -static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id) +static inline void iwl_txq_set_inactive(struct iwl_trans *trans, u16 txq_id) { /* Simply stop the queue, but don't change any configuration; * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ @@ -415,33 +415,6 @@ static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id) (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } -void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index) -{ - IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d\n", txq_id, index & 0xff); - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (index & 0xff) | (txq_id << 8)); - iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), index); -} - -void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - int tx_fifo_id, bool active) -{ - int txq_id = txq->q.id; - - iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), - (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << SCD_QUEUE_STTS_REG_POS_WSL) | - SCD_QUEUE_STTS_REG_MSK); - - if (active) - IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d\n", - txq_id, tx_fifo_id); - else - IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); -} - void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn) @@ -454,7 +427,7 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, WARN_ONCE(1, "queue %d already used - expect issues", txq_id); /* Stop this Tx queue before configuring it */ - iwlagn_tx_queue_stop_scheduler(trans, txq_id); + iwl_txq_set_inactive(trans, txq_id); /* Set this queue as a chain-building queue unless it is CMD queue */ if (txq_id != trans_pcie->cmd_queue) @@ -465,17 +438,27 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ra_tid = BUILD_RAxTID(sta_id, tid); /* Map receiver-address / traffic-ID to this queue */ - iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id); + iwl_txq_set_ratid_map(trans, ra_tid, txq_id); /* enable aggregations for the queue */ iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + } else { + /* + * disable aggregations for the queue, this will also make the + * ra_tid mapping configuration irrelevant since it is now a + * non-AGG queue. + */ + iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); } /* Place first TFD at index corresponding to start sequence number. * Assumes that ssn_idx is valid (!= 0xFFF) */ trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); - iwl_trans_set_wr_ptrs(trans, txq_id, ssn); + + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (ssn & 0xff) | (txq_id << 8)); + iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); /* Set up Tx window size and frame limit for this queue */ iwl_write_targ_mem(trans, trans_pcie->scd_base_addr + @@ -488,8 +471,13 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], - fifo, true); + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (fifo << SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << SCD_QUEUE_STTS_REG_POS_WSL) | + SCD_QUEUE_STTS_REG_MSK); + IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n", + txq_id, fifo, ssn & 0xff); } void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, @@ -509,22 +497,22 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u16 rd_ptr, wr_ptr; + int n_bd = trans_pcie->txq[txq_id].q.n_bd; if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { WARN_ONCE(1, "queue %d not used", txq_id); return; } - iwlagn_tx_queue_stop_scheduler(trans, txq_id); - - iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); + rd_ptr = iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) & (n_bd - 1); + wr_ptr = iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)); - trans_pcie->txq[txq_id].q.read_ptr = 0; - trans_pcie->txq[txq_id].q.write_ptr = 0; - iwl_trans_set_wr_ptrs(trans, txq_id, 0); + WARN_ONCE(rd_ptr != wr_ptr, "queue %d isn't empty: [%d,%d]", + txq_id, rd_ptr, wr_ptr); - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], - 0, false); + iwl_txq_set_inactive(trans, txq_id); + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } /*************** HOST COMMAND QUEUE FUNCTIONS *****/ -- cgit v1.2.3 From 5bf9a89d9a2eaa136a23d872db4195ca8cada4c8 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 7 Jun 2012 13:44:14 +0300 Subject: iwlwifi: remove lock around txq_enable This locking isn't needed. The only locking we need is when we access prph registers but there is already a separate lock for that. Since we haven't returned from the mac80211's IEEE80211_AMPDU_TX_OPERATIONAL ampdu_action, we cannot receive any Tx frame for that sta / tid while enabling the queue. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/internal.h | 5 +---- drivers/net/wireless/iwlwifi/pcie/trans.c | 5 ++--- drivers/net/wireless/iwlwifi/pcie/tx.c | 21 ++------------------- 3 files changed, 5 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 6c7b35530dad..5024fb662bf6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -339,12 +339,9 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt); -void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); -void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, - int fifo, int sta_id, int tid, - int frame_limit, u16 ssn); void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); +void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 1904fdaf3177..4d4cbae83a07 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1068,9 +1068,8 @@ static void iwl_tx_start(struct iwl_trans *trans) for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { int fifo = trans_pcie->setup_q_to_fifo[i]; - __iwl_trans_pcie_txq_enable(trans, i, fifo, IWL_INVALID_STATION, - IWL_TID_NON_QOS, - SCD_FRAME_LIMIT, 0); + iwl_trans_pcie_txq_enable(trans, i, fifo, IWL_INVALID_STATION, + IWL_TID_NON_QOS, SCD_FRAME_LIMIT, 0); } /* Activate all Tx DMA/FIFO channels */ diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 4efdeb996e48..6baf8deef519 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -415,14 +415,11 @@ static inline void iwl_txq_set_inactive(struct iwl_trans *trans, u16 txq_id) (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } -void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, - int fifo, int sta_id, int tid, - int frame_limit, u16 ssn) +void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, + int sta_id, int tid, int frame_limit, u16 ssn) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - lockdep_assert_held(&trans_pcie->irq_lock); - if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); @@ -480,20 +477,6 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, txq_id, fifo, ssn & 0xff); } -void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, - int sta_id, int tid, int frame_limit, u16 ssn) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - __iwl_trans_pcie_txq_enable(trans, txq_id, fifo, sta_id, - tid, frame_limit, ssn); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); -} - void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); -- cgit v1.2.3 From 11741739391c2f03bbaba5349f868618fe63922f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 7 Jun 2012 14:23:06 +0300 Subject: iwlwifi: comment that setting driver_data overrides info->control Using the driver_data area in ieee80211_tx_info which resides in the CB overrides the info->control field. Add a comment to prevent mistakes. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/tx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 0dfaf649b257..1f9457fdb933 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -403,6 +403,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) info->driver_data[0] = ctx; info->driver_data[1] = dev_cmd; + /* From now on, we cannot access info->control */ spin_lock(&priv->sta_lock); -- cgit v1.2.3 From 65fe593a519c4d7c62289d3b0a17ca412e95b037 Mon Sep 17 00:00:00 2001 From: Brandon Misemer Date: Fri, 8 Jun 2012 14:59:27 -0700 Subject: iwlwifi: Fix Makefile build order for built-in driver When the driver is built into the kernel instead of a module when the system boots it results in a panic. The order things are built in results in their initialization order when built into the kernel. Wifi has to be initialized before mvm or dvm. Reviewed-by: Donald H Fry Tested-by: Donald H Fry Reviewed-by: Emmanuel Grumbach Signed-off-by: Brandon Misemer Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index afa9758364ea..170ec330d2a9 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,7 +1,3 @@ -obj-$(CONFIG_IWLDVM) += dvm/ - -CFLAGS_iwl-devtrace.o := -I$(src) - # common obj-$(CONFIG_IWLWIFI) += iwlwifi.o iwlwifi-objs += iwl-io.o @@ -16,3 +12,8 @@ iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src) + + +obj-$(CONFIG_IWLDVM) += dvm/ + +CFLAGS_iwl-devtrace.o := -I$(src) -- cgit v1.2.3 From 12af0468734dcef5d123d774f98e284deedd361c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 11 Jun 2012 11:44:49 +0300 Subject: iwlwifi: print even more info when a queue is stuck Since the queue gets stuck from time to time, we are trying to get as much information as we can when this occurs. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 4d4cbae83a07..d6a73179ebf1 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -315,10 +315,6 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) jiffies_to_msecs(trans_pcie->wd_timeout)); IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", txq->q.read_ptr, txq->q.write_ptr); - IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n", - iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq->q.id)) - & (TFD_QUEUE_SIZE_MAX - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id))); iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); @@ -328,6 +324,28 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, iwl_read_direct32(trans, FH_TX_TRB_REG(i))); + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); + u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + u32 tbl_dw = + iwl_read_targ_mem(trans, + trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(i)); + + if (i & 0x1) + tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; + else + tbl_dw = tbl_dw & 0x0000FFFF; + + IWL_ERR(trans, + "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", + i, active ? "" : "in", fifo, tbl_dw, + iwl_read_prph(trans, + SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); + } + iwl_op_mode_nic_error(trans->op_mode); } -- cgit v1.2.3 From f609607c009140491dd134f0165d9a8f9f726114 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 10 Jun 2012 14:25:22 +0300 Subject: iwlwifi: turn on a lockdep assertion CMD_SYNC is zero so the if (cmd->flags & CMD_SYNC) is never true and we never check the assertion. Signed-off-by: Dan Carpenter Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index cb1ca7a25dd5..76f259283c3a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -1265,7 +1265,7 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) * the mutex, this ensures we don't try to send two * (or more) synchronous commands at a time. */ - if (cmd->flags & CMD_SYNC) + if (!(cmd->flags & CMD_ASYNC)) lockdep_assert_held(&priv->mutex); if (priv->ucode_owner == IWL_OWNERSHIP_TM && -- cgit v1.2.3 From 49a4fc20df7fced31ffe4038b32b86a0438d1c27 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 10 Jun 2012 18:25:09 +0300 Subject: iwlwifi: don't modify the timer if we don't Tx In fragmentation we don't update the write pointer of the HW immediately. So we shouldn't modify the timer in that case. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index d6a73179ebf1..b647eb4dca64 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1354,7 +1354,8 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, skb->data + hdr_len, secondlen); /* start timer if queue currently empty */ - if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) + if (txq->need_update && q->read_ptr == q->write_ptr && + trans_pcie->wd_timeout) mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); /* Tell device the write index *just past* this latest filled TFD */ -- cgit v1.2.3 From 7bc057ffb5078d3e7f391ef2357f4ab01b9694e0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 10 Jun 2012 18:25:09 +0300 Subject: iwlwifi: warn if TFD index and WiFi Seq don't match For AGG queues, we must match between the WiFi sequence number and the TFD number. This is a HW (SCD) requirement. This is a take two of my iwlwifi: add debug in Tx path in AGG flow This will allow us to catch bad cases in which the packets aren't in the right place on the ring. which disappeared during code move. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index b647eb4dca64..9db8c6eaa153 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1261,6 +1261,19 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, spin_lock(&txq->lock); + /* In AGG mode, the index in the ring must correspond to the WiFi + * sequence number. This is a HW requirements to help the SCD to parse + * the BA. + * Check here that the packets are in the right place on the ring. + */ +#ifdef CONFIG_IWLWIFI_DEBUG + wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) && + ((wifi_seq & 0xff) != q->write_ptr), + "Q: %d WiFi Seq %d tfdNum %d", + txq_id, wifi_seq, q->write_ptr); +#endif + /* Set up driver data for this TFD */ txq->entries[q->write_ptr].skb = skb; txq->entries[q->write_ptr].cmd = dev_cmd; -- cgit v1.2.3 From 3c70d08795f9c81d8dc69b98a0e65175f7786aa3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 12 Jun 2012 21:43:28 +0200 Subject: iwlwifi: fix 6035 device parameters Due to commit 26a7ca9a71a ("iwlwifi: refactor EEPROM reading/parsing") adding a new parameter, while commit d2c8b15d0cb ("iwlwifi: use correct supported firmware for 6035 and 6000g2") added a new device structure we need to add the parameter to the new device structure to make 6035 device work. Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/6000.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/6000.c b/drivers/net/wireless/iwlwifi/pcie/6000.c index cb08ba03aae7..4a57624afc40 100644 --- a/drivers/net/wireless/iwlwifi/pcie/6000.c +++ b/drivers/net/wireless/iwlwifi/pcie/6000.c @@ -258,6 +258,7 @@ const struct iwl_cfg iwl6030_2bg_cfg = { .eeprom_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ .base_params = &iwl6000_g2_base_params, \ .bt_params = &iwl6000_bt_params, \ + .eeprom_params = &iwl6000_eeprom_params, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true -- cgit v1.2.3 From d4b10483223cf1a3fd2057785b12e2790a961867 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 12 Jun 2012 19:50:43 +0200 Subject: iwlwifi: use request_module instead of _nowait Since request_module_nowait() can't be backported use request_module() instead -- we don't need the asynchronous behaviour of request_module_nowait() here since we're running in the firmware request work struct. Tested-by: Donald H Fry Reviewed-by: Donald H Fry Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-drv.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 80898bb808b5..095547b37a27 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -778,6 +778,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) const unsigned int api_min = drv->cfg->ucode_api_min; u32 api_ver; int i; + bool load_module = false; fw->ucode_capa.max_probe_length = 200; fw->ucode_capa.standard_phy_calibration_size = @@ -913,7 +914,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) if (!drv->op_mode) goto out_unbind; } else { - request_module_nowait("%s", op->name); + load_module = true; } mutex_unlock(&iwlwifi_opmode_table_mtx); @@ -923,6 +924,14 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) * are doing the start() above. */ complete(&drv->request_firmware_complete); + + /* + * Load the module last so we don't block anything + * else from proceeding if the module fails to load + * or hangs loading. + */ + if (load_module) + request_module("%s", op->name); return; try_again: -- cgit v1.2.3 From 67711c17fc8bcab8f9e92c49049b8356f9a557c1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 12 Jun 2012 18:31:21 +0300 Subject: iwlwifi: WARN only once when we have trouble in reclaim This flow can actually happen due to a corner case in mac80211: the station is deleted before we get a chance to reclaim all the packets in flight in AGG queue. The tid_data for this station is zeroed, and we lose the match with the Tx queue. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/tx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 1f9457fdb933..5971a23aa47d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1183,7 +1183,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, } /*we can free until ssn % q.n_bd not inclusive */ - WARN_ON(iwl_reclaim(priv, sta_id, tid, txq_id, ssn, &skbs)); + WARN_ON_ONCE(iwl_reclaim(priv, sta_id, tid, + txq_id, ssn, &skbs)); iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; -- cgit v1.2.3 From ed88bed881c9948c4035828c5d63f60c7b015f86 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Jun 2012 19:26:33 +0300 Subject: x86/apic/irq_remap: Silence a bogus pr_err() There is an extra semicolon here so the pr_err() message is printed when it is not intended. Signed-off-by: Dan Carpenter Acked-by: Yinghai Lu Cc: Alexander Gordeev Cc: Suresh Siddha Cc: Joerg Roedel Link: http://lkml.kernel.org/r/20120612162633.GA11077@elgon.mountain Signed-off-by: Ingo Molnar --- drivers/iommu/intel_irq_remapping.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index dafbad06390a..853902a1b7db 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -938,7 +938,7 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, err = apic->cpu_mask_to_apicid_and(cfg->domain, mask, &dest); if (err) { - if (assign_irq_vector(irq, cfg, data->affinity)); + if (assign_irq_vector(irq, cfg, data->affinity)) pr_err("Failed to recover vector for irq %d\n", irq); return err; } -- cgit v1.2.3 From 0ff472467a04dfecffbf44d2414c756c83212cbe Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jun 2012 17:18:49 +0800 Subject: regulator: wm8350: Add missing min_uV and uV_step settings for DCDC4 Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index ca678eea79de..53bf3df02975 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1105,6 +1105,8 @@ static const struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .irq = WM8350_IRQ_UV_DC4, .type = REGULATOR_VOLTAGE, .n_voltages = WM8350_DCDC_MAX_VSEL + 1, + .min_uV = 850000, + .uV_step = 25000, .vsel_reg = WM8350_DCDC4_CONTROL, .vsel_mask = WM8350_DC4_VSEL_MASK, .enable_reg = WM8350_DCDC_LDO_REQUESTED, -- cgit v1.2.3 From 3fe3a182adfeca84f39751af03c8571831a0877f Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Wed, 13 Jun 2012 17:26:15 +0900 Subject: regulator: Remove s5m8767a buck initialization As s5m8767a is revisioned, remove unnecessary buck 2,3,4 initialization routine. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 290d6fc01029..4edf344fb7c4 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -668,9 +668,6 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck4_vol[i]); } } - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, 0x78, 0xff); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, 0x58, 0xff); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, 0x78, 0xff); if (s5m8767->buck2_ramp) s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, 0x08, 0x08); -- cgit v1.2.3 From 38c20eb23fb7b5505ac80595f18f4209abc19cd3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jun 2012 16:34:55 +0800 Subject: regulator: wm8400: Use wm8400_ldo_list_voltage instead of open code to verify selected voltage Call wm8400_ldo_list_voltage() instead of open code to verify selected voltage falls within specified range. Use wm8400_ldo_list_voltage() here is less error prone. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8400-regulator.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index f365795d51c2..9f9df8eef1a1 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -35,27 +35,19 @@ static int wm8400_ldo_map_voltage(struct regulator_dev *dev, int min_uV, int max_uV) { u16 val; + int volt; if (min_uV < 900000 || min_uV > 3300000) return -EINVAL; - if (min_uV < 1700000) { - /* Steps of 50mV from 900mV; */ + if (min_uV < 1700000) /* Steps of 50mV from 900mV; */ val = DIV_ROUND_UP(min_uV - 900000, 50000); + else /* Steps of 100mV from 1700mV */ + val = DIV_ROUND_UP(min_uV - 1700000, 100000) + 15; - if ((val * 50000) + 900000 > max_uV) - return -EINVAL; - BUG_ON((val * 50000) + 900000 < min_uV); - } else { - /* Steps of 100mV from 1700mV */ - val = DIV_ROUND_UP(min_uV - 1700000, 100000); - - if ((val * 100000) + 1700000 > max_uV) - return -EINVAL; - BUG_ON((val * 100000) + 1700000 < min_uV); - - val += 0xf; - } + volt = wm8400_ldo_list_voltage(dev, val); + if (volt < min_uV || volt > max_uV) + return -EINVAL; return val; } -- cgit v1.2.3 From df2643cfa4ad4f3775838ad0c0815c465f351f92 Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Wed, 13 Jun 2012 17:28:18 +0900 Subject: regulator: Replace set_voltage with set_voltage_sel To apply delay time for voltage change, replace s5m8767_set_voltage with s5m8767_set_voltage_sel. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 4edf344fb7c4..a4a3c7eefd1f 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -357,32 +357,34 @@ static int s5m8767_convert_voltage_to_sel( return selector; } -static inline void s5m8767_set_high(struct s5m8767_info *s5m8767) +static inline int s5m8767_set_high(struct s5m8767_info *s5m8767) { int temp_index = s5m8767->buck_gpioindex; gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); + + return 0; } -static inline void s5m8767_set_low(struct s5m8767_info *s5m8767) +static inline int s5m8767_set_low(struct s5m8767_info *s5m8767) { int temp_index = s5m8767->buck_gpioindex; gpio_set_value(s5m8767->buck_gpios[2], temp_index & 0x1); gpio_set_value(s5m8767->buck_gpios[1], (temp_index >> 1) & 0x1); gpio_set_value(s5m8767->buck_gpios[0], (temp_index >> 2) & 0x1); + + return 0; } -static int s5m8767_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) +static int s5m8767_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) { struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev); - const struct s5m_voltage_desc *desc; int reg_id = rdev_get_id(rdev); - int sel, reg, mask, ret = 0, old_index, index = 0; - u8 val; + int reg, mask, ret = 0, old_index, index = 0; u8 *buck234_vol = NULL; switch (reg_id) { @@ -407,15 +409,9 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, return -EINVAL; } - desc = reg_voltage_map[reg_id]; - - sel = s5m8767_convert_voltage_to_sel(desc, min_uV, max_uV); - if (sel < 0) - return sel; - /* buck234_vol != NULL means to control buck234 voltage via DVS GPIO */ if (buck234_vol) { - while (*buck234_vol != sel) { + while (*buck234_vol != selector) { buck234_vol++; index++; } @@ -423,22 +419,16 @@ static int s5m8767_set_voltage(struct regulator_dev *rdev, s5m8767->buck_gpioindex = index; if (index > old_index) - s5m8767_set_high(s5m8767); + return s5m8767_set_high(s5m8767); else - s5m8767_set_low(s5m8767); + return s5m8767_set_low(s5m8767); } else { ret = s5m8767_get_voltage_register(rdev, ®); if (ret) return ret; - s5m_reg_read(s5m8767->iodev, reg, &val); - val = (val & ~mask) | sel; - - ret = s5m_reg_write(s5m8767->iodev, reg, val); + return s5m_reg_update(s5m8767->iodev, reg, selector, mask); } - - *selector = sel; - return ret; } static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, @@ -463,7 +453,7 @@ static struct regulator_ops s5m8767_ops = { .enable = s5m8767_reg_enable, .disable = s5m8767_reg_disable, .get_voltage_sel = s5m8767_get_voltage_sel, - .set_voltage = s5m8767_set_voltage, + .set_voltage_sel = s5m8767_set_voltage_sel, .set_voltage_time_sel = s5m8767_set_voltage_time_sel, }; -- cgit v1.2.3 From 74e20e569158063970624e360c8395ac5cffd654 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jun 2012 16:36:20 +0800 Subject: regulator: wm8400: Adjust the equation for selector >= 15 in wm8400_ldo_list_voltage Adjust the equation for selector >= 15 for better readability. The equation "1700000 + ((selector - 15) * 100000)" can be interpreted by: Starting from selector 15: Steps of 100mV from 1700mV Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8400-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 9f9df8eef1a1..9035dd053611 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -28,7 +28,7 @@ static int wm8400_ldo_list_voltage(struct regulator_dev *dev, if (selector < 15) return 900000 + (selector * 50000); else - return 1600000 + ((selector - 14) * 100000); + return 1700000 + ((selector - 15) * 100000); } static int wm8400_ldo_map_voltage(struct regulator_dev *dev, -- cgit v1.2.3 From b4bc9ef6253578ecc71eec79a7dd423d0a282a4b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 11:27:11 +0800 Subject: regulator: tps65217: Convert to regulator_[is_enabled|get_voltage_sel]_regmap This patch converts .is_enabled and .get_voltage_sel to regulator_is_enabled_regmap and regulator_get_voltage_sel_regmap. For .enable, .disable, and .set_voltage_sel, the write protect level is either 1 or 2. So we cannot use regulator_[enable|disable|set_voltage_sel]_regmap. Now we store the enable reg/mask and vsel reg/mask in regulator_desc, so we can remove enable_mask, set_vout_reg, and set_vout_mask from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 114 ++++++++++++--------------------- 1 file changed, 42 insertions(+), 72 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 9d371d2cbcae..f5fa05b5bea4 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -26,7 +26,7 @@ #include #include -#define TPS65217_REGULATOR(_name, _id, _ops, _n) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em) \ { \ .name = _name, \ .id = _id, \ @@ -34,9 +34,13 @@ .n_voltages = _n, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .enable_reg = TPS65217_REG_ENABLE, \ + .enable_mask = _em, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n, _em, _vr, _vm) \ +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n)\ { \ .name = _nm, \ .min_uV = _min, \ @@ -45,9 +49,6 @@ .uv_to_vsel = _f2, \ .table = _t, \ .table_len = _n, \ - .enable_mask = _em, \ - .set_vout_reg = _vr, \ - .set_vout_mask = _vm, \ } static const int LDO1_VSEL_table[] = { @@ -127,46 +128,21 @@ static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) static struct tps_info tps65217_pmic_regs[] = { TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC1_EN, - TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC2_EN, - TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_DC3_EN, - TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, - 16, TPS65217_ENABLE_LDO1_EN, TPS65217_REG_DEFLDO1, - TPS65217_DEFLDO1_LDO1_MASK), + 16), TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64, TPS65217_ENABLE_LDO2_EN, - TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK), + tps65217_uv_to_vsel1, NULL, 64), TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32, - TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, - TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK), + tps65217_uv_to_vsel2, NULL, 32), TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32, - TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, - TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK), + tps65217_uv_to_vsel2, NULL, 32), }; -static int tps65217_pmic_is_enabled(struct regulator_dev *dev) -{ - int ret; - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int data, rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - ret = tps65217_reg_read(tps, TPS65217_REG_ENABLE, &data); - if (ret) - return ret; - - return (data & tps->info[rid]->enable_mask) ? 1 : 0; -} - static int tps65217_pmic_enable(struct regulator_dev *dev) { struct tps65217 *tps = rdev_get_drvdata(dev); @@ -177,9 +153,8 @@ static int tps65217_pmic_enable(struct regulator_dev *dev) /* Enable the regulator and password protection is level 1 */ return tps65217_set_bits(tps, TPS65217_REG_ENABLE, - tps->info[rid]->enable_mask, - tps->info[rid]->enable_mask, - TPS65217_PROTECT_L1); + dev->desc->enable_mask, dev->desc->enable_mask, + TPS65217_PROTECT_L1); } static int tps65217_pmic_disable(struct regulator_dev *dev) @@ -192,25 +167,7 @@ static int tps65217_pmic_disable(struct regulator_dev *dev) /* Disable the regulator and password protection is level 1 */ return tps65217_clear_bits(tps, TPS65217_REG_ENABLE, - tps->info[rid]->enable_mask, TPS65217_PROTECT_L1); -} - -static int tps65217_pmic_get_voltage_sel(struct regulator_dev *dev) -{ - int ret; - struct tps65217 *tps = rdev_get_drvdata(dev); - unsigned int selector, rid = rdev_get_id(dev); - - if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) - return -EINVAL; - - ret = tps65217_reg_read(tps, tps->info[rid]->set_vout_reg, &selector); - if (ret) - return ret; - - selector &= tps->info[rid]->set_vout_mask; - - return selector; + dev->desc->enable_mask, TPS65217_PROTECT_L1); } static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, @@ -221,8 +178,7 @@ static int tps65217_pmic_set_voltage_sel(struct regulator_dev *dev, unsigned int rid = rdev_get_id(dev); /* Set the voltage based on vsel value and write protect level is 2 */ - ret = tps65217_set_bits(tps, tps->info[rid]->set_vout_reg, - tps->info[rid]->set_vout_mask, + ret = tps65217_set_bits(tps, dev->desc->vsel_reg, dev->desc->vsel_mask, selector, TPS65217_PROTECT_L2); /* Set GO bit for DCDCx to initiate voltage transistion */ @@ -285,10 +241,10 @@ static int tps65217_pmic_list_voltage(struct regulator_dev *dev, /* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */ static struct regulator_ops tps65217_pmic_ops = { - .is_enabled = tps65217_pmic_is_enabled, + .is_enabled = regulator_is_enabled_regmap, .enable = tps65217_pmic_enable, .disable = tps65217_pmic_disable, - .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = tps65217_pmic_list_voltage, .map_voltage = tps65217_pmic_map_voltage, @@ -296,22 +252,36 @@ static struct regulator_ops tps65217_pmic_ops = { /* Operations permitted on LDO1 */ static struct regulator_ops tps65217_pmic_ldo1_ops = { - .is_enabled = tps65217_pmic_is_enabled, + .is_enabled = regulator_is_enabled_regmap, .enable = tps65217_pmic_enable, .disable = tps65217_pmic_disable, - .get_voltage_sel = tps65217_pmic_get_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = tps65217_pmic_list_voltage, }; static const struct regulator_desc regulators[] = { - TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16), - TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64), - TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32), - TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32), + TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC1_EN), + TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC2_EN), + TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, + TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, + TPS65217_ENABLE_DC3_EN), + TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, + TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, + TPS65217_ENABLE_LDO1_EN), + TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, + TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, + TPS65217_ENABLE_LDO2_EN), + TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN), + TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, + TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN), }; static int __devinit tps65217_regulator_probe(struct platform_device *pdev) -- cgit v1.2.3 From e8790ab4ce2a46505a8e1479645414425dbc81f0 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 22:57:24 +0800 Subject: regmap: mmio: Staticize regmap_mmio_gen_context() regmap_mmio_gen_context() is only used in regmap-mmio.c. Thus make it static. Signed-off-by: Axel Lin Acked-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index eec86639cac4..d64a7fc71df8 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -128,7 +128,7 @@ static struct regmap_bus regmap_mmio = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; -struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, +static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, const struct regmap_config *config) { struct regmap_mmio_context *ctx; -- cgit v1.2.3 From e001f1c8e9c1d3b2f0c638bc712bcac10294aae3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 7 Jun 2012 17:57:36 -0600 Subject: of: export of_platform_populate() Without this, modules can't use this API, leading to build failures. Signed-off-by: Stephen Warren Signed-off-by: Rob Herring --- drivers/of/platform.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 343ad29e211c..3132ea068d95 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -462,4 +462,5 @@ int of_platform_populate(struct device_node *root, of_node_put(root); return rc; } +EXPORT_SYMBOL_GPL(of_platform_populate); #endif /* CONFIG_OF_ADDRESS */ -- cgit v1.2.3 From d597ee7e28d2576ab075abdebab96d3cb65f1b54 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 9 Jun 2012 22:51:41 +0200 Subject: brcmsmac: remove brcms_set_hint() function The function brcms_set_hint() does not add any functionality so regulatory_hint() can be called directly. The error value has been removed from the message when regulatory_hint() fails. Reported-by: Seth Forshee Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 50f92a0b7c41..341e06a0d6ec 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -721,14 +721,6 @@ static const struct ieee80211_ops brcms_ops = { .flush = brcms_ops_flush, }; -/* - * is called in brcms_bcma_probe() context, therefore no locking required. - */ -static int brcms_set_hint(struct brcms_info *wl, char *abbrev) -{ - return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev); -} - void brcms_dpc(unsigned long data) { struct brcms_info *wl; @@ -1068,9 +1060,9 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev) wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status" "%d\n", __func__, err); - if (wl->pub->srom_ccode[0] && brcms_set_hint(wl, wl->pub->srom_ccode)) - wiphy_err(wl->wiphy, "%s: regulatory_hint failed, status %d\n", - __func__, err); + if (wl->pub->srom_ccode[0] && + regulatory_hint(wl->wiphy, wl->pub->srom_ccode)) + wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__); n_adapters_found++; return wl; -- cgit v1.2.3 From 5e16e8ee3a14b59366d25f075926da3ca5d03d11 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 9 Jun 2012 22:51:42 +0200 Subject: brcmsmac: fix smatch warning found in ampdu.c This patch fixes potential NULL pointer dereference in ampdu. This was found running smatch static code checker. Smatch warning says: drivers/net/wireless/brcm80211/brcmsmac/ampdu.c:741 brcms_c_sendampdu() warn: variable dereferenced before check 'p' Reported-by: Dan Carpenter Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c index 95b5902bc4b3..01b190a25d94 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c @@ -735,10 +735,8 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, * a candidate for aggregation */ p = pktq_ppeek(&qi->q, prec); - /* tx_info must be checked with current p */ - tx_info = IEEE80211_SKB_CB(p); - if (p) { + tx_info = IEEE80211_SKB_CB(p); if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && ((u8) (p->priority) == tid)) { plen = p->len + AMPDU_MAX_MPDU_OVERHEAD; @@ -759,6 +757,7 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, p = NULL; continue; } + /* next packet fit for aggregation so dequeue */ p = brcmu_pktq_pdeq(&qi->q, prec); } else { p = NULL; -- cgit v1.2.3 From d319a7cfd9d80a2e2359e978ba88991531312ee6 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 9 Jun 2012 22:51:43 +0200 Subject: brcmfmac: add debugfs helper functions This patch adds debugfs support to brcmfmac. It provide helper functions to setup the debugfs folder structure for the driver, which has following hierarchy: /brcmfmac// ie.: /sys/kernel/debug/brcmfmac/mmc0:0001:2/ The new source file provides functions to create and remove the two folders and a function to retrieve the device-specific folder so files can be created in it. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 + drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 3 ++ drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 63 ++++++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 23 ++++++++ .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 7 +++ 5 files changed, 98 insertions(+) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index abb48032753b..9d5170b6df50 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -34,3 +34,5 @@ brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ sdio_chip.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o +brcmfmac-$(CONFIG_BRCMDBG) += \ + dhd_dbg.o \ No newline at end of file diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 9f637014486e..a11fe54f5950 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -613,6 +613,9 @@ struct brcmf_pub { struct work_struct multicast_work; u8 macvalue[ETH_ALEN]; atomic_t pend_8021x_cnt; +#ifdef DEBUG + struct dentry *dbgfs_dir; +#endif }; struct brcmf_if_event { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c new file mode 100644 index 000000000000..0a7a3d50df6e --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2012 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include + +#include +#include +#include +#include "dhd.h" +#include "dhd_bus.h" + +static struct dentry *root_folder; + +void brcmf_debugfs_init(void) +{ + root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (IS_ERR(root_folder)) + root_folder = NULL; +} + +void brcmf_debugfs_exit(void) +{ + if (!root_folder) + return; + + debugfs_remove_recursive(root_folder); + root_folder = NULL; +} + +int brcmf_debugfs_attach(struct brcmf_pub *drvr) +{ + if (!root_folder) + return -ENODEV; + + drvr->dbgfs_dir = debugfs_create_dir(dev_name(drvr->dev), root_folder); + return PTR_RET(drvr->dbgfs_dir); +} + +void brcmf_debugfs_detach(struct brcmf_pub *drvr) +{ + if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) + debugfs_remove_recursive(drvr->dbgfs_dir); +} + +struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) +{ + return drvr->dbgfs_dir; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index a2c4576cf9ff..0efb226030a4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -76,4 +76,27 @@ do { \ extern int brcmf_msg_level; +struct brcmf_pub; +#ifdef DEBUG +void brcmf_debugfs_init(void); +void brcmf_debugfs_exit(void); +int brcmf_debugfs_attach(struct brcmf_pub *drvr); +void brcmf_debugfs_detach(struct brcmf_pub *drvr); +struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); +#else +static inline void brcmf_debugfs_init(void) +{ +} +static inline void brcmf_debugfs_exit(void) +{ +} +static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) +{ + return 0; +} +static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) +{ +} +#endif + #endif /* _BRCMF_DBG_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 8933f9b31a9a..01cf6c03390b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -1007,6 +1007,9 @@ int brcmf_attach(uint bus_hdrlen, struct device *dev) drvr->bus_if->drvr = drvr; drvr->dev = dev; + /* create device debugfs folder */ + brcmf_debugfs_attach(drvr); + /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); if (ret != 0) { @@ -1123,6 +1126,7 @@ void brcmf_detach(struct device *dev) brcmf_proto_detach(drvr); } + brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; kfree(drvr); } @@ -1192,6 +1196,8 @@ exit: static void brcmf_driver_init(struct work_struct *work) { + brcmf_debugfs_init(); + #ifdef CONFIG_BRCMFMAC_SDIO brcmf_sdio_init(); #endif @@ -1219,6 +1225,7 @@ static void __exit brcmfmac_module_exit(void) #ifdef CONFIG_BRCMFMAC_USB brcmf_usb_exit(); #endif + brcmf_debugfs_exit(); } module_init(brcmfmac_module_init); -- cgit v1.2.3 From 80969836a7ddde326d3f289d234e9e2d9ed27272 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 9 Jun 2012 22:51:44 +0200 Subject: brcmfmac: expose sdio internal counters in debugfs The structure brcmf_sdio contains a number of counters that are useful for debugging. These were not available in user-space. This patch exposes them in debugfs under the filename 'counters'. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 63 ++++++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 36 +++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 169 +++++++++------------ 3 files changed, 175 insertions(+), 93 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 0a7a3d50df6e..7f89540b56da 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -17,12 +17,14 @@ #include #include #include +#include #include #include #include #include "dhd.h" #include "dhd_bus.h" +#include "dhd_dbg.h" static struct dentry *root_folder; @@ -61,3 +63,64 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { return drvr->dbgfs_dir; } + +static +ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data, + size_t count, loff_t *ppos) +{ + struct brcmf_sdio_count *sdcnt = f->private_data; + char buf[750]; + int res; + + /* only allow read from start */ + if (*ppos > 0) + return 0; + + res = scnprintf(buf, sizeof(buf), + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + + return simple_read_from_buffer(data, count, ppos, buf, res); +} + +static const struct file_operations brcmf_debugfs_sdio_counter_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = brcmf_debugfs_sdio_counter_read +}; + +void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, + struct brcmf_sdio_count *sdcnt) +{ + struct dentry *dentry = drvr->dbgfs_dir; + + if (!IS_ERR_OR_NULL(dentry)) + debugfs_create_file("counters", S_IRUGO, dentry, + sdcnt, &brcmf_debugfs_sdio_counter_ops); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 0efb226030a4..b784920532d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -76,6 +76,40 @@ do { \ extern int brcmf_msg_level; +/* + * hold counter variables used in brcmfmac sdio driver. + */ +struct brcmf_sdio_count { + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint pollcnt; /* Count of active polls */ + uint regfails; /* Count of R_REG failures */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + uint tickcnt; /* Number of watchdog been schedule */ + ulong tx_ctlerrs; /* Err of sending ctrl frames */ + ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ + ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ + ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ + ulong rx_readahead_cnt; /* packets where header read-ahead was used */ +}; + struct brcmf_pub; #ifdef DEBUG void brcmf_debugfs_init(void); @@ -83,6 +117,8 @@ void brcmf_debugfs_exit(void); int brcmf_debugfs_attach(struct brcmf_pub *drvr); void brcmf_debugfs_detach(struct brcmf_pub *drvr); struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); +void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, + struct brcmf_sdio_count *sdcnt); #else static inline void brcmf_debugfs_init(void) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1dbf2be478c8..a07fb01bc36b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -502,12 +502,9 @@ struct brcmf_sdio { bool intr; /* Use interrupts */ bool poll; /* Use polling */ bool ipend; /* Device interrupt is pending */ - uint intrcount; /* Count of device interrupt callbacks */ - uint lastintrs; /* Count as of last watchdog timer */ uint spurious; /* Count of spurious interrupts */ uint pollrate; /* Ticks between device polls */ uint polltick; /* Tick counter */ - uint pollcnt; /* Count of active polls */ #ifdef DEBUG uint console_interval; @@ -515,8 +512,6 @@ struct brcmf_sdio { uint console_addr; /* Console address from shared struct */ #endif /* DEBUG */ - uint regfails; /* Count of R_REG failures */ - uint clkstate; /* State of sd and backplane clock(s) */ bool activity; /* Activity flag for clock down */ s32 idletime; /* Control for activity timeout */ @@ -531,33 +526,6 @@ struct brcmf_sdio { /* Field to decide if rx of control frames happen in rxbuf or lb-pool */ bool usebufpool; - /* Some additional counters */ - uint tx_sderrs; /* Count of tx attempts with sd errors */ - uint fcqueued; /* Tx packets that got queued */ - uint rxrtx; /* Count of rtx requests (NAK to dongle) */ - uint rx_toolong; /* Receive frames too long to receive */ - uint rxc_errors; /* SDIO errors when reading control frames */ - uint rx_hdrfail; /* SDIO errors on header reads */ - uint rx_badhdr; /* Bad received headers (roosync?) */ - uint rx_badseq; /* Mismatched rx sequence number */ - uint fc_rcvd; /* Number of flow-control events received */ - uint fc_xoff; /* Number which turned on flow-control */ - uint fc_xon; /* Number which turned off flow-control */ - uint rxglomfail; /* Failed deglom attempts */ - uint rxglomframes; /* Number of glom frames (superframes) */ - uint rxglompkts; /* Number of packets from glom frames */ - uint f2rxhdrs; /* Number of header reads */ - uint f2rxdata; /* Number of frame data reads */ - uint f2txdata; /* Number of f2 frame writes */ - uint f1regdata; /* Number of f1 register accesses */ - uint tickcnt; /* Number of watchdog been schedule */ - unsigned long tx_ctlerrs; /* Err of sending ctrl frames */ - unsigned long tx_ctlpkts; /* Ctrl frames sent to dongle */ - unsigned long rx_ctlerrs; /* Err of processing rx ctrl frames */ - unsigned long rx_ctlpkts; /* Ctrl frames processed from dongle */ - unsigned long rx_readahead_cnt; /* Number of packets where header - * read-ahead was used. */ - u8 *ctrl_frame_buf; u32 ctrl_frame_len; bool ctrl_frame_stat; @@ -583,6 +551,7 @@ struct brcmf_sdio { u32 fw_ptr; bool txoff; /* Transmit flow-controlled */ + struct brcmf_sdio_count sdcnt; }; /* clkstate */ @@ -945,7 +914,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) if (ret == 0) w_sdreg32(bus, SMB_INT_ACK, offsetof(struct sdpcmd_regs, tosbmailbox)); - bus->f1regdata += 2; + bus->sdcnt.f1regdata += 2; /* Dongle recomposed rx frames, accept them again */ if (hmb_data & HMB_DATA_NAKHANDLED) { @@ -984,12 +953,12 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) HMB_DATA_FCDATA_SHIFT; if (fcbits & ~bus->flowcontrol) - bus->fc_xoff++; + bus->sdcnt.fc_xoff++; if (bus->flowcontrol & ~fcbits) - bus->fc_xon++; + bus->sdcnt.fc_xon++; - bus->fc_rcvd++; + bus->sdcnt.fc_rcvd++; bus->flowcontrol = fcbits; } @@ -1021,7 +990,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM, &err); - bus->f1regdata++; + bus->sdcnt.f1regdata++; /* Wait until the packet has been flushed (device/FIFO stable) */ for (lastrbc = retries = 0xffff; retries > 0; retries--) { @@ -1029,7 +998,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) SBSDIO_FUNC1_RFRAMEBCHI, &err); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_RFRAMEBCLO, &err); - bus->f1regdata += 2; + bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; @@ -1047,11 +1016,11 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries); if (rtx) { - bus->rxrtx++; + bus->sdcnt.rxrtx++; err = w_sdreg32(bus, SMB_NAK, offsetof(struct sdpcmd_regs, tosbmailbox)); - bus->f1regdata++; + bus->sdcnt.f1regdata++; if (err == 0) bus->rxskip = true; } @@ -1243,7 +1212,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) dlen); errcode = -1; } - bus->f2rxdata++; + bus->sdcnt.f2rxdata++; /* On failure, kill the superframe, allow a couple retries */ if (errcode < 0) { @@ -1256,7 +1225,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } else { bus->glomerr = 0; brcmf_sdbrcm_rxfail(bus, true, false); - bus->rxglomfail++; + bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } return 0; @@ -1312,7 +1281,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (rxseq != seq) { brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n", seq, rxseq); - bus->rx_badseq++; + bus->sdcnt.rx_badseq++; rxseq = seq; } @@ -1376,7 +1345,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } else { bus->glomerr = 0; brcmf_sdbrcm_rxfail(bus, true, false); - bus->rxglomfail++; + bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } bus->nextlen = 0; @@ -1402,7 +1371,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (rxseq != seq) { brcmf_dbg(GLOM, "rx_seq %d, expected %d\n", seq, rxseq); - bus->rx_badseq++; + bus->sdcnt.rx_badseq++; rxseq = seq; } rxseq++; @@ -1441,8 +1410,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) down(&bus->sdsem); } - bus->rxglomframes++; - bus->rxglompkts += bus->glom.qlen; + bus->sdcnt.rxglomframes++; + bus->sdcnt.rxglompkts += bus->glom.qlen; } return num; } @@ -1526,7 +1495,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) brcmf_dbg(ERROR, "%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", len, len - doff, bus->sdiodev->bus_if->maxctl); bus->sdiodev->bus_if->dstats.rx_errors++; - bus->rx_toolong++; + bus->sdcnt.rx_toolong++; brcmf_sdbrcm_rxfail(bus, false, false); goto done; } @@ -1536,13 +1505,13 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen); - bus->f2rxdata++; + bus->sdcnt.f2rxdata++; /* Control frame failures need retransmission */ if (sdret < 0) { brcmf_dbg(ERROR, "read %d control bytes failed: %d\n", rdlen, sdret); - bus->rxc_errors++; + bus->sdcnt.rxc_errors++; brcmf_sdbrcm_rxfail(bus, true, true); goto done; } @@ -1589,7 +1558,7 @@ brcmf_alloc_pkt_and_read(struct brcmf_sdio *bus, u16 rdlen, /* Read the entire frame */ sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, *pkt); - bus->f2rxdata++; + bus->sdcnt.f2rxdata++; if (sdret < 0) { brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n", @@ -1630,7 +1599,7 @@ brcmf_check_rxbuf(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *rxbuf, if ((u16)~(*len ^ check)) { brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n", nextlen, *len, check); - bus->rx_badhdr++; + bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); goto fail; } @@ -1746,7 +1715,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) bus->nextlen = 0; } - bus->rx_readahead_cnt++; + bus->sdcnt.rx_readahead_cnt++; /* Handle Flow Control */ fcbits = SDPCM_FCMASK_VALUE( @@ -1754,12 +1723,12 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) if (bus->flowcontrol != fcbits) { if (~bus->flowcontrol & fcbits) - bus->fc_xoff++; + bus->sdcnt.fc_xoff++; if (bus->flowcontrol & ~fcbits) - bus->fc_xon++; + bus->sdcnt.fc_xon++; - bus->fc_rcvd++; + bus->sdcnt.fc_rcvd++; bus->flowcontrol = fcbits; } @@ -1767,7 +1736,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) if (rxseq != seq) { brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n", seq, rxseq); - bus->rx_badseq++; + bus->sdcnt.rx_badseq++; rxseq = seq; } @@ -1814,11 +1783,11 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->rxhdr, BRCMF_FIRSTREAD); - bus->f2rxhdrs++; + bus->sdcnt.f2rxhdrs++; if (sdret < 0) { brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret); - bus->rx_hdrfail++; + bus->sdcnt.rx_hdrfail++; brcmf_sdbrcm_rxfail(bus, true, true); continue; } @@ -1840,7 +1809,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) if ((u16) ~(len ^ check)) { brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n", len, check); - bus->rx_badhdr++; + bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); continue; } @@ -1861,7 +1830,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) if ((doff < SDPCM_HDRLEN) || (doff > len)) { brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n", doff, len, SDPCM_HDRLEN, seq); - bus->rx_badhdr++; + bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); continue; } @@ -1880,19 +1849,19 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) if (bus->flowcontrol != fcbits) { if (~bus->flowcontrol & fcbits) - bus->fc_xoff++; + bus->sdcnt.fc_xoff++; if (bus->flowcontrol & ~fcbits) - bus->fc_xon++; + bus->sdcnt.fc_xon++; - bus->fc_rcvd++; + bus->sdcnt.fc_rcvd++; bus->flowcontrol = fcbits; } /* Check and update sequence number */ if (rxseq != seq) { brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq); - bus->rx_badseq++; + bus->sdcnt.rx_badseq++; rxseq = seq; } @@ -1937,7 +1906,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) brcmf_dbg(ERROR, "too long: len %d rdlen %d\n", len, rdlen); bus->sdiodev->bus_if->dstats.rx_errors++; - bus->rx_toolong++; + bus->sdcnt.rx_toolong++; brcmf_sdbrcm_rxfail(bus, false, false); continue; } @@ -1960,7 +1929,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) /* Read the remaining frame data */ sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); - bus->f2rxdata++; + bus->sdcnt.f2rxdata++; if (sdret < 0) { brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen, @@ -2147,18 +2116,18 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); - bus->f2txdata++; + bus->sdcnt.f2txdata++; if (ret < 0) { /* On failure, abort the command and terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", ret); - bus->tx_sderrs++; + bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); - bus->f1regdata++; + bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; @@ -2166,7 +2135,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, SBSDIO_FUNC1_WFRAMEBCHI, NULL); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->f1regdata += 2; + bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; } @@ -2224,7 +2193,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) ret = r_sdreg32(bus, &intstatus, offsetof(struct sdpcmd_regs, intstatus)); - bus->f2txdata++; + bus->sdcnt.f2txdata++; if (ret != 0) break; if (intstatus & bus->hostintmask) @@ -2417,7 +2386,7 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) bus->ipend = false; err = r_sdreg32(bus, &newstatus, offsetof(struct sdpcmd_regs, intstatus)); - bus->f1regdata++; + bus->sdcnt.f1regdata++; if (err != 0) newstatus = 0; newstatus &= bus->hostintmask; @@ -2426,7 +2395,7 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) err = w_sdreg32(bus, newstatus, offsetof(struct sdpcmd_regs, intstatus)); - bus->f1regdata++; + bus->sdcnt.f1regdata++; } } @@ -2445,7 +2414,7 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) err = r_sdreg32(bus, &newstatus, offsetof(struct sdpcmd_regs, intstatus)); - bus->f1regdata += 2; + bus->sdcnt.f1regdata += 2; bus->fcstate = !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)); intstatus |= (newstatus & bus->hostintmask); @@ -2510,13 +2479,13 @@ clkwait: terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", ret); - bus->tx_sderrs++; + bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, &err); - bus->f1regdata++; + bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; @@ -2526,7 +2495,7 @@ clkwait: lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, &err); - bus->f1regdata += 2; + bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; } @@ -2657,7 +2626,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) /* Check for existing queue, current flow-control, pending event, or pending clock */ brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); - bus->fcqueued++; + bus->sdcnt.fcqueued++; /* Priority based enq */ spin_lock_bh(&bus->txqlock); @@ -2845,13 +2814,13 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) /* On failure, abort the command and terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", ret); - bus->tx_sderrs++; + bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); - bus->f1regdata++; + bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; @@ -2859,7 +2828,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) SBSDIO_FUNC1_WFRAMEBCHI, NULL); lo = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->f1regdata += 2; + bus->sdcnt.f1regdata += 2; if (hi == 0 && lo == 0) break; } @@ -2976,13 +2945,26 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) up(&bus->sdsem); if (ret) - bus->tx_ctlerrs++; + bus->sdcnt.tx_ctlerrs++; else - bus->tx_ctlpkts++; + bus->sdcnt.tx_ctlpkts++; return ret ? -EIO : 0; } +#ifdef DEBUG +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +{ + struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; + + brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); +} +#else +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + static int brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) { @@ -3017,9 +2999,9 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) } if (rxlen) - bus->rx_ctlpkts++; + bus->sdcnt.rx_ctlpkts++; else - bus->rx_ctlerrs++; + bus->sdcnt.rx_ctlerrs++; return rxlen ? (int)rxlen : -ETIMEDOUT; } @@ -3419,7 +3401,7 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) return 0; /* Start the watchdog timer */ - bus->tickcnt = 0; + bus->sdcnt.tickcnt = 0; brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); down(&bus->sdsem); @@ -3512,7 +3494,7 @@ void brcmf_sdbrcm_isr(void *arg) return; } /* Count the interrupt call */ - bus->intrcount++; + bus->sdcnt.intrcount++; bus->ipend = true; /* Shouldn't get this interrupt if we're sleeping? */ @@ -3554,7 +3536,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->polltick = 0; /* Check device if no interrupts */ - if (!bus->intr || (bus->intrcount == bus->lastintrs)) { + if (!bus->intr || + (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { if (!bus->dpc_sched) { u8 devpend; @@ -3569,7 +3552,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) /* If there is something, make like the ISR and schedule the DPC */ if (intstatus) { - bus->pollcnt++; + bus->sdcnt.pollcnt++; bus->ipend = true; bus->dpc_sched = true; @@ -3581,7 +3564,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) } /* Update interrupt tracking */ - bus->lastintrs = bus->intrcount; + bus->sdcnt.lastintrs = bus->sdcnt.intrcount; } #ifdef DEBUG /* Poll for console output periodically */ @@ -3793,7 +3776,7 @@ brcmf_sdbrcm_watchdog_thread(void *data) if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { brcmf_sdbrcm_bus_watchdog(bus); /* Count the tick for reference */ - bus->tickcnt++; + bus->sdcnt.tickcnt++; } else break; } @@ -3834,7 +3817,6 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); - if (bus) { /* De-register interrupt handler */ brcmf_sdio_intr_unregister(bus->sdiodev); @@ -3938,6 +3920,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) goto fail; } + brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); /* if firmware path present try to download and bring up bus */ -- cgit v1.2.3 From 6304c20167337e21699bb3f7d25fcf099263416e Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 11 Jun 2012 10:18:36 +0530 Subject: ath9k_hw: Update initvals for AR9462 Adjust quick_drop value in the baseband AGC register to improve RX in HT20 mode. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 4a93e1534c1d..8f406ff2c95e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -52,7 +52,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000d8}, {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, - {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3376605e, 0x33795d5e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3376605e, 0x32395d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, -- cgit v1.2.3 From cec429dd65ef1ccc693e5fa1468b0a12982b4283 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:29 +0530 Subject: ath9k: choose legacy rate as last rate of MRR series Choose legacy rate as the last rate of Multi Rate Retry series if and only if the last selected rate is MCS and having higher PER rate. The current code fills a legacy rate as last one even though the previous rates in the series are having good PER value. This could limit the aggregation that affects the uplink performance. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/rc.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 92a6c0a87f89..e034add9cd5a 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -770,7 +770,7 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate *rates = tx_info->control.rates; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; - u8 try_per_rate, i = 0, rix, high_rix; + u8 try_per_rate, i = 0, rix; int is_probe = 0; if (rate_control_send_low(sta, priv_sta, txrc)) @@ -791,7 +791,6 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, rate_table = ath_rc_priv->rate_table; rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe, false); - high_rix = rix; /* * If we're in HT mode and both us and our peer supports LDPC. @@ -839,16 +838,16 @@ static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, try_per_rate = 8; /* - * Use a legacy rate as last retry to ensure that the frame - * is tried in both MCS and legacy rates. + * If the last rate in the rate series is MCS and has + * more than 80% of per thresh, then use a legacy rate + * as last retry to ensure that the frame is tried in both + * MCS and legacy rate. */ - if ((rates[2].flags & IEEE80211_TX_RC_MCS) && - (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) || - (ath_rc_priv->per[high_rix] > 45))) + ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &rix); + if (WLAN_RC_PHY_HT(rate_table->info[rix].phy) && + (ath_rc_priv->per[rix] > 45)) rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe, true); - else - ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &rix); /* All other rates in the series have RTS enabled */ ath_rc_rate_set_series(rate_table, &rates[i], txrc, -- cgit v1.2.3 From 8389fb3fd7c0bbe3289578f698917f594e0b6fc9 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:30 +0530 Subject: ath9k: restore power state on set channel failure Not doing so, could cause imbalance in powersave count. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c0f478b0a9a2..e655f2a907b5 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1258,6 +1258,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); mutex_unlock(&sc->mutex); + ath9k_ps_restore(sc); return -EINVAL; } -- cgit v1.2.3 From 9dd9b0dc1de8031a31b3eaebc6a9c0ab60612026 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:31 +0530 Subject: ath9k_hw: Fix AR9462 power consumption on idle associated The HW statemachine is sometimes found stuck in the state WL_LNA_CTRL_DISABLE when BT is in sleep, which will cause TX_HOLD always asserted and resmgr stuck in PENDING_TX state Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 34 +++++++++++++++++++++++++++++ drivers/net/wireless/ath/ath9k/ar9003_mci.h | 10 +++++++-- drivers/net/wireless/ath/ath9k/hw.c | 3 +++ 3 files changed, 45 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index b1ced2a76da3..13907f63bdc0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1404,3 +1404,37 @@ void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) /* Force another 2g5g update at next scanning */ mci->update_2g5g = true; } + +void ar9003_mci_set_power_awake(struct ath_hw *ah) +{ + u32 btcoex_ctrl2, diag_sw; + int i; + u8 lna_ctrl, bt_sleep; + + for (i = 0; i < AH_WAIT_TIMEOUT; i++) { + btcoex_ctrl2 = REG_READ(ah, AR_BTCOEX_CTRL2); + if (btcoex_ctrl2 != 0xdeadbeef) + break; + udelay(AH_TIME_QUANTUM); + } + REG_WRITE(ah, AR_BTCOEX_CTRL2, (btcoex_ctrl2 | BIT(23))); + + for (i = 0; i < AH_WAIT_TIMEOUT; i++) { + diag_sw = REG_READ(ah, AR_DIAG_SW); + if (diag_sw != 0xdeadbeef) + break; + udelay(AH_TIME_QUANTUM); + } + REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18))); + lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3; + bt_sleep = REG_READ(ah, AR_MCI_RX_STATUS) & AR_MCI_RX_REMOTE_SLEEP; + + REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2); + REG_WRITE(ah, AR_DIAG_SW, diag_sw); + + if (bt_sleep && (lna_ctrl == 2)) { + REG_SET_BIT(ah, AR_BTCOEX_RC, 0x1); + REG_CLR_BIT(ah, AR_BTCOEX_RC, 0x1); + udelay(50); + } +} diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 10282e2bcdc9..2a8c764281ba 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -265,8 +265,6 @@ void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, void ar9003_mci_cleanup(struct ath_hw *ah); void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr); -void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah); - /* * These functions are used by ath9k_hw. */ @@ -285,6 +283,8 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, bool is_full_sleep); void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked); +void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah); +void ar9003_mci_set_power_awake(struct ath_hw *ah); #else @@ -322,6 +322,12 @@ static inline void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, static inline void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked) { } +static inline void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) +{ +} +static inline void ar9003_mci_set_power_awake(struct ath_hw *ah) +{ +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 45e670087e1c..6d893335f42b 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2111,6 +2111,9 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah) AR_RTC_FORCE_WAKE_EN); udelay(50); + if (ath9k_hw_mci_is_enabled(ah)) + ar9003_mci_set_power_awake(ah); + for (i = POWER_UP_TIME / 50; i > 0; i--) { val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; if (val == AR_RTC_STATUS_ON) -- cgit v1.2.3 From 3863495b86d8ee8e7e70a328de5b88d555d7305a Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:32 +0530 Subject: ath9k_hw: check GPM HW write pointer before chip reset Both "MAC Warm Reset" and "MCI Reset Rx" will reset GPM HW write_ptr. We should check software cached write_ptr against HW write_ptr before reset. Otherwise the pending DMA data will be lost. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 18 ++++++++++++++++++ drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 + drivers/net/wireless/ath/ath9k/hw.c | 3 +++ 3 files changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 13907f63bdc0..cbeff9c4b5d8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -893,6 +893,9 @@ void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, udelay(100); } + /* Check pending GPM msg before MCI Reset Rx */ + ar9003_mci_state(ah, MCI_STATE_CHECK_GPM_OFFSET, NULL); + regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); udelay(1); @@ -1190,6 +1193,21 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); mci->gpm_idx = value; break; + case MCI_STATE_CHECK_GPM_OFFSET: + /* + * This should only be called before "MAC Warm Reset" or + * "MCI Reset Rx". + */ + value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); + if (mci->gpm_idx == value) + break; + ath_dbg(common, MCI, + "GPM cached write pointer mismatch %d %d\n", + mci->gpm_idx, value); + mci->query_bt = true; + mci->need_flush_btinfo = true; + mci->gpm_idx = 0; + break; case MCI_STATE_NEXT_GPM_OFFSET: case MCI_STATE_LAST_GPM_OFFSET: /* diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 2a8c764281ba..45624e1d3960 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -190,6 +190,7 @@ enum mci_bt_state { enum mci_state_type { MCI_STATE_ENABLE, MCI_STATE_INIT_GPM_OFFSET, + MCI_STATE_CHECK_GPM_OFFSET, MCI_STATE_NEXT_GPM_OFFSET, MCI_STATE_LAST_GPM_OFFSET, MCI_STATE_BT, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 6d893335f42b..8412128b842a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1348,6 +1348,9 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) } } + if (ath9k_hw_mci_is_enabled(ah)) + ar9003_mci_state(ah, MCI_STATE_CHECK_GPM_OFFSET, NULL); + REG_WRITE(ah, AR_RTC_RC, rst_flags); REGWRITE_BUFFER_FLUSH(ah); -- cgit v1.2.3 From 1bde95fa7ed84094d0606d9e79356a9dd8d0e01b Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:33 +0530 Subject: ath9k_hw: fix incorrect LNA register settings After a full reset, mci_reset will put LNA update to the setting for 2G mode. Those registers need to be forced to update when the channel is in 5G. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 24 +++++++++--------------- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 2 +- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 3 files changed, 11 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index cbeff9c4b5d8..c11dbaa2f5b1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1013,38 +1013,32 @@ static void ar9003_mci_queue_unsent_gpm(struct ath_hw *ah, u8 header, } } -void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool wait_done) +void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - if (!mci->update_2g5g) + if (!mci->update_2g5g && !force) return; if (mci->is_2g) { ar9003_mci_send_2g5g_status(ah, true); - ar9003_mci_send_lna_transfer(ah, true); - udelay(5); - REG_CLR_BIT(ah, AR_MCI_TX_CTRL, + REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) - REG_SET_BIT(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); + ar9003_mci_osla_setup(ah, true); } else { - ar9003_mci_send_lna_take(ah, true); - udelay(5); - REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); - REG_CLR_BIT(ah, AR_BTCOEX_CTRL, - AR_BTCOEX_CTRL_ONE_STEP_LOOK_AHEAD_EN); - ar9003_mci_send_2g5g_status(ah, true); + ar9003_mci_osla_setup(ah, false); + if (!force) + ar9003_mci_send_2g5g_status(ah, true); } } @@ -1313,7 +1307,7 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) if (mci->unhalt_bt_gpm) ar9003_mci_send_coex_halt_bt_gpm(ah, false, true); - ar9003_mci_2g5g_switch(ah, true); + ar9003_mci_2g5g_switch(ah, false); break; case MCI_STATE_SET_BT_CAL_START: mci->bt_state = MCI_BT_CAL_START; @@ -1394,7 +1388,7 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) mci->query_bt = true; mci->need_flush_btinfo = true; ar9003_mci_send_coex_wlan_channels(ah, true); - ar9003_mci_2g5g_switch(ah, true); + ar9003_mci_2g5g_switch(ah, false); break; case MCI_STATE_NEED_FTP_STOMP: value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 45624e1d3960..8cb33078f40a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -276,7 +276,7 @@ void ar9003_mci_stop_bt(struct ath_hw *ah, bool save_fullsleep); void ar9003_mci_init_cal_req(struct ath_hw *ah, bool *is_reusable); void ar9003_mci_init_cal_done(struct ath_hw *ah); void ar9003_mci_set_full_sleep(struct ath_hw *ah); -void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool wait_done); +void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force); void ar9003_mci_check_bt(struct ath_hw *ah); bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan); int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 8412128b842a..a9d328c955e9 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1711,7 +1711,7 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) ath9k_hw_start_nfcal(ah, true); if (ath9k_hw_mci_is_enabled(ah)) - ar9003_mci_2g5g_switch(ah, true); + ar9003_mci_2g5g_switch(ah, false); if (AR_SREV_9271(ah)) ar9002_hw_load_ani_reg(ah, chan); -- cgit v1.2.3 From ed6ebd8bc852845e34e8160eed43b29236b1892f Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:34 +0530 Subject: ath9k_hw: program OBS register only when MCI is disabled Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index a9d328c955e9..4ec496dae0eb 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1915,7 +1915,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_set_dma(ah); - REG_WRITE(ah, AR_OBS, 8); + if (!ath9k_hw_mci_is_enabled(ah)) + REG_WRITE(ah, AR_OBS, 8); if (ah->config.rx_intr_mitigation) { REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); -- cgit v1.2.3 From 92a33298e9a329d4bed9f723811c5bedc7d92b9c Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:35 +0530 Subject: ath9k_hw: process MCI interrupts only when btcoex is enabled let us process MCI interrupts only when BTCOEX is enabled to avoid processing bogus interrupts. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index d9e0824af093..78816b8b2173 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -181,11 +181,14 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) u32 mask2 = 0; struct ath9k_hw_capabilities *pCap = &ah->caps; struct ath_common *common = ath9k_hw_common(ah); - u32 sync_cause = 0, async_cause; + u32 sync_cause = 0, async_cause, async_mask = AR_INTR_MAC_IRQ; + + if (ath9k_hw_mci_is_enabled(ah)) + async_mask |= AR_INTR_ASYNC_MASK_MCI; async_cause = REG_READ(ah, AR_INTR_ASYNC_CAUSE); - if (async_cause & (AR_INTR_MAC_IRQ | AR_INTR_ASYNC_MASK_MCI)) { + if (async_cause & async_mask) { if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON) isr = REG_READ(ah, AR_ISR); -- cgit v1.2.3 From 9e2e0c84699ce7adeddb5cd5763956493ca7d331 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:36 +0530 Subject: ath9k: simplify btcoex profile management This patch simplifies profile management utility functions. * Separate find_profile from add/del functions * Return correct values when the profile list is empty or profile is ot found * flush the profiles when there are entries in the list Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mci.c | 48 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 49137f477b05..2ea5e933b3ba 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -28,11 +28,14 @@ ath_mci_find_profile(struct ath_mci_profile *mci, { struct ath_mci_profile_info *entry; + if (list_empty(&mci->info)) + return NULL; + list_for_each_entry(entry, &mci->info, list) { if (entry->conn_handle == info->conn_handle) - break; + return entry; } - return entry; + return NULL; } static bool ath_mci_add_profile(struct ath_common *common, @@ -49,31 +52,21 @@ static bool ath_mci_add_profile(struct ath_common *common, (info->type != MCI_GPM_COEX_PROFILE_VOICE)) return false; - entry = ath_mci_find_profile(mci, info); - - if (entry) { - memcpy(entry, info, 10); - } else { - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return false; + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return false; - memcpy(entry, info, 10); - INC_PROF(mci, info); - list_add_tail(&info->list, &mci->info); - } + memcpy(entry, info, 10); + INC_PROF(mci, info); + list_add_tail(&entry->list, &mci->info); return true; } static void ath_mci_del_profile(struct ath_common *common, struct ath_mci_profile *mci, - struct ath_mci_profile_info *info) + struct ath_mci_profile_info *entry) { - struct ath_mci_profile_info *entry; - - entry = ath_mci_find_profile(mci, info); - if (!entry) return; @@ -86,12 +79,16 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci) { struct ath_mci_profile_info *info, *tinfo; + mci->aggr_limit = 0; + + if (list_empty(&mci->info)) + return; + list_for_each_entry_safe(info, tinfo, &mci->info, list) { list_del(&info->list); DEC_PROF(mci, info); kfree(info); } - mci->aggr_limit = 0; } static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex) @@ -229,12 +226,17 @@ static void ath_mci_process_profile(struct ath_softc *sc, struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; + struct ath_mci_profile_info *entry = NULL; + + entry = ath_mci_find_profile(mci, info); + if (entry) + memcpy(entry, info, 10); if (info->start) { - if (!ath_mci_add_profile(common, mci, info)) + if (!entry && !ath_mci_add_profile(common, mci, info)) return; } else - ath_mci_del_profile(common, mci, info); + ath_mci_del_profile(common, mci, entry); btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; mci->aggr_limit = mci->num_sco ? 6 : 0; @@ -263,8 +265,6 @@ static void ath_mci_process_status(struct ath_softc *sc, if (status->is_link) return; - memset(&info, 0, sizeof(struct ath_mci_profile_info)); - info.conn_handle = status->conn_handle; if (ath_mci_find_profile(mci, &info)) return; -- cgit v1.2.3 From dfd0587a7d99d42cc2d608324991169494d2dbba Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 11 Jun 2012 12:19:37 +0530 Subject: ath9k: keep btcoex period in milliseconds btcoex periord is converted into micro seconds during initialization and converted back to milli seconds while starting timer. As MCI code handles btcoex period in msec, lets keep the btcoex timer in msec and convert them into other form whenever needed. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/gpio.c | 8 ++++---- drivers/net/wireless/ath/ath9k/mci.c | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index af6d27350291..00ff5ab5a54f 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -232,7 +232,7 @@ static void ath_btcoex_period_timer(unsigned long data) } ath9k_ps_restore(sc); - timer_period = btcoex->btcoex_period / 1000; + timer_period = btcoex->btcoex_period; mod_timer(&btcoex->period_timer, jiffies + msecs_to_jiffies(timer_period)); } @@ -267,10 +267,10 @@ static int ath_init_btcoex_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; - btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * + btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 1000 * btcoex->btcoex_period / 100; - btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * + btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * 1000 * btcoex->btcoex_period / 100; setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 2ea5e933b3ba..92d61cc0c8e5 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -182,8 +182,7 @@ skip_tuning: if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; - btcoex->btcoex_period *= 1000; - btcoex->btcoex_no_stomp = btcoex->btcoex_period * + btcoex->btcoex_no_stomp = btcoex->btcoex_period * 1000 * (100 - btcoex->duty_cycle) / 100; ath9k_hw_btcoex_enable(sc->sc_ah); -- cgit v1.2.3 From 3c7992e33ab8d0c625a45ded11cc10d5b622bcd4 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 10:13:53 +0530 Subject: ath9k: defer btcoex scheme update As btcoex scheme updation might sleep, remove the function call from tasklet context and queue it up as a separate work. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 3 +++ drivers/net/wireless/ath/ath9k/mci.c | 14 +++++++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 02fc1c1e5eeb..a8c050085648 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -698,6 +698,7 @@ struct ath_softc { #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; struct ath_mci_coex mci_coex; + struct work_struct mci_work; #endif struct ath_descdma txsdma; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e655f2a907b5..a6f83b683db1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -150,6 +150,9 @@ static void __ath_cancel_work(struct ath_softc *sc) cancel_work_sync(&sc->hw_check_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT + cancel_work_sync(&sc->mci_work); +#endif } static void ath_cancel_work(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 92d61cc0c8e5..e53df86dfce5 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -52,7 +52,7 @@ static bool ath_mci_add_profile(struct ath_common *common, (info->type != MCI_GPM_COEX_PROFILE_VOICE)) return false; - entry = kzalloc(sizeof(*entry), GFP_KERNEL); + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return false; @@ -219,6 +219,13 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) } } +static void ath9k_mci_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, mci_work); + + ath_mci_update_scheme(sc); +} + static void ath_mci_process_profile(struct ath_softc *sc, struct ath_mci_profile_info *info) { @@ -249,7 +256,7 @@ static void ath_mci_process_profile(struct ath_softc *sc, btcoex->duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; } - ath_mci_update_scheme(sc); + ieee80211_queue_work(sc->hw, &sc->mci_work); } static void ath_mci_process_status(struct ath_softc *sc, @@ -283,7 +290,7 @@ static void ath_mci_process_status(struct ath_softc *sc, } while (++i < ATH_MCI_MAX_PROFILE); if (old_num_mgmt != mci->num_mgmt) - ath_mci_update_scheme(sc); + ieee80211_queue_work(sc->hw, &sc->mci_work); } static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) @@ -377,6 +384,7 @@ int ath_mci_setup(struct ath_softc *sc) mci->gpm_buf.bf_addr, (mci->gpm_buf.bf_len >> 4), mci->sched_buf.bf_paddr); + INIT_WORK(&sc->mci_work, ath9k_mci_work); ath_dbg(common, MCI, "MCI Initialized\n"); return 0; -- cgit v1.2.3 From a197b76c230725962dc8e13102b24468e8621bfb Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 10:13:54 +0530 Subject: ath9k: fix btcoex duty cycle * Reset duty cycle before updating btcoex scheme. Otherwise duty cycle reaches max limit and never be reduced again * Adjust duty cycle with proper BDR profile value Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mci.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index e53df86dfce5..f0c8fa48d79e 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -20,7 +20,7 @@ #include "ath9k.h" #include "mci.h" -static const u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 }; +static const u8 ath_mci_duty_cycle[] = { 55, 50, 60, 70, 80, 85, 90, 95, 98 }; static struct ath_mci_profile_info* ath_mci_find_profile(struct ath_mci_profile *mci, @@ -120,6 +120,8 @@ static void ath_mci_update_scheme(struct ath_softc *sc) if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) goto skip_tuning; + btcoex->duty_cycle = ath_mci_duty_cycle[num_profile]; + if (num_profile == 1) { info = list_first_entry(&mci->info, struct ath_mci_profile_info, @@ -178,7 +180,7 @@ skip_tuning: if (IS_CHAN_5GHZ(sc->sc_ah->curchan)) return; - btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_MAX_DUTY_CYCLE : 0); + btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_BDR_DUTY_CYCLE : 0); if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; @@ -247,14 +249,12 @@ static void ath_mci_process_profile(struct ath_softc *sc, btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; mci->aggr_limit = mci->num_sco ? 6 : 0; - if (NUM_PROF(mci)) { + btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; + if (NUM_PROF(mci)) btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; - } else { + else btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : ATH_BTCOEX_STOMP_LOW; - btcoex->duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; - } ieee80211_queue_work(sc->hw, &sc->mci_work); } -- cgit v1.2.3 From 64bc1239c790e051ff677e023435d770d2ffa174 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Tue, 12 Jun 2012 20:13:43 +0530 Subject: ath9k: Fix softlockup in AR9485 steps to recreate: load latest ath9k driver with AR9485 stop the network-manager and wpa_supplicant bring the interface up Call Trace: [] ? ath_hw_check+0xe0/0xe0 [ath9k] [] __const_udelay+0x28/0x30 [] ar9003_get_pll_sqsum_dvc+0x4a/0x80 [ath9k_hw] [] ath_hw_pll_work+0x5b/0xe0 [ath9k] [] process_one_work+0x11e/0x470 [] worker_thread+0x15f/0x360 [] ? manage_workers+0x230/0x230 [] kthread+0x93/0xa0 [] kernel_thread_helper+0x4/0x10 [] ? kthread_freezable_should_stop+0x70/0x70 [] ? gs_change+0x13/0x13 ensure that the PLL-WAR for AR9485/AR9340 is executed only if the STA is associated (or) IBSS/AP mode had started beaconing. Ideally this WAR is needed to recover from some rare beacon stuck during stress testing. Before the STA is associated/IBSS had started beaconing, PLL4(0x1618c) always seem to have zero even though we had configured PLL3(0x16188) to query about PLL's locking status. When we keep on polling infinitely PLL4's 8th bit(ie check for PLL locking measurements is done), machine hangs due to softlockup. fixes https://bugzilla.redhat.com/show_bug.cgi?id=811142 Reported-by: Rolf Offermanns Cc: stable@vger.kernel.org Tested-by: Mohammed Shafi Shajakhan Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/link.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 0cc4c70f7f0c..a105c9426251 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -136,6 +136,14 @@ void ath_hw_pll_work(struct work_struct *work) u32 pll_sqsum; struct ath_softc *sc = container_of(work, struct ath_softc, hw_pll_work.work); + /* + * ensure that the PLL WAR is executed only + * after the STA is associated (or) if the + * beaconing had started in interfaces that + * uses beacons. + */ + if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) + return; ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); -- cgit v1.2.3 From 506847ad34c08fe4f766ffe1b955713628acf6bd Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:16 +0530 Subject: ath9k_hw: cleanup MCI gpm offset state Add utility functions to get and test GPM offset and remove MCI_STATE*_GPM_OFFSET states. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 198 ++++++++++++++-------------- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 9 +- drivers/net/wireless/ath/ath9k/hw.c | 2 +- drivers/net/wireless/ath/ath9k/mci.c | 6 +- 4 files changed, 110 insertions(+), 105 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index c11dbaa2f5b1..0433f2b12c53 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -593,8 +593,7 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, if (!time_out) break; - offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, - &more_data); + offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) continue; @@ -658,8 +657,7 @@ static u32 ar9003_mci_wait_for_gpm(struct ath_hw *ah, u8 gpm_type, time_out = 0; while (more_data == MCI_GPM_MORE) { - offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, - &more_data); + offset = ar9003_mci_get_next_gpm_offset(ah, false, &more_data); if (offset == MCI_GPM_INVALID) break; @@ -894,7 +892,7 @@ void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, } /* Check pending GPM msg before MCI Reset Rx */ - ar9003_mci_state(ah, MCI_STATE_CHECK_GPM_OFFSET, NULL); + ar9003_mci_check_gpm_offset(ah); regval |= SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); @@ -902,7 +900,7 @@ void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, regval &= ~SM(1, AR_MCI_COMMAND2_RESET_RX); REG_WRITE(ah, AR_MCI_COMMAND2, regval); - ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET, NULL); + ar9003_mci_get_next_gpm_offset(ah, true, NULL); REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, (SM(0xe801, AR_MCI_MSG_ATTRIBUTES_TABLE_INVALID_HDR) | @@ -1170,7 +1168,7 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - u32 value = 0, more_gpm = 0, gpm_ptr; + u32 value = 0; u8 query_type; switch (state_type) { @@ -1182,96 +1180,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) value = 0; } value &= AR_BTCOEX_CTRL_MCI_MODE_EN; - break; - case MCI_STATE_INIT_GPM_OFFSET: - value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); - mci->gpm_idx = value; - break; - case MCI_STATE_CHECK_GPM_OFFSET: - /* - * This should only be called before "MAC Warm Reset" or - * "MCI Reset Rx". - */ - value = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); - if (mci->gpm_idx == value) - break; - ath_dbg(common, MCI, - "GPM cached write pointer mismatch %d %d\n", - mci->gpm_idx, value); - mci->query_bt = true; - mci->need_flush_btinfo = true; - mci->gpm_idx = 0; - break; - case MCI_STATE_NEXT_GPM_OFFSET: - case MCI_STATE_LAST_GPM_OFFSET: - /* - * This could be useful to avoid new GPM message interrupt which - * may lead to spurious interrupt after power sleep, or multiple - * entry of ath_mci_intr(). - * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can - * alleviate this effect, but clearing GPM RX interrupt bit is - * safe, because whether this is called from hw or driver code - * there must be an interrupt bit set/triggered initially - */ - REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, - AR_MCI_INTERRUPT_RX_MSG_GPM); - - gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); - value = gpm_ptr; - - if (value == 0) - value = mci->gpm_len - 1; - else if (value >= mci->gpm_len) { - if (value != 0xFFFF) - value = 0; - } else { - value--; - } - - if (value == 0xFFFF) { - value = MCI_GPM_INVALID; - more_gpm = MCI_GPM_NOMORE; - } else if (state_type == MCI_STATE_NEXT_GPM_OFFSET) { - if (gpm_ptr == mci->gpm_idx) { - value = MCI_GPM_INVALID; - more_gpm = MCI_GPM_NOMORE; - } else { - for (;;) { - u32 temp_index; - - /* skip reserved GPM if any */ - - if (value != mci->gpm_idx) - more_gpm = MCI_GPM_MORE; - else - more_gpm = MCI_GPM_NOMORE; - - temp_index = mci->gpm_idx; - mci->gpm_idx++; - - if (mci->gpm_idx >= - mci->gpm_len) - mci->gpm_idx = 0; - - if (ar9003_mci_is_gpm_valid(ah, - temp_index)) { - value = temp_index; - break; - } - - if (more_gpm == MCI_GPM_NOMORE) { - value = MCI_GPM_INVALID; - break; - } - } - } - if (p_data) - *p_data = more_gpm; - } - - if (value != MCI_GPM_INVALID) - value <<= 4; - break; case MCI_STATE_LAST_SCHD_MSG_OFFSET: value = MS(REG_READ(ah, AR_MCI_RX_STATUS), @@ -1450,3 +1358,99 @@ void ar9003_mci_set_power_awake(struct ath_hw *ah) udelay(50); } } + +void ar9003_mci_check_gpm_offset(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + u32 offset; + + /* + * This should only be called before "MAC Warm Reset" or "MCI Reset Rx". + */ + offset = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); + if (mci->gpm_idx == offset) + return; + ath_dbg(common, MCI, "GPM cached write pointer mismatch %d %d\n", + mci->gpm_idx, offset); + mci->query_bt = true; + mci->need_flush_btinfo = true; + mci->gpm_idx = 0; +} + +u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more) +{ + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + u32 offset, more_gpm = 0, gpm_ptr; + + if (first) { + gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); + mci->gpm_idx = gpm_ptr; + return gpm_ptr; + } + + /* + * This could be useful to avoid new GPM message interrupt which + * may lead to spurious interrupt after power sleep, or multiple + * entry of ath_mci_intr(). + * Adding empty GPM check by returning HAL_MCI_GPM_INVALID can + * alleviate this effect, but clearing GPM RX interrupt bit is + * safe, because whether this is called from hw or driver code + * there must be an interrupt bit set/triggered initially + */ + REG_WRITE(ah, AR_MCI_INTERRUPT_RX_MSG_RAW, + AR_MCI_INTERRUPT_RX_MSG_GPM); + + gpm_ptr = MS(REG_READ(ah, AR_MCI_GPM_1), AR_MCI_GPM_WRITE_PTR); + offset = gpm_ptr; + + if (!offset) + offset = mci->gpm_len - 1; + else if (offset >= mci->gpm_len) { + if (offset != 0xFFFF) + offset = 0; + } else { + offset--; + } + + if ((offset == 0xFFFF) || (gpm_ptr == mci->gpm_idx)) { + offset = MCI_GPM_INVALID; + more_gpm = MCI_GPM_NOMORE; + goto out; + } + for (;;) { + u32 temp_index; + + /* skip reserved GPM if any */ + + if (offset != mci->gpm_idx) + more_gpm = MCI_GPM_MORE; + else + more_gpm = MCI_GPM_NOMORE; + + temp_index = mci->gpm_idx; + mci->gpm_idx++; + + if (mci->gpm_idx >= mci->gpm_len) + mci->gpm_idx = 0; + + if (ar9003_mci_is_gpm_valid(ah, temp_index)) { + offset = temp_index; + break; + } + + if (more_gpm == MCI_GPM_NOMORE) { + offset = MCI_GPM_INVALID; + break; + } + } + + if (offset != MCI_GPM_INVALID) + offset <<= 4; +out: + if (more) + *more = more_gpm; + + return offset; +} +EXPORT_SYMBOL(ar9003_mci_get_next_gpm_offset); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 8cb33078f40a..163d1562163d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -189,10 +189,6 @@ enum mci_bt_state { /* Type of state query */ enum mci_state_type { MCI_STATE_ENABLE, - MCI_STATE_INIT_GPM_OFFSET, - MCI_STATE_CHECK_GPM_OFFSET, - MCI_STATE_NEXT_GPM_OFFSET, - MCI_STATE_LAST_GPM_OFFSET, MCI_STATE_BT, MCI_STATE_SET_BT_SLEEP, MCI_STATE_SET_BT_AWAKE, @@ -266,6 +262,7 @@ void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, void ar9003_mci_cleanup(struct ath_hw *ah); void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr); +u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more); /* * These functions are used by ath9k_hw. */ @@ -286,6 +283,7 @@ void ar9003_mci_reset(struct ath_hw *ah, bool en_int, bool is_2g, void ar9003_mci_get_isr(struct ath_hw *ah, enum ath9k_int *masked); void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah); void ar9003_mci_set_power_awake(struct ath_hw *ah); +void ar9003_mci_check_gpm_offset(struct ath_hw *ah); #else @@ -329,6 +327,9 @@ static inline void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) static inline void ar9003_mci_set_power_awake(struct ath_hw *ah) { } +static inline void ar9003_mci_check_gpm_offset(struct ath_hw *ah) +{ +} #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ #endif diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 4ec496dae0eb..784baee5db84 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1349,7 +1349,7 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) } if (ath9k_hw_mci_is_enabled(ah)) - ar9003_mci_state(ah, MCI_STATE_CHECK_GPM_OFFSET, NULL); + ar9003_mci_check_gpm_offset(ah); REG_WRITE(ah, AR_RTC_RC, rst_flags); diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index f0c8fa48d79e..9e152316fd5c 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -421,7 +421,7 @@ void ath_mci_intr(struct ath_softc *sc) ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg); if (ar9003_mci_state(ah, MCI_STATE_ENABLE, NULL) == 0) { - ar9003_mci_state(ah, MCI_STATE_INIT_GPM_OFFSET, NULL); + ar9003_mci_get_next_gpm_offset(ah, true, NULL); return; } @@ -488,8 +488,8 @@ void ath_mci_intr(struct ath_softc *sc) while (more_data == MCI_GPM_MORE) { pgpm = mci->gpm_buf.bf_addr; - offset = ar9003_mci_state(ah, MCI_STATE_NEXT_GPM_OFFSET, - &more_data); + offset = ar9003_mci_get_next_gpm_offset(ah, false, + &more_data); if (offset == MCI_GPM_INVALID) break; -- cgit v1.2.3 From e1763d3f3388968d622a340fed711ae16fc1bc11 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:17 +0530 Subject: ath9k_hw: add utility function to set BT version Add a utility function to set bluetooth version and remove MCI_STATE_SET_BT_COEX_VERSION. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 25 ++++++++++++------------- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 2 +- drivers/net/wireless/ath/ath9k/mci.c | 11 +++-------- 3 files changed, 16 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 0433f2b12c53..a021e7474757 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1166,7 +1166,6 @@ EXPORT_SYMBOL(ar9003_mci_cleanup); u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) { - struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 value = 0; u8 query_type; @@ -1239,18 +1238,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) case MCI_STATE_SEND_WLAN_COEX_VERSION: ar9003_mci_send_coex_version_response(ah, true); break; - case MCI_STATE_SET_BT_COEX_VERSION: - if (!p_data) - ath_dbg(common, MCI, - "MCI Set BT Coex version with NULL data!!\n"); - else { - mci->bt_ver_major = (*p_data >> 8) & 0xff; - mci->bt_ver_minor = (*p_data) & 0xff; - mci->bt_version_known = true; - ath_dbg(common, MCI, "MCI BT version set: %d.%d\n", - mci->bt_ver_major, mci->bt_ver_minor); - } - break; case MCI_STATE_SEND_WLAN_CHANNELS: if (p_data) { if (((mci->wlan_channels[1] & 0xffff0000) == @@ -1454,3 +1441,15 @@ out: return offset; } EXPORT_SYMBOL(ar9003_mci_get_next_gpm_offset); + +void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor) +{ + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + + mci->bt_ver_major = major; + mci->bt_ver_minor = minor; + mci->bt_version_known = true; + ath_dbg(ath9k_hw_common(ah), MCI, "MCI BT version set: %d.%d\n", + mci->bt_ver_major, mci->bt_ver_minor); +} +EXPORT_SYMBOL(ar9003_mci_set_bt_version); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 163d1562163d..d4e7739b89ff 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -201,7 +201,6 @@ enum mci_state_type { MCI_STATE_CONT_TXRX, MCI_STATE_RESET_REQ_WAKE, MCI_STATE_SEND_WLAN_COEX_VERSION, - MCI_STATE_SET_BT_COEX_VERSION, MCI_STATE_SEND_WLAN_CHANNELS, MCI_STATE_SEND_VERSION_QUERY, MCI_STATE_SEND_STATUS_QUERY, @@ -263,6 +262,7 @@ void ar9003_mci_cleanup(struct ath_hw *ah); void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr); u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more); +void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor); /* * These functions are used by ath9k_hw. */ diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 9e152316fd5c..6f4188a32ff0 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -299,22 +299,17 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) struct ath_mci_profile_info profile_info; struct ath_mci_profile_status profile_status; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u32 version; - u8 major; - u8 minor; + u8 major, minor; u32 seq_num; switch (opcode) { case MCI_GPM_COEX_VERSION_QUERY: - version = ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION, - NULL); + ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION, NULL); break; case MCI_GPM_COEX_VERSION_RESPONSE: major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); minor = *(rx_payload + MCI_GPM_COEX_B_MINOR_VERSION); - version = (major << 8) + minor; - version = ar9003_mci_state(ah, MCI_STATE_SET_BT_COEX_VERSION, - &version); + ar9003_mci_set_bt_version(ah, major, minor); break; case MCI_GPM_COEX_STATUS_QUERY: ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_CHANNELS, NULL); -- cgit v1.2.3 From 2d340ac81e7474535db7a14f35c80b45b02b63a1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:18 +0530 Subject: ath9k_hw: remove MCI_STATE_SEND_WLAN_CHANNELS Add a MCI util function to send wlan channel info to BT. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 25 +++++++++---------------- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 2 +- drivers/net/wireless/ath/ath9k/mci.c | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index a021e7474757..3b44ebe3f750 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1238,22 +1238,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) case MCI_STATE_SEND_WLAN_COEX_VERSION: ar9003_mci_send_coex_version_response(ah, true); break; - case MCI_STATE_SEND_WLAN_CHANNELS: - if (p_data) { - if (((mci->wlan_channels[1] & 0xffff0000) == - (*(p_data + 1) & 0xffff0000)) && - (mci->wlan_channels[2] == *(p_data + 2)) && - (mci->wlan_channels[3] == *(p_data + 3))) - break; - - mci->wlan_channels[0] = *p_data++; - mci->wlan_channels[1] = *p_data++; - mci->wlan_channels[2] = *p_data++; - mci->wlan_channels[3] = *p_data++; - } - mci->wlan_channels_update = true; - ar9003_mci_send_coex_wlan_channels(ah, true); - break; case MCI_STATE_SEND_VERSION_QUERY: ar9003_mci_send_coex_version_query(ah, true); break; @@ -1453,3 +1437,12 @@ void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor) mci->bt_ver_major, mci->bt_ver_minor); } EXPORT_SYMBOL(ar9003_mci_set_bt_version); + +void ar9003_mci_send_wlan_channels(struct ath_hw *ah) +{ + struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; + + mci->wlan_channels_update = true; + ar9003_mci_send_coex_wlan_channels(ah, true); +} +EXPORT_SYMBOL(ar9003_mci_send_wlan_channels); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index d4e7739b89ff..05f42a98aeac 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -201,7 +201,6 @@ enum mci_state_type { MCI_STATE_CONT_TXRX, MCI_STATE_RESET_REQ_WAKE, MCI_STATE_SEND_WLAN_COEX_VERSION, - MCI_STATE_SEND_WLAN_CHANNELS, MCI_STATE_SEND_VERSION_QUERY, MCI_STATE_SEND_STATUS_QUERY, MCI_STATE_NEED_FLUSH_BT_INFO, @@ -263,6 +262,7 @@ void ar9003_mci_get_interrupt(struct ath_hw *ah, u32 *raw_intr, u32 *rx_msg_intr); u32 ar9003_mci_get_next_gpm_offset(struct ath_hw *ah, bool first, u32 *more); void ar9003_mci_set_bt_version(struct ath_hw *ah, u8 major, u8 minor); +void ar9003_mci_send_wlan_channels(struct ath_hw *ah); /* * These functions are used by ath9k_hw. */ diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 6f4188a32ff0..dbc8e1dabad1 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -312,7 +312,7 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) ar9003_mci_set_bt_version(ah, major, minor); break; case MCI_GPM_COEX_STATUS_QUERY: - ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_CHANNELS, NULL); + ar9003_mci_send_wlan_channels(ah); break; case MCI_GPM_COEX_BT_PROFILE_INFO: memcpy(&profile_info, -- cgit v1.2.3 From d02ca07e6fdf89ed6f453fe1c9229c71a0358886 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:19 +0530 Subject: ath9k_hw: remove MCI_STATE_NEED_FLUSH_BT_INFO Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 17 ----------------- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 - 2 files changed, 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 3b44ebe3f750..9a761a1fe1bf 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1245,23 +1245,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) query_type = MCI_GPM_COEX_QUERY_BT_TOPOLOGY; ar9003_mci_send_coex_bt_status_query(ah, true, query_type); break; - case MCI_STATE_NEED_FLUSH_BT_INFO: - /* - * btcoex_hw.mci.unhalt_bt_gpm means whether it's - * needed to send UNHALT message. It's set whenever - * there's a request to send HALT message. - * mci_halted_bt_gpm means whether HALT message is sent - * out successfully. - * - * Checking (mci_unhalt_bt_gpm == false) instead of - * checking (ah->mci_halted_bt_gpm == false) will make - * sure currently is in UNHALT-ed mode and BT can - * respond to status query. - */ - value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; - if (p_data) - mci->need_flush_btinfo = (*p_data != 0) ? true : false; - break; case MCI_STATE_RECOVER_RX: ar9003_mci_prep_interface(ah); mci->query_bt = true; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 05f42a98aeac..a7c17e5b90d4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -203,7 +203,6 @@ enum mci_state_type { MCI_STATE_SEND_WLAN_COEX_VERSION, MCI_STATE_SEND_VERSION_QUERY, MCI_STATE_SEND_STATUS_QUERY, - MCI_STATE_NEED_FLUSH_BT_INFO, MCI_STATE_SET_CONCUR_TX_PRI, MCI_STATE_RECOVER_RX, MCI_STATE_NEED_FTP_STOMP, -- cgit v1.2.3 From b98ccec0a497a9f08d705f1a7d63d5690bb48e70 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:20 +0530 Subject: ath9k_hw: remove p_data argument from ar9003_mci_state As p_data is unuse, lets remove it from ar9003_mci_state. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 6 ++-- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 2 +- drivers/net/wireless/ath/ath9k/gpio.c | 2 +- drivers/net/wireless/ath/ath9k/mci.c | 49 +++++++++++++---------------- 4 files changed, 27 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 9a761a1fe1bf..d6e90f5effd0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -321,7 +321,7 @@ void ar9003_mci_set_full_sleep(struct ath_hw *ah) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; - if (ar9003_mci_state(ah, MCI_STATE_ENABLE, NULL) && + if (ar9003_mci_state(ah, MCI_STATE_ENABLE) && (mci->bt_state != MCI_BT_SLEEP) && !mci->halted_bt_gpm) { ar9003_mci_send_coex_halt_bt_gpm(ah, true, true); @@ -484,7 +484,7 @@ static void ar9003_mci_sync_bt_state(struct ath_hw *ah) struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 cur_bt_state; - cur_bt_state = ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL); + cur_bt_state = ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP); if (mci->bt_state != cur_bt_state) mci->bt_state = cur_bt_state; @@ -1164,7 +1164,7 @@ void ar9003_mci_cleanup(struct ath_hw *ah) } EXPORT_SYMBOL(ar9003_mci_cleanup); -u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data) +u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) { struct ath9k_hw_mci *mci = &ah->btcoex_hw.mci; u32 value = 0; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index a7c17e5b90d4..697a56c3599e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -253,7 +253,7 @@ enum mci_gpm_coex_opcode { bool ar9003_mci_send_message(struct ath_hw *ah, u8 header, u32 flag, u32 *payload, u8 len, bool wait_done, bool check_bt); -u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type, u32 *p_data); +u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type); void ar9003_mci_setup(struct ath_hw *ah, u32 gpm_addr, void *gpm_buf, u16 len, u32 sched_addr); void ar9003_mci_cleanup(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 00ff5ab5a54f..26032cb59b8a 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -202,7 +202,7 @@ static void ath_btcoex_period_timer(unsigned long data) btcoex->bt_wait_time += btcoex->btcoex_period; if (btcoex->bt_wait_time > ATH_BTCOEX_RX_WAIT_TIME) { - if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP, NULL) && + if (ar9003_mci_state(ah, MCI_STATE_NEED_FTP_STOMP) && (mci->num_pan || mci->num_other_acl)) ah->btcoex_hw.mci.stomp_ftp = (sc->rx.num_pkts < ATH_BTCOEX_STOMP_FTP_THRESH); diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index dbc8e1dabad1..61a68e856049 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -199,16 +199,16 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) switch (opcode) { case MCI_GPM_BT_CAL_REQ: - if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_AWAKE) { - ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START, NULL); + if (ar9003_mci_state(ah, MCI_STATE_BT) == MCI_BT_AWAKE) { + ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); } else { ath_dbg(common, MCI, "MCI State mismatch: %d\n", - ar9003_mci_state(ah, MCI_STATE_BT, NULL)); + ar9003_mci_state(ah, MCI_STATE_BT)); } break; case MCI_GPM_BT_CAL_DONE: - ar9003_mci_state(ah, MCI_STATE_BT, NULL); + ar9003_mci_state(ah, MCI_STATE_BT); break; case MCI_GPM_BT_CAL_GRANT: MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); @@ -304,7 +304,7 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) switch (opcode) { case MCI_GPM_COEX_VERSION_QUERY: - ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION, NULL); + ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION); break; case MCI_GPM_COEX_VERSION_RESPONSE: major = *(rx_payload + MCI_GPM_COEX_B_MAJOR_VERSION); @@ -415,7 +415,7 @@ void ath_mci_intr(struct ath_softc *sc) ar9003_mci_get_interrupt(sc->sc_ah, &mci_int, &mci_int_rxmsg); - if (ar9003_mci_state(ah, MCI_STATE_ENABLE, NULL) == 0) { + if (ar9003_mci_state(ah, MCI_STATE_ENABLE) == 0) { ar9003_mci_get_next_gpm_offset(ah, true, NULL); return; } @@ -435,46 +435,41 @@ void ath_mci_intr(struct ath_softc *sc) NULL, 0, true, false); mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_REQ_WAKE; - ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE, NULL); + ar9003_mci_state(ah, MCI_STATE_RESET_REQ_WAKE); /* * always do this for recovery and 2G/5G toggling and LNA_TRANS */ - ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE, NULL); + ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING; - if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_SLEEP) { - if (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL) != - MCI_BT_SLEEP) - ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE, - NULL); - } + if ((ar9003_mci_state(ah, MCI_STATE_BT) == MCI_BT_SLEEP) && + (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != + MCI_BT_SLEEP)) + ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; - if (ar9003_mci_state(ah, MCI_STATE_BT, NULL) == MCI_BT_AWAKE) { - if (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP, NULL) != - MCI_BT_AWAKE) - ar9003_mci_state(ah, MCI_STATE_SET_BT_SLEEP, - NULL); - } + if ((ar9003_mci_state(ah, MCI_STATE_BT) == MCI_BT_AWAKE) && + (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != + MCI_BT_AWAKE)) + ar9003_mci_state(ah, MCI_STATE_SET_BT_SLEEP); } if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { - ar9003_mci_state(ah, MCI_STATE_RECOVER_RX, NULL); + ar9003_mci_state(ah, MCI_STATE_RECOVER_RX); skip_gpm = true; } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SCHD_INFO; - offset = ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET, - NULL); + offset = ar9003_mci_state(ah, MCI_STATE_LAST_SCHD_MSG_OFFSET); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_GPM) { @@ -526,21 +521,21 @@ void ath_mci_intr(struct ath_softc *sc) if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { int value_dbm = ar9003_mci_state(ah, - MCI_STATE_CONT_RSSI_POWER, NULL); + MCI_STATE_CONT_RSSI_POWER); mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO; - if (ar9003_mci_state(ah, MCI_STATE_CONT_TXRX, NULL)) + if (ar9003_mci_state(ah, MCI_STATE_CONT_TXRX)) ath_dbg(common, MCI, "MCI CONT_INFO: (tx) pri = %d, pwr = %d dBm\n", ar9003_mci_state(ah, - MCI_STATE_CONT_PRIORITY, NULL), + MCI_STATE_CONT_PRIORITY), value_dbm); else ath_dbg(common, MCI, "MCI CONT_INFO: (rx) pri = %d,pwr = %d dBm\n", ar9003_mci_state(ah, - MCI_STATE_CONT_PRIORITY, NULL), + MCI_STATE_CONT_PRIORITY), value_dbm); } -- cgit v1.2.3 From 6d97be48e27603f99743fc0e94e57dce2edb9cbf Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:21 +0530 Subject: ath9k_hw: remove MCI_STATE_BT remove MCI_STATE_BT and use bt_state instead. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 --- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 - drivers/net/wireless/ath/ath9k/mci.c | 15 ++++++--------- 3 files changed, 6 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index d6e90f5effd0..1508500e73be 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1200,9 +1200,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) case MCI_STATE_CONT_TXRX: value = MS(mci->cont_status, AR_MCI_CONT_TXRX); break; - case MCI_STATE_BT: - value = mci->bt_state; - break; case MCI_STATE_SET_BT_SLEEP: mci->bt_state = MCI_BT_SLEEP; break; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 697a56c3599e..0c02bd86597b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -189,7 +189,6 @@ enum mci_bt_state { /* Type of state query */ enum mci_state_type { MCI_STATE_ENABLE, - MCI_STATE_BT, MCI_STATE_SET_BT_SLEEP, MCI_STATE_SET_BT_AWAKE, MCI_STATE_SET_BT_CAL_START, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 61a68e856049..156454892588 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -195,20 +195,16 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 payload[4] = {0, 0, 0, 0}; switch (opcode) { case MCI_GPM_BT_CAL_REQ: - if (ar9003_mci_state(ah, MCI_STATE_BT) == MCI_BT_AWAKE) { + if (mci_hw->bt_state == MCI_BT_AWAKE) { ar9003_mci_state(ah, MCI_STATE_SET_BT_CAL_START); ieee80211_queue_work(sc->hw, &sc->hw_reset_work); - } else { - ath_dbg(common, MCI, "MCI State mismatch: %d\n", - ar9003_mci_state(ah, MCI_STATE_BT)); } - break; - case MCI_GPM_BT_CAL_DONE: - ar9003_mci_state(ah, MCI_STATE_BT); + ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); break; case MCI_GPM_BT_CAL_GRANT: MCI_GPM_SET_CAL_TYPE(payload, MCI_GPM_WLAN_CAL_DONE); @@ -407,6 +403,7 @@ void ath_mci_intr(struct ath_softc *sc) struct ath_mci_coex *mci = &sc->mci_coex; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_mci *mci_hw = &ah->btcoex_hw.mci; u32 mci_int, mci_int_rxmsg; u32 offset, subtype, opcode; u32 *pgpm; @@ -446,7 +443,7 @@ void ath_mci_intr(struct ath_softc *sc) if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_WAKING; - if ((ar9003_mci_state(ah, MCI_STATE_BT) == MCI_BT_SLEEP) && + if ((mci_hw->bt_state == MCI_BT_SLEEP) && (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != MCI_BT_SLEEP)) ar9003_mci_state(ah, MCI_STATE_SET_BT_AWAKE); @@ -455,7 +452,7 @@ void ath_mci_intr(struct ath_softc *sc) if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING) { mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_SYS_SLEEPING; - if ((ar9003_mci_state(ah, MCI_STATE_BT) == MCI_BT_AWAKE) && + if ((mci_hw->bt_state == MCI_BT_AWAKE) && (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != MCI_BT_AWAKE)) ar9003_mci_state(ah, MCI_STATE_SET_BT_SLEEP); -- cgit v1.2.3 From 26e942b790eddc757b5be179ef67907e025ff87b Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:22 +0530 Subject: ath9k_hw: remove MCI_STATE_CONT_* state Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 9 --------- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 3 --- drivers/net/wireless/ath/ath9k/mci.c | 22 ++++++++-------------- drivers/net/wireless/ath/ath9k/reg.h | 4 ++-- 4 files changed, 10 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 1508500e73be..25f99ef48b20 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1191,15 +1191,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) AR_MCI_RX_REMOTE_SLEEP) ? MCI_BT_SLEEP : MCI_BT_AWAKE; break; - case MCI_STATE_CONT_RSSI_POWER: - value = MS(mci->cont_status, AR_MCI_CONT_RSSI_POWER); - break; - case MCI_STATE_CONT_PRIORITY: - value = MS(mci->cont_status, AR_MCI_CONT_RRIORITY); - break; - case MCI_STATE_CONT_TXRX: - value = MS(mci->cont_status, AR_MCI_CONT_TXRX); - break; case MCI_STATE_SET_BT_SLEEP: mci->bt_state = MCI_BT_SLEEP; break; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 0c02bd86597b..98bfc62d6b9b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -195,9 +195,6 @@ enum mci_state_type { MCI_STATE_SET_BT_CAL, MCI_STATE_LAST_SCHD_MSG_OFFSET, MCI_STATE_REMOTE_SLEEP, - MCI_STATE_CONT_RSSI_POWER, - MCI_STATE_CONT_PRIORITY, - MCI_STATE_CONT_TXRX, MCI_STATE_RESET_REQ_WAKE, MCI_STATE_SEND_WLAN_COEX_VERSION, MCI_STATE_SEND_VERSION_QUERY, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 156454892588..e83d7200f762 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -517,23 +517,17 @@ void ath_mci_intr(struct ath_softc *sc) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_LNA_INFO; if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_INFO) { - int value_dbm = ar9003_mci_state(ah, - MCI_STATE_CONT_RSSI_POWER); + int value_dbm = MS(mci_hw->cont_status, + AR_MCI_CONT_RSSI_POWER); mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_CONT_INFO; - if (ar9003_mci_state(ah, MCI_STATE_CONT_TXRX)) - ath_dbg(common, MCI, - "MCI CONT_INFO: (tx) pri = %d, pwr = %d dBm\n", - ar9003_mci_state(ah, - MCI_STATE_CONT_PRIORITY), - value_dbm); - else - ath_dbg(common, MCI, - "MCI CONT_INFO: (rx) pri = %d,pwr = %d dBm\n", - ar9003_mci_state(ah, - MCI_STATE_CONT_PRIORITY), - value_dbm); + ath_dbg(common, MCI, + "MCI CONT_INFO: (%s) pri = %d pwr = %d dBm\n", + MS(mci_hw->cont_status, AR_MCI_CONT_TXRX) ? + "tx" : "rx", + MS(mci_hw->cont_status, AR_MCI_CONT_PRIORITY), + value_dbm); } if (mci_int_rxmsg & AR_MCI_INTERRUPT_RX_MSG_CONT_NACK) diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 560d6effac7a..75acefbd4937 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -2098,8 +2098,8 @@ enum { #define AR_MCI_CONT_STATUS 0x1848 #define AR_MCI_CONT_RSSI_POWER 0x000000FF #define AR_MCI_CONT_RSSI_POWER_S 0 -#define AR_MCI_CONT_RRIORITY 0x0000FF00 -#define AR_MCI_CONT_RRIORITY_S 8 +#define AR_MCI_CONT_PRIORITY 0x0000FF00 +#define AR_MCI_CONT_PRIORITY_S 8 #define AR_MCI_CONT_TXRX 0x00010000 #define AR_MCI_CONT_TXRX_S 16 -- cgit v1.2.3 From 9330969b8fd1304fdcb7c1825f1528eea38d321c Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 12 Jun 2012 20:18:23 +0530 Subject: ath9k_hw: remove MCI_STATE_SET_BT_SLEEP Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 3 --- drivers/net/wireless/ath/ath9k/ar9003_mci.h | 1 - drivers/net/wireless/ath/ath9k/mci.c | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 25f99ef48b20..cc2853ade8f8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1191,9 +1191,6 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) AR_MCI_RX_REMOTE_SLEEP) ? MCI_BT_SLEEP : MCI_BT_AWAKE; break; - case MCI_STATE_SET_BT_SLEEP: - mci->bt_state = MCI_BT_SLEEP; - break; case MCI_STATE_SET_BT_AWAKE: mci->bt_state = MCI_BT_AWAKE; ar9003_mci_send_coex_version_query(ah, true); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index 98bfc62d6b9b..d33b8e128855 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -189,7 +189,6 @@ enum mci_bt_state { /* Type of state query */ enum mci_state_type { MCI_STATE_ENABLE, - MCI_STATE_SET_BT_SLEEP, MCI_STATE_SET_BT_AWAKE, MCI_STATE_SET_BT_CAL_START, MCI_STATE_SET_BT_CAL, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index e83d7200f762..7d34a504d617 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -455,7 +455,7 @@ void ath_mci_intr(struct ath_softc *sc) if ((mci_hw->bt_state == MCI_BT_AWAKE) && (ar9003_mci_state(ah, MCI_STATE_REMOTE_SLEEP) != MCI_BT_AWAKE)) - ar9003_mci_state(ah, MCI_STATE_SET_BT_SLEEP); + mci_hw->bt_state = MCI_BT_SLEEP; } if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || -- cgit v1.2.3 From 04414e2aa516c7af6aa316562e046c5aca025e33 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Mon, 11 Jun 2012 17:59:55 +0300 Subject: wlcore: avoid using NET_IP_ALIGN for RX alignment NET_IP_ALIGN can be overriden on different architectures and therefore cannot be used in the RX path to account for the 2 bytes added for alignment (either by the FW in the case of 18xx or by the host for 12xx). Instead use an internal define. Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/rx.c | 4 ++-- drivers/net/wireless/ti/wlcore/rx.h | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 78200dcacfca..a1db4e032409 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -127,7 +127,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, } if (rx_align == WLCORE_RX_BUF_UNALIGNED) - reserved = NET_IP_ALIGN; + reserved = RX_BUF_ALIGN; /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) data; @@ -175,7 +175,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, */ memcpy(buf, data + sizeof(*desc), pkt_data_len); if (rx_align == WLCORE_RX_BUF_PADDED) - skb_pull(skb, NET_IP_ALIGN); + skb_pull(skb, RX_BUF_ALIGN); *hlid = desc->hlid; diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 9be780179456..4324a427e835 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -103,6 +103,12 @@ /* If set, the buffer was padded by the FW to be 4 bytes aligned */ #define RX_BUF_PADDED_PAYLOAD BIT(30) +/* + * Account for the padding inserted by the FW in case of RX_ALIGNMENT + * or for fixing alignment in case the packet wasn't aligned. + */ +#define RX_BUF_ALIGN 2 + /* Describes the alignment state of a Rx buffer */ enum wl_rx_buf_align { WLCORE_RX_BUF_ALIGNED, -- cgit v1.2.3 From 8f1a8684a56b3640510c0610b5635f5a4fe366fd Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 12 Jun 2012 12:39:55 +0300 Subject: wlcore: send EAPOLs with basic rate policy EAPOLs are sent at high rates as they are considered data packets. Some APs like Motorola Symbol AP7131 and AP650 don't respond well to these rates and don't respond with EAPOL 3/4 consistently. When sending EAPOL 2/4 at 54Mbps we've seen approx 30% success rate in getting EAPOL 3/4 response while using 11Mbps we got 100% success. To increase the chances of successful 4-Way handshake with such APs, send EAPOLs with basic rate policy in order to avoid high rates. Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/tx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 6983e7a829d0..8ee82b9f93f4 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -305,11 +305,15 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (is_dummy || !wlvif) rate_idx = 0; else if (wlvif->bss_type != BSS_TYPE_AP_BSS) { - /* if the packets are destined for AP (have a STA entry) - send them with AP rate policies, otherwise use default - basic rates */ + /* + * if the packets are destined for AP (have a STA entry) + * send them with AP rate policies (EAPOLs are an exception), + * otherwise use default basic rates + */ if (control->flags & IEEE80211_TX_CTL_NO_CCK_RATE) rate_idx = wlvif->sta.p2p_rate_idx; + else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + rate_idx = wlvif->sta.basic_rate_idx; else if (control->control.sta) rate_idx = wlvif->sta.ap_rate_idx; else -- cgit v1.2.3 From 2812eef151de189567f421c2cb1397b58334d9bd Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 12 Jun 2012 12:45:27 +0300 Subject: wlcore: update basic rates on channel switch On channel switch we have to update the basic rates, in order to reflect possible band changes (otherwise, we might start beaconing on 11a with the default rates of 11g). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1156e3f578c1..21e05476cd35 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -2454,6 +2454,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, wlvif->channel_type = conf->channel_type; if (is_ap) { + wl1271_set_band_rate(wl, wlvif); ret = wl1271_init_ap_rates(wl, wlvif); if (ret < 0) wl1271_error("AP rate policy change failed %d", -- cgit v1.2.3 From 05f48d45747e422dba0baaaf96ae2ea103791bce Mon Sep 17 00:00:00 2001 From: Yair Shapira Date: Wed, 13 Jun 2012 17:14:21 +0300 Subject: wlcore/wl12xx: add support for HP and SKW FEM radio manufacturers Add support for HP (High Performance TQS fem type 3) and SKW (fem type 2). This is done by increasing the number of FEM manufacturers to 4. Usually FEM parameters from ini file are read from nvs file and passed to firmware using TEST_CMD_INI_FILE_RADIO_PARAM. Still, because the nvs file has only place for 2 FEMs, we need to pass the new FEM types information in one of the available entries. This is done by mapping new fem types 2,3 to entry 0. This solution works for manual FEM selection. AutoDetect-FEM still support only fem types 0 and 1. Signed-off-by: Yair Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/cmd.c | 16 ++++++++++------ drivers/net/wireless/ti/wlcore/ini.h | 22 +++++++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c index 50ba7480b790..30be784a40d8 100644 --- a/drivers/net/wireless/ti/wl12xx/cmd.c +++ b/drivers/net/wireless/ti/wl12xx/cmd.c @@ -174,7 +174,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs; struct wl1271_radio_parms_cmd *radio_parms; struct wl1271_ini_general_params *gp = &nvs->general_params; - int ret; + int ret, fem_idx; if (!wl->nvs) return -ENODEV; @@ -185,11 +185,13 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; + fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer); + /* 2.4GHz parameters */ memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2, sizeof(struct wl1271_ini_band_params_2)); memcpy(&radio_parms->dyn_params_2, - &nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params, + &nvs->dyn_radio_params_2[fem_idx].params, sizeof(struct wl1271_ini_fem_params_2)); /* 5GHz parameters */ @@ -197,7 +199,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) &nvs->stat_radio_params_5, sizeof(struct wl1271_ini_band_params_5)); memcpy(&radio_parms->dyn_params_5, - &nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params, + &nvs->dyn_radio_params_5[fem_idx].params, sizeof(struct wl1271_ini_fem_params_5)); wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", @@ -216,7 +218,7 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl) struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; struct wl128x_radio_parms_cmd *radio_parms; struct wl128x_ini_general_params *gp = &nvs->general_params; - int ret; + int ret, fem_idx; if (!wl->nvs) return -ENODEV; @@ -227,11 +229,13 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl) radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; + fem_idx = WL12XX_FEM_TO_NVS_ENTRY(gp->tx_bip_fem_manufacturer); + /* 2.4GHz parameters */ memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2, sizeof(struct wl128x_ini_band_params_2)); memcpy(&radio_parms->dyn_params_2, - &nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params, + &nvs->dyn_radio_params_2[fem_idx].params, sizeof(struct wl128x_ini_fem_params_2)); /* 5GHz parameters */ @@ -239,7 +243,7 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl) &nvs->stat_radio_params_5, sizeof(struct wl128x_ini_band_params_5)); memcpy(&radio_parms->dyn_params_5, - &nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params, + &nvs->dyn_radio_params_5[fem_idx].params, sizeof(struct wl128x_ini_fem_params_5)); radio_parms->fem_vendor_and_options = nvs->fem_vendor_and_options; diff --git a/drivers/net/wireless/ti/wlcore/ini.h b/drivers/net/wireless/ti/wlcore/ini.h index 4cf9ecc56212..d24fe3bbc672 100644 --- a/drivers/net/wireless/ti/wlcore/ini.h +++ b/drivers/net/wireless/ti/wlcore/ini.h @@ -172,7 +172,19 @@ struct wl128x_ini_fem_params_5 { /* NVS data structure */ #define WL1271_INI_NVS_SECTION_SIZE 468 -#define WL1271_INI_FEM_MODULE_COUNT 2 + +/* We have four FEM module types: 0-RFMD, 1-TQS, 2-SKW, 3-TQS_HP */ +#define WL1271_INI_FEM_MODULE_COUNT 4 + +/* + * In NVS we only store two FEM module entries - + * FEM modules 0,2,3 are stored in entry 0 + * FEM module 1 is stored in entry 1 + */ +#define WL12XX_NVS_FEM_MODULE_COUNT 2 + +#define WL12XX_FEM_TO_NVS_ENTRY(ini_fem_module) \ + ((ini_fem_module) == 1 ? 1 : 0) #define WL1271_INI_LEGACY_NVS_FILE_SIZE 800 @@ -188,13 +200,13 @@ struct wl1271_nvs_file { struct { struct wl1271_ini_fem_params_2 params; u8 padding; - } dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT]; + } dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT]; struct wl1271_ini_band_params_5 stat_radio_params_5; u8 padding3; struct { struct wl1271_ini_fem_params_5 params; u8 padding; - } dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT]; + } dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT]; } __packed; struct wl128x_nvs_file { @@ -209,12 +221,12 @@ struct wl128x_nvs_file { struct { struct wl128x_ini_fem_params_2 params; u8 padding; - } dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT]; + } dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT]; struct wl128x_ini_band_params_5 stat_radio_params_5; u8 padding3; struct { struct wl128x_ini_fem_params_5 params; u8 padding; - } dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT]; + } dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT]; } __packed; #endif -- cgit v1.2.3 From b0b09e312ad36993a9ae51993b73448c1e38fc14 Mon Sep 17 00:00:00 2001 From: Yair Shapira Date: Wed, 13 Jun 2012 17:14:22 +0300 Subject: wlcore: add print logs of radio_status in case of BIP calibration FEM BIP calibration may fail with fw/phy radio status. In order to recognize these failures a log is added to the calibration answer (TEST_CMD_P2G_CAL) Signed-off-by: Yair Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.h | 21 +++++++++++++++++++++ drivers/net/wireless/ti/wlcore/testmode.c | 14 ++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 85171f2bf68e..c8a6510c72cb 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -652,4 +652,25 @@ struct wl12xx_cmd_stop_channel_switch { struct wl1271_cmd_header header; } __packed; +/* Used to check radio status after calibration */ +#define MAX_TLV_LENGTH 500 +#define TEST_CMD_P2G_CAL 2 /* TX BiP */ + +struct wl1271_cmd_cal_p2g { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + __le32 ver; + __le16 len; + u8 buf[MAX_TLV_LENGTH]; + u8 type; + u8 padding; + + __le16 radio_status; + + u8 sub_band_mask; + u8 padding2; +} __packed; + #endif /* __WL1271_CMD_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c index 0e59ea2cdd39..eeb339d61d1e 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.c +++ b/drivers/net/wireless/ti/wlcore/testmode.c @@ -108,6 +108,20 @@ static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[]) } if (answer) { + /* If we got bip calibration answer print radio status */ + struct wl1271_cmd_cal_p2g *params = + (struct wl1271_cmd_cal_p2g *) buf; + + s16 radio_status = (s16) le16_to_cpu(params->radio_status); + + if (params->test.id == TEST_CMD_P2G_CAL && + radio_status < 0) + wl1271_warning("testmode cmd: radio status=%d", + radio_status); + else + wl1271_info("testmode cmd: radio status=%d", + radio_status); + len = nla_total_size(buf_len); skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); if (!skb) { -- cgit v1.2.3 From bcab320ba20edf166d82d42928401a0afe61e0c5 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 13 Jun 2012 20:29:16 +0300 Subject: wlcore: declare interface combinations Advertise to the stack that the wlcore driver supports multiple interfaces for a single device. This is required in order to be able to run multirole with mac80211. Signed-off-by: Eliad Peller Signed-off-by: Yoni Divinsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 21e05476cd35..8eefcd7505e2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4977,6 +4977,29 @@ static void wl1271_unregister_hw(struct wl1271 *wl) } +static const struct ieee80211_iface_limit wlcore_iface_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +}; + +static const struct ieee80211_iface_combination +wlcore_iface_combinations[] = { + { + .num_different_channels = 1, + .max_interfaces = 2, + .limits = wlcore_iface_limits, + .n_limits = ARRAY_SIZE(wlcore_iface_limits), + }, +}; + static int wl1271_init_ieee80211(struct wl1271 *wl) { static const u32 cipher_suites[] = { @@ -5070,6 +5093,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + /* allowed interface combinations */ + wl->hw->wiphy->iface_combinations = wlcore_iface_combinations; + wl->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(wlcore_iface_combinations); + SET_IEEE80211_DEV(wl->hw, wl->dev); wl->hw->sta_data_size = sizeof(struct wl1271_station); -- cgit v1.2.3 From 32587371ad3db2f9d335de10dbd8cffd4fff5669 Mon Sep 17 00:00:00 2001 From: Tao Guo Date: Wed, 13 Jun 2012 21:17:21 +0200 Subject: umem: fix up unplugging Fix a regression introduced by 7eaceaccab5f40 ("block: remove per-queue plugging"). In that patch, Jens removed the whole mm_unplug_device() function, which used to be the trigger to make umem start to work. We need to implement unplugging to make umem start to work, or I/O will never be triggered. Signed-off-by: Tao Guo Cc: Neil Brown Cc: Jens Axboe Cc: Shaohua Li Cc: Acked-by: NeilBrown Signed-off-by: Jens Axboe --- drivers/block/umem.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers') diff --git a/drivers/block/umem.c b/drivers/block/umem.c index aa2712060bfb..9a72277a31df 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -513,6 +513,44 @@ static void process_page(unsigned long data) } } +struct mm_plug_cb { + struct blk_plug_cb cb; + struct cardinfo *card; +}; + +static void mm_unplug(struct blk_plug_cb *cb) +{ + struct mm_plug_cb *mmcb = container_of(cb, struct mm_plug_cb, cb); + + spin_lock_irq(&mmcb->card->lock); + activate(mmcb->card); + spin_unlock_irq(&mmcb->card->lock); + kfree(mmcb); +} + +static int mm_check_plugged(struct cardinfo *card) +{ + struct blk_plug *plug = current->plug; + struct mm_plug_cb *mmcb; + + if (!plug) + return 0; + + list_for_each_entry(mmcb, &plug->cb_list, cb.list) { + if (mmcb->cb.callback == mm_unplug && mmcb->card == card) + return 1; + } + /* Not currently on the callback list */ + mmcb = kmalloc(sizeof(*mmcb), GFP_ATOMIC); + if (!mmcb) + return 0; + + mmcb->card = card; + mmcb->cb.callback = mm_unplug; + list_add(&mmcb->cb.list, &plug->cb_list); + return 1; +} + static void mm_make_request(struct request_queue *q, struct bio *bio) { struct cardinfo *card = q->queuedata; @@ -523,6 +561,8 @@ static void mm_make_request(struct request_queue *q, struct bio *bio) *card->biotail = bio; bio->bi_next = NULL; card->biotail = &bio->bi_next; + if (bio->bi_rw & REQ_SYNC || !mm_check_plugged(card)) + activate(card); spin_unlock_irq(&card->lock); return; -- cgit v1.2.3 From b918c62e086b2130a7bae44110ca516ef10bfe5a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: replace struct pci_bus secondary/subordinate with busn_res Replace the struct pci_bus secondary/subordinate members with the struct resource busn_res. Later we'll build a resource tree of these bus numbers. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/iommu/intel-iommu.c | 2 +- drivers/net/ethernet/broadcom/tg3.c | 4 +-- drivers/parisc/dino.c | 10 +++--- drivers/parisc/iosapic.c | 2 +- drivers/parisc/lba_pci.c | 22 ++++++------- drivers/pci/hotplug/acpiphp_glue.c | 8 ++--- drivers/pci/hotplug/cpci_hotplug_pci.c | 6 ++-- drivers/pci/hotplug/pciehp_pci.c | 4 +-- drivers/pci/hotplug/shpchp_pci.c | 6 ++-- drivers/pci/hotplug/shpchp_sysfs.c | 6 ++-- drivers/pci/iov.c | 4 +-- drivers/pci/pci.c | 2 +- drivers/pci/probe.c | 58 +++++++++++++++++----------------- drivers/pci/setup-bus.c | 24 +++++++------- drivers/pcmcia/cardbus.c | 2 +- drivers/pcmcia/yenta_socket.c | 26 +++++++-------- 16 files changed, 93 insertions(+), 93 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b12af2ff8c54..2fb7d1598a68 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -661,7 +661,7 @@ static struct intel_iommu *device_to_iommu(int segment, u8 bus, u8 devfn) if (drhd->devices[i] && drhd->devices[i]->subordinate && drhd->devices[i]->subordinate->number <= bus && - drhd->devices[i]->subordinate->subordinate >= bus) + drhd->devices[i]->subordinate->busn_res.end >= bus) return drhd->iommu; } diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index edeeb516807a..09fa3c687a1f 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -14168,7 +14168,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (bridge->subordinate && (bridge->subordinate->number <= tp->pdev->bus->number) && - (bridge->subordinate->subordinate >= + (bridge->subordinate->busn_res.end >= tp->pdev->bus->number)) { tg3_flag_set(tp, 5701_DMA_BUG); pci_dev_put(bridge); @@ -14196,7 +14196,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (bridge && bridge->subordinate && (bridge->subordinate->number <= tp->pdev->bus->number) && - (bridge->subordinate->subordinate >= + (bridge->subordinate->busn_res.end >= tp->pdev->bus->number)) { tg3_flag_set(tp, 40BIT_DMA_BUG); pci_dev_put(bridge); diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 432d4bbcc62a..70517b0f94e6 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -174,7 +174,7 @@ static int dino_cfg_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val) { struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); void __iomem *base_addr = d->hba.base_addr; unsigned long flags; @@ -209,7 +209,7 @@ static int dino_cfg_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { struct dino_device *d = DINO_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 v = DINO_CFG_TOK(local_bus, devfn, where & ~3); void __iomem *base_addr = d->hba.base_addr; unsigned long flags; @@ -554,7 +554,7 @@ dino_fixup_bus(struct pci_bus *bus) struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge)); DBG(KERN_WARNING "%s(0x%p) bus %d platform_data 0x%p\n", - __func__, bus, bus->secondary, + __func__, bus, bus->busn_res.start, bus->bridge->platform_data); /* Firmware doesn't set up card-mode dino, so we have to */ @@ -998,12 +998,12 @@ static int __init dino_probe(struct parisc_device *dev) return 0; } - bus->subordinate = pci_scan_child_bus(bus); + bus->busn_res.end = pci_scan_child_bus(bus); /* This code *depends* on scanning being single threaded * if it isn't, this global bus number count will fail */ - dino_current_bus = bus->subordinate + 1; + dino_current_bus = bus->busn_res.end + 1; pci_bus_assign_resources(bus); pci_bus_add_devices(bus); return 0; diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 1f9e9fefb8e7..83380c8fcb6b 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -532,7 +532,7 @@ iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) intr_slot = PCI_SLOT(pcidev->devfn); } DBG_IRT("iosapic_xlate_pin: bus %d slot %d pin %d\n", - pcidev->bus->secondary, intr_slot, intr_pin); + pcidev->bus->busn_res.start, intr_slot, intr_pin); return irt_find_irqline(isi, intr_slot, intr_pin); } diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 052fa230bc77..cd8f9ce8720f 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -189,8 +189,8 @@ lba_dump_res(struct resource *r, int d) static int lba_device_present(u8 bus, u8 dfn, struct lba_device *d) { - u8 first_bus = d->hba.hba_bus->secondary; - u8 last_sub_bus = d->hba.hba_bus->subordinate; + u8 first_bus = d->hba.hba_bus->busn_res.start; + u8 last_sub_bus = d->hba.hba_bus->busn_res.end; if ((bus < first_bus) || (bus > last_sub_bus) || @@ -364,7 +364,7 @@ lba_rd_cfg(struct lba_device *d, u32 tok, u8 reg, u32 size) static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data) { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus, devfn); void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; @@ -380,7 +380,7 @@ static int elroy_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int return 0; } - if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->secondary, devfn, d)) { + if (LBA_SKIP_PROBE(d) && !lba_device_present(bus->busn_res.start, devfn, d)) { DBG_CFG("%s(%x+%2x) -> -1 (b)\n", __func__, tok, pos); /* either don't want to look or know device isn't present. */ *data = ~0U; @@ -431,7 +431,7 @@ lba_wr_cfg(struct lba_device *d, u32 tok, u8 reg, u32 data, u32 size) static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 data) { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus,devfn); if ((pos > 255) || (devfn > 255)) @@ -444,7 +444,7 @@ static int elroy_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, int return 0; } - if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->secondary, devfn, d))) { + if (LBA_SKIP_PROBE(d) && (!lba_device_present(bus->busn_res.start, devfn, d))) { DBG_CFG("%s(%x+%2x) = 0x%x (b)\n", __func__, tok, pos,data); return 1; /* New Workaround */ } @@ -481,7 +481,7 @@ static struct pci_ops elroy_cfg_ops = { static int mercury_cfg_read(struct pci_bus *bus, unsigned int devfn, int pos, int size, u32 *data) { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus, devfn); void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; @@ -514,7 +514,7 @@ static int mercury_cfg_write(struct pci_bus *bus, unsigned int devfn, int pos, i { struct lba_device *d = LBA_DEV(parisc_walk_tree(bus->bridge)); void __iomem *data_reg = d->hba.base_addr + LBA_PCI_CFG_DATA; - u32 local_bus = (bus->parent == NULL) ? 0 : bus->secondary; + u32 local_bus = (bus->parent == NULL) ? 0 : bus->busn_res.start; u32 tok = LBA_CFG_TOK(local_bus,devfn); if ((pos > 255) || (devfn > 255)) @@ -636,7 +636,7 @@ lba_fixup_bus(struct pci_bus *bus) struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge)); DBG("lba_fixup_bus(0x%p) bus %d platform_data 0x%p\n", - bus, bus->secondary, bus->bridge->platform_data); + bus, (int)bus->busn_res.start, bus->bridge->platform_data); /* ** Properly Setup MMIO resources for this bus. @@ -1511,7 +1511,7 @@ lba_driver_probe(struct parisc_device *dev) return 0; } - lba_bus->subordinate = pci_scan_child_bus(lba_bus); + lba_bus->busn_res.end = pci_scan_child_bus(lba_bus); /* This is in lieu of calling pci_assign_unassigned_resources() */ if (is_pdc_pat()) { @@ -1541,7 +1541,7 @@ lba_driver_probe(struct parisc_device *dev) lba_dev->flags |= LBA_FLAG_SKIP_PROBE; } - lba_next_bus = lba_bus->subordinate + 1; + lba_next_bus = lba_res->busn_res.end + 1; pci_bus_add_devices(lba_bus); /* Whew! Finally done! Tell services we got this one covered. */ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44fa645a..62d0ae4dfcad 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -100,11 +100,11 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val, PCI_PRIMARY_BUS, &buses); - if (((buses >> 8) & 0xff) != bus->secondary) { + if (((buses >> 8) & 0xff) != bus->busn_res.start) { buses = (buses & 0xff000000) | ((unsigned int)(bus->primary) << 0) - | ((unsigned int)(bus->secondary) << 8) - | ((unsigned int)(bus->subordinate) << 16); + | ((unsigned int)(bus->busn_res.start) << 8) + | ((unsigned int)(bus->busn_res.end) << 16); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); } return NOTIFY_OK; @@ -692,7 +692,7 @@ static unsigned char acpiphp_max_busnr(struct pci_bus *bus) * bus->subordinate value because it could have * padding in it. */ - max = bus->secondary; + max = bus->busn_res.start; list_for_each(tmp, &bus->children) { n = pci_bus_max_busnr(pci_bus_b(tmp)); diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index ae853ccd0cd5..42f3a61db87c 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -292,8 +292,8 @@ int __ref cpci_configure_slot(struct slot *slot) (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { /* Find an unused bus number for the new bridge */ struct pci_bus *child; - unsigned char busnr, start = parent->secondary; - unsigned char end = parent->subordinate; + unsigned char busnr, start = parent->busn_res.start; + unsigned char end = parent->busn_res.end; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), @@ -312,7 +312,7 @@ int __ref cpci_configure_slot(struct slot *slot) pci_dev_put(dev); continue; } - child->subordinate = pci_do_scan_bus(child); + child->busn_res.end = pci_do_scan_bus(child); pci_bus_size_bridges(child); } pci_dev_put(dev); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 47d9dc06b109..b898f06b588d 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -37,8 +37,8 @@ static int __ref pciehp_add_bridge(struct pci_dev *dev) { struct pci_bus *parent = dev->bus; - int pass, busnr, start = parent->secondary; - int end = parent->subordinate; + int pass, busnr, start = parent->busn_res.start; + int end = parent->busn_res.end; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), busnr)) diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index df7e4bfadae3..d021eb031b3c 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -64,8 +64,8 @@ int __ref shpchp_configure_device(struct slot *p_slot) (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { /* Find an unused bus number for the new bridge */ struct pci_bus *child; - unsigned char busnr, start = parent->secondary; - unsigned char end = parent->subordinate; + unsigned char busnr, start = parent->busn_res.start; + unsigned char end = parent->busn_res.end; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), busnr)) @@ -84,7 +84,7 @@ int __ref shpchp_configure_device(struct slot *p_slot) pci_dev_put(dev); continue; } - child->subordinate = pci_do_scan_bus(child); + child->busn_res.end = pci_do_scan_bus(child); pci_bus_size_bridges(child); } pci_configure_slot(dev); diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c index efa30da1ae8f..eeb23ceae4a8 100644 --- a/drivers/pci/hotplug/shpchp_sysfs.c +++ b/drivers/pci/hotplug/shpchp_sysfs.c @@ -73,13 +73,13 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha } } out += sprintf(out, "Free resources: bus numbers\n"); - for (busnr = bus->secondary; busnr <= bus->subordinate; busnr++) { + for (busnr = bus->busn_res.start; busnr <= bus->busn_res.end; busnr++) { if (!pci_find_bus(pci_domain_nr(bus), busnr)) break; } - if (busnr < bus->subordinate) + if (busnr < bus->busn_res.end) out += sprintf(out, "start = %8.8x, length = %8.8x\n", - busnr, (bus->subordinate - busnr)); + busnr, (int)(bus->busn_res.end - busnr)); return out - buf; } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 6554e1a0f634..e873060fb35b 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -47,7 +47,7 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) if (!child) return NULL; - child->subordinate = busnr; + child->busn_res.end = busnr; child->dev.parent = bus->bridge; rc = pci_bus_add_child(child); if (rc) { @@ -327,7 +327,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) iov->offset = offset; iov->stride = stride; - if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->subordinate) { + if (virtfn_bus(dev, nr_virtfn - 1) > dev->bus->busn_res.end) { dev_err(&dev->dev, "SR-IOV: bus number out of range\n"); return -ENOMEM; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..aeda6e9c245c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -110,7 +110,7 @@ unsigned char pci_bus_max_busnr(struct pci_bus* bus) struct list_head *tmp; unsigned char max, n; - max = bus->subordinate; + max = bus->busn_res.end; list_for_each(tmp, &bus->children) { n = pci_bus_max_busnr(pci_bus_b(tmp)); if(n > max) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 658ac977cb56..651b096134dc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -381,8 +381,8 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ return; - dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n", - child->secondary, child->subordinate, + dev_info(&dev->dev, "PCI bridge to %pR%s\n", + &child->busn_res, dev->transparent ? " (subtractive decode)" : ""); pci_bus_remove_resources(child); @@ -599,9 +599,9 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, * Set up the primary, secondary and subordinate * bus numbers. */ - child->number = child->secondary = busnr; - child->primary = parent->secondary; - child->subordinate = 0xff; + child->number = child->busn_res.start = busnr; + child->primary = parent->busn_res.start; + child->busn_res.end = 0xff; if (!bridge) return child; @@ -643,8 +643,8 @@ static void pci_fixup_parent_subordinate_busnr(struct pci_bus *child, int max) if (!pcibios_assign_all_busses()) return; - while (parent->parent && parent->subordinate < max) { - parent->subordinate = max; + while (parent->parent && parent->busn_res.end < max) { + parent->busn_res.end = max; pci_write_config_byte(parent->self, PCI_SUBORDINATE_BUS, max); parent = parent->parent; } @@ -718,15 +718,15 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, if (!child) goto out; child->primary = primary; - child->subordinate = subordinate; + child->busn_res.end = subordinate; child->bridge_ctl = bctl; } cmax = pci_scan_child_bus(child); if (cmax > max) max = cmax; - if (child->subordinate > max) - max = child->subordinate; + if (child->busn_res.end > max) + max = child->busn_res.end; } else { /* * We need to assign a number to this bus which we always @@ -759,8 +759,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, } buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) - | ((unsigned int)(child->secondary) << 8) - | ((unsigned int)(child->subordinate) << 16); + | ((unsigned int)(child->busn_res.start) << 8) + | ((unsigned int)(child->busn_res.end) << 16); /* * yenta.c forces a secondary latency timer of 176. @@ -805,8 +805,8 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, break; while (parent->parent) { if ((!pcibios_assign_all_busses()) && - (parent->subordinate > max) && - (parent->subordinate <= max+i)) { + (parent->busn_res.end > max) && + (parent->busn_res.end <= max+i)) { j = 1; } parent = parent->parent; @@ -827,7 +827,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* * Set the subordinate bus number to its real value. */ - child->subordinate = max; + child->busn_res.end = max; pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } @@ -837,19 +837,19 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* Has only triggered on CardBus, fixup is in yenta_socket */ while (bus->parent) { - if ((child->subordinate > bus->subordinate) || - (child->number > bus->subordinate) || + if ((child->busn_res.end > bus->busn_res.end) || + (child->number > bus->busn_res.end) || (child->number < bus->number) || - (child->subordinate < bus->number)) { - dev_info(&child->dev, "[bus %02x-%02x] %s " - "hidden behind%s bridge %s [bus %02x-%02x]\n", - child->number, child->subordinate, - (bus->number > child->subordinate && - bus->subordinate < child->number) ? + (child->busn_res.end < bus->number)) { + dev_info(&child->dev, "%pR %s " + "hidden behind%s bridge %s %pR\n", + &child->busn_res, + (bus->number > child->busn_res.end && + bus->busn_res.end < child->number) ? "wholly" : "partially", bus->self->transparent ? " transparent" : "", dev_name(&bus->dev), - bus->number, bus->subordinate); + &bus->busn_res); } bus = bus->parent; } @@ -1548,7 +1548,7 @@ EXPORT_SYMBOL_GPL(pcie_bus_configure_settings); unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) { - unsigned int devfn, pass, max = bus->secondary; + unsigned int devfn, pass, max = bus->busn_res.start; struct pci_dev *dev; dev_dbg(&bus->dev, "scanning bus\n"); @@ -1642,7 +1642,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, /* Create legacy_io and legacy_mem files for this bus */ pci_create_legacy_files(b); - b->number = b->secondary = bus; + b->number = b->busn_res.start = bus; if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); @@ -1693,7 +1693,7 @@ struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, if (!b) return NULL; - b->subordinate = pci_scan_child_bus(b); + b->busn_res.end = pci_scan_child_bus(b); pci_bus_add_devices(b); return b; } @@ -1710,7 +1710,7 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, pci_add_resource(&resources, &iomem_resource); b = pci_create_root_bus(parent, bus, ops, sysdata, &resources); if (b) - b->subordinate = pci_scan_child_bus(b); + b->busn_res.end = pci_scan_child_bus(b); else pci_free_resource_list(&resources); return b; @@ -1727,7 +1727,7 @@ struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, pci_add_resource(&resources, &iomem_resource); b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources); if (b) { - b->subordinate = pci_scan_child_bus(b); + b->busn_res.end = pci_scan_child_bus(b); pci_bus_add_devices(b); } else { pci_free_resource_list(&resources); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8fa2d4be88de..192172c87b77 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -404,8 +404,8 @@ void pci_setup_cardbus(struct pci_bus *bus) struct resource *res; struct pci_bus_region region; - dev_info(&bridge->dev, "CardBus bridge to [bus %02x-%02x]\n", - bus->secondary, bus->subordinate); + dev_info(&bridge->dev, "CardBus bridge to %pR\n", + &bus->busn_res); res = bus->resource[0]; pcibios_resource_to_bus(bridge, ®ion, res); @@ -553,8 +553,8 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) { struct pci_dev *bridge = bus->self; - dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n", - bus->secondary, bus->subordinate); + dev_info(&bridge->dev, "PCI bridge to %pR\n", + &bus->busn_res); if (type & IORESOURCE_IO) pci_setup_bridge_io(bus); @@ -745,8 +745,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (!size0 && !size1) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " - "%pR to [bus %02x-%02x] (unused)\n", b_res, - bus->secondary, bus->subordinate); + "%pR to %pR (unused)\n", b_res, + &bus->busn_res); b_res->flags = 0; return; } @@ -757,8 +757,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096); dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " - "%pR to [bus %02x-%02x] add_size %lx\n", b_res, - bus->secondary, bus->subordinate, size1-size0); + "%pR to %pR add_size %lx\n", b_res, + &bus->busn_res, size1-size0); } } @@ -863,8 +863,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (!size0 && !size1) { if (b_res->start || b_res->end) dev_info(&bus->self->dev, "disabling bridge window " - "%pR to [bus %02x-%02x] (unused)\n", b_res, - bus->secondary, bus->subordinate); + "%pR to %pR (unused)\n", b_res, + &bus->busn_res); b_res->flags = 0; return 1; } @@ -874,8 +874,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (size1 > size0 && realloc_head) { add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window " - "%pR to [bus %02x-%02x] add_size %llx\n", b_res, - bus->secondary, bus->subordinate, (unsigned long long)size1-size0); + "%pR to %pR add_size %llx\n", b_res, + &bus->busn_res, (unsigned long long)size1-size0); } return 1; } diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 6e75153c5b4f..24caeaf50529 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -73,7 +73,7 @@ int __ref cb_alloc(struct pcmcia_socket *s) s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0)); pci_fixup_cardbus(bus); - max = bus->secondary; + max = bus->busn_res.start; for (pass = 0; pass < 2; pass++) list_for_each_entry(dev, &bus->devices, bus_list) if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index d07f9ac8c41d..667678db1153 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1048,8 +1048,8 @@ static void yenta_config_init(struct yenta_socket *socket) config_writeb(socket, PCI_LATENCY_TIMER, 168); config_writel(socket, PCI_PRIMARY_BUS, (176 << 24) | /* sec. latency timer */ - (dev->subordinate->subordinate << 16) | /* subordinate bus */ - (dev->subordinate->secondary << 8) | /* secondary bus */ + ((unsigned int)dev->subordinate->busn_res.end << 16) | /* subordinate bus */ + ((unsigned int)dev->subordinate->busn_res.start << 8) | /* secondary bus */ dev->subordinate->primary); /* primary bus */ /* @@ -1086,14 +1086,14 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) struct pci_bus *bridge_to_fix = cardbus_bridge->parent; /* Check bus numbers are already set up correctly: */ - if (bridge_to_fix->subordinate >= cardbus_bridge->subordinate) + if (bridge_to_fix->busn_res.end >= cardbus_bridge->busn_res.end) return; /* The subordinate number is ok, nothing to do */ if (!bridge_to_fix->parent) return; /* Root bridges are ok */ /* stay within the limits of the bus range of the parent: */ - upper_limit = bridge_to_fix->parent->subordinate; + upper_limit = bridge_to_fix->parent->busn_res.end; /* check the bus ranges of all silbling bridges to prevent overlap */ list_for_each(tmp, &bridge_to_fix->parent->children) { @@ -1104,36 +1104,36 @@ static void yenta_fixup_parent_bridge(struct pci_bus *cardbus_bridge) * current upper limit, set the new upper limit to * the bus number below the silbling's range: */ - if (silbling->secondary > bridge_to_fix->subordinate - && silbling->secondary <= upper_limit) - upper_limit = silbling->secondary - 1; + if (silbling->busn_res.start > bridge_to_fix->busn_res.end + && silbling->busn_res.start <= upper_limit) + upper_limit = silbling->busn_res.start - 1; } /* Show that the wanted subordinate number is not possible: */ - if (cardbus_bridge->subordinate > upper_limit) + if (cardbus_bridge->busn_res.end > upper_limit) dev_printk(KERN_WARNING, &cardbus_bridge->dev, "Upper limit for fixing this " "bridge's parent bridge: #%02x\n", upper_limit); /* If we have room to increase the bridge's subordinate number, */ - if (bridge_to_fix->subordinate < upper_limit) { + if (bridge_to_fix->busn_res.end < upper_limit) { /* use the highest number of the hidden bus, within limits */ unsigned char subordinate_to_assign = - min(cardbus_bridge->subordinate, upper_limit); + min_t(int, cardbus_bridge->busn_res.end, upper_limit); dev_printk(KERN_INFO, &bridge_to_fix->dev, "Raising subordinate bus# of parent " "bus (#%02x) from #%02x to #%02x\n", bridge_to_fix->number, - bridge_to_fix->subordinate, subordinate_to_assign); + (int)bridge_to_fix->busn_res.end, subordinate_to_assign); /* Save the new subordinate in the bus struct of the bridge */ - bridge_to_fix->subordinate = subordinate_to_assign; + bridge_to_fix->busn_res.end = subordinate_to_assign; /* and update the PCI config space with the new subordinate */ pci_write_config_byte(bridge_to_fix->self, - PCI_SUBORDINATE_BUS, bridge_to_fix->subordinate); + PCI_SUBORDINATE_BUS, bridge_to_fix->busn_res.end); } } -- cgit v1.2.3 From 5cc62c202211096ec26309722ec27455d52c8726 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: build a bus number resource tree for every domain This adds get_pci_domain_busn_res(), which returns the root of the bus number resource tree for a domain, creating it if necessary. We will later populate the tree with the bus numbers used by host bridges and P2P bridges in the domain. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 651b096134dc..674a477a6486 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -20,6 +20,36 @@ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); +static LIST_HEAD(pci_domain_busn_res_list); + +struct pci_domain_busn_res { + struct list_head list; + struct resource res; + int domain_nr; +}; + +static struct resource *get_pci_domain_busn_res(int domain_nr) +{ + struct pci_domain_busn_res *r; + + list_for_each_entry(r, &pci_domain_busn_res_list, list) + if (r->domain_nr == domain_nr) + return &r->res; + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + return NULL; + + r->domain_nr = domain_nr; + r->res.start = 0; + r->res.end = 0xff; + r->res.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED; + + list_add_tail(&r->list, &pci_domain_busn_res_list); + + return &r->res; +} + static int find_anything(struct device *dev, void *data) { return 1; -- cgit v1.2.3 From 98a3583107ed587ed3cfe2a1d8e5347421de5a80 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 18 May 2012 11:35:50 -0600 Subject: PCI: add busn_res operation functions Will use them insert/update busn res in pci_bus struct. [bhelgaas: print conflicting entry if insertion fails] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 674a477a6486..7662ab7b2640 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1714,6 +1714,74 @@ err_out: return NULL; } +int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) +{ + struct resource *res = &b->busn_res; + struct resource *parent_res, *conflict; + + res->start = bus; + res->end = bus_max; + res->flags = IORESOURCE_BUS; + + if (!pci_is_root_bus(b)) + parent_res = &b->parent->busn_res; + else { + parent_res = get_pci_domain_busn_res(pci_domain_nr(b)); + res->flags |= IORESOURCE_PCI_FIXED; + } + + conflict = insert_resource_conflict(parent_res, res); + + if (conflict) + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n", + res, pci_is_root_bus(b) ? "domain " : "", + parent_res, conflict->name, conflict); + else + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR is inserted under %s%pR\n", + res, pci_is_root_bus(b) ? "domain " : "", + parent_res); + + return conflict == NULL; +} + +int pci_bus_update_busn_res_end(struct pci_bus *b, int bus_max) +{ + struct resource *res = &b->busn_res; + struct resource old_res = *res; + resource_size_t size; + int ret; + + if (res->start > bus_max) + return -EINVAL; + + size = bus_max - res->start + 1; + ret = adjust_resource(res, res->start, size); + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR end %s updated to %02x\n", + &old_res, ret ? "can not be" : "is", bus_max); + + if (!ret && !res->parent) + pci_bus_insert_busn_res(b, res->start, res->end); + + return ret; +} + +void pci_bus_release_busn_res(struct pci_bus *b) +{ + struct resource *res = &b->busn_res; + int ret; + + if (!res->flags || !res->parent) + return; + + ret = release_resource(res); + dev_printk(KERN_DEBUG, &b->dev, + "busn_res: %pR %s released\n", + res, ret ? "can not be" : "is"); +} + struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { -- cgit v1.2.3 From f6dd68a77f9c07088eee71a1787cfc84dcf49198 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:11 -0700 Subject: PCI: release busn_res when removing bus Release bus number resource when removing a bus. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/remove.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index fd77e2bde2e8..04a4861b4749 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -68,6 +68,7 @@ void pci_remove_bus(struct pci_bus *pci_bus) down_write(&pci_bus_sem); list_del(&pci_bus->node); + pci_bus_release_busn_res(pci_bus); up_write(&pci_bus_sem); if (!pci_bus->is_added) return; -- cgit v1.2.3 From f848ffb1043ed0d168064176fb452cc51ec8e0b7 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: PCI: insert busn_res in pci_create_root_bus() That busn_res is from resources list. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7662ab7b2640..59011ce9840f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1684,7 +1684,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, list_move_tail(&window->list, &bridge->windows); res = window->res; offset = window->offset; - pci_bus_add_resource(b, res, 0); + if (res->flags & IORESOURCE_BUS) + pci_bus_insert_busn_res(b, bus, res->end); + else + pci_bus_add_resource(b, res, 0); if (offset) { if (resource_type(res) == IORESOURCE_IO) fmt = " (bus address [%#06llx-%#06llx])"; -- cgit v1.2.3 From 4d99f524234c2e772eea68ad019ec9c805991f23 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: PCI: checking busn_res in pci_scan_root_bus() Some callers do not supply the bus number aperture, usually because they do not know the end. In this case, we assume the aperture extends from the root bus number to bus 255, scan the bus, and shrink the bus number resource so it ends at the largest bus number we found. This is obviously not correct because the actual end of the aperture may well be larger than the largest bus number we found. But I guess it's all we have for now. Also print out one info about that, so we could find out which path does not have busn_res in resources list. [bhelgaas: changelog, _safe iterator unnecessary, use %pR format for bus] Signed-off-by: Yinghai Lu --- drivers/pci/probe.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 59011ce9840f..6258f6f24983 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1788,13 +1788,33 @@ void pci_bus_release_busn_res(struct pci_bus *b) struct pci_bus * __devinit pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { + struct pci_host_bridge_window *window; + bool found = false; struct pci_bus *b; + int max; + + list_for_each_entry(window, resources, list) + if (window->res->flags & IORESOURCE_BUS) { + found = true; + break; + } b = pci_create_root_bus(parent, bus, ops, sysdata, resources); if (!b) return NULL; - b->busn_res.end = pci_scan_child_bus(b); + if (!found) { + dev_info(&b->dev, + "No busn resource found for root bus, will use [bus %02x-ff]\n", + bus); + pci_bus_insert_busn_res(b, bus, 255); + } + + max = pci_scan_child_bus(b); + + if (!found) + pci_bus_update_busn_res_end(b, max); + pci_bus_add_devices(b); return b; } -- cgit v1.2.3 From 67cdc827286366acb6c60c821013c1185ee00b36 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: PCI: add default busn_resource We need to put into the resources list for legacy system. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6258f6f24983..68e75cb0831b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -16,6 +16,13 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +struct resource busn_resource = { + .name = "PCI busn", + .start = 0, + .end = 255, + .flags = IORESOURCE_BUS, +}; + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); -- cgit v1.2.3 From 857c3b668ae35c48d9d7a4248b6c0bdb65797d0e Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: PCI: add default busn_res for pci_scan_bus() also do not need to shrink busn_res. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 68e75cb0831b..9f68b9d3597c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1836,9 +1836,10 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, pci_add_resource(&resources, &ioport_resource); pci_add_resource(&resources, &iomem_resource); + pci_add_resource(&resources, &busn_resource); b = pci_create_root_bus(parent, bus, ops, sysdata, &resources); if (b) - b->busn_res.end = pci_scan_child_bus(b); + pci_scan_child_bus(b); else pci_free_resource_list(&resources); return b; @@ -1853,9 +1854,10 @@ struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, pci_add_resource(&resources, &ioport_resource); pci_add_resource(&resources, &iomem_resource); + pci_add_resource(&resources, &busn_resource); b = pci_create_root_bus(NULL, bus, ops, sysdata, &resources); if (b) { - b->busn_res.end = pci_scan_child_bus(b); + pci_scan_child_bus(b); pci_bus_add_devices(b); } else { pci_free_resource_list(&resources); -- cgit v1.2.3 From 30aa80da43a5f23728eae1516e3a1aad40e808dd Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:12 -0700 Subject: parisc/PCI: register busn_res for root buses Add the host bridge bus number aperture to the resource list. Like the MMIO and I/O port apertures, this is used when assigning resources to hot-added devices or in the case of conflicts. [bhelgaas: changelog, set flags directly, at LBA bus_num init] CC: Kyle McMartin CC: Helge Deller CC: linux-parisc@vger.kernel.org Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/parisc/dino.c | 10 ++++++++-- drivers/parisc/lba_pci.c | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 70517b0f94e6..ffddc4f64268 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -898,6 +898,7 @@ static int __init dino_probe(struct parisc_device *dev) LIST_HEAD(resources); struct pci_bus *bus; unsigned long hpa = dev->hpa.start; + int max; name = "Dino"; if (is_card_dino(&dev->id)) { @@ -983,6 +984,10 @@ static int __init dino_probe(struct parisc_device *dev) if (dino_dev->hba.gmmio_space.flags) pci_add_resource(&resources, &dino_dev->hba.gmmio_space); + dino_dev->hba.bus_num.start = dino_current_bus; + dino_dev->hba.bus_num.end = 255; + dino_dev->hba.bus_num.flags = IORESOURCE_BUS; + pci_add_resource(&resources, &dino_dev->hba.bus_num); /* ** It's not used to avoid chicken/egg problems ** with configuration accessor functions. @@ -998,12 +1003,13 @@ static int __init dino_probe(struct parisc_device *dev) return 0; } - bus->busn_res.end = pci_scan_child_bus(bus); + max = pci_scan_child_bus(bus); + pci_bus_update_busn_res_end(bus, max); /* This code *depends* on scanning being single threaded * if it isn't, this global bus number count will fail */ - dino_current_bus = bus->busn_res.end + 1; + dino_current_bus = max + 1; pci_bus_assign_resources(bus); pci_bus_add_devices(bus); return 0; diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index cd8f9ce8720f..4f9cf2456f4e 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -989,6 +989,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) case PAT_PBNUM: lba_dev->hba.bus_num.start = p->start; lba_dev->hba.bus_num.end = p->end; + lba_dev->hba.bus_num.flags = IORESOURCE_BUS; break; case PAT_LMMIO: @@ -1366,6 +1367,7 @@ lba_driver_probe(struct parisc_device *dev) void *tmp_obj; char *version; void __iomem *addr = ioremap_nocache(dev->hpa.start, 4096); + int max; /* Read HW Rev First */ func_class = READ_REG32(addr + LBA_FCLASS); @@ -1502,6 +1504,8 @@ lba_driver_probe(struct parisc_device *dev) if (lba_dev->hba.gmmio_space.flags) pci_add_resource(&resources, &lba_dev->hba.gmmio_space); + pci_add_resource(&resources, &lba_dev->hba.bus_num); + dev->dev.platform_data = lba_dev; lba_bus = lba_dev->hba.hba_bus = pci_create_root_bus(&dev->dev, lba_dev->hba.bus_num.start, @@ -1511,7 +1515,7 @@ lba_driver_probe(struct parisc_device *dev) return 0; } - lba_bus->busn_res.end = pci_scan_child_bus(lba_bus); + max = pci_scan_child_bus(lba_bus); /* This is in lieu of calling pci_assign_unassigned_resources() */ if (is_pdc_pat()) { @@ -1541,7 +1545,7 @@ lba_driver_probe(struct parisc_device *dev) lba_dev->flags |= LBA_FLAG_SKIP_PROBE; } - lba_next_bus = lba_res->busn_res.end + 1; + lba_next_bus = max + 1; pci_bus_add_devices(lba_bus); /* Whew! Finally done! Tell services we got this one covered. */ -- cgit v1.2.3 From b7eac055c0a8f6026393a83cdf9699e9052eae25 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: register busn_res for iov bus Insert that to tree. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index e873060fb35b..74bbaf82638d 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -47,7 +47,7 @@ static struct pci_bus *virtfn_add_bus(struct pci_bus *bus, int busnr) if (!child) return NULL; - child->busn_res.end = busnr; + pci_bus_insert_busn_res(child, busnr, busnr); child->dev.parent = bus->bridge; rc = pci_bus_add_child(child); if (rc) { -- cgit v1.2.3 From bc76b7310a352be1c2ed24133e89c5df24eff05e Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: insert busn_res for child bus Now we can insert busn_res now, after all root bus's get inserted. Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9f68b9d3597c..08404098080b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -755,7 +755,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, if (!child) goto out; child->primary = primary; - child->busn_res.end = subordinate; + pci_bus_insert_busn_res(child, secondary, subordinate); child->bridge_ctl = bctl; } @@ -793,6 +793,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, child = pci_add_new_bus(bus, dev, ++max); if (!child) goto out; + pci_bus_insert_busn_res(child, max, 0xff); } buses = (buses & 0xff000000) | ((unsigned int)(child->primary) << 0) @@ -864,7 +865,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, /* * Set the subordinate bus number to its real value. */ - child->busn_res.end = max; + pci_bus_update_busn_res_end(child, max); pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } -- cgit v1.2.3 From f406384628e97618955e17e8d61e59d5ecdc9ca0 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: cpci_hotplug: register busn_res Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpci_hotplug_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 42f3a61db87c..111b7d94c9a9 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -294,6 +294,7 @@ int __ref cpci_configure_slot(struct slot *slot) struct pci_bus *child; unsigned char busnr, start = parent->busn_res.start; unsigned char end = parent->busn_res.end; + int max; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), @@ -312,7 +313,8 @@ int __ref cpci_configure_slot(struct slot *slot) pci_dev_put(dev); continue; } - child->busn_res.end = pci_do_scan_bus(child); + max = pci_do_scan_bus(child); + pci_bus_update_busn_res_end(child, max); pci_bus_size_bridges(child); } pci_dev_put(dev); -- cgit v1.2.3 From 6cda0fcf26df18f0e5476fbff12845cc46e1f41b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: shpchp: register busn_res Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index d021eb031b3c..13069802b8c8 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -66,6 +66,7 @@ int __ref shpchp_configure_device(struct slot *p_slot) struct pci_bus *child; unsigned char busnr, start = parent->busn_res.start; unsigned char end = parent->busn_res.end; + int max; for (busnr = start; busnr <= end; busnr++) { if (!pci_find_bus(pci_domain_nr(parent), busnr)) @@ -84,7 +85,8 @@ int __ref shpchp_configure_device(struct slot *p_slot) pci_dev_put(dev); continue; } - child->busn_res.end = pci_do_scan_bus(child); + max = pci_do_scan_bus(child); + pci_bus_update_busn_res_end(child, max); pci_bus_size_bridges(child); } pci_configure_slot(dev); -- cgit v1.2.3 From 450878759aa10c6d172a0cafd5922b23a3b4547a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: cpqhp: register busn_res Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_pci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 1c8494021a42..24716725263f 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -106,9 +106,11 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) } if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + int max; pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); - pci_do_scan_bus(child); + max = pci_do_scan_bus(child); + pci_bus_update_busn_res_end(child, max); } pci_dev_put(func->pci_dev); -- cgit v1.2.3 From d0a350fe57c0a4014a8eccf8166d7dc2e599ce5c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: ibmhp: register busn_res Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/ibmphp_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 4fda7e6a86a7..a62f296e8b0d 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -805,9 +805,11 @@ static int ibm_configure_device(struct pci_func *func) } } if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { + int max; pci_read_config_byte(func->dev, PCI_SECONDARY_BUS, &bus); child = pci_add_new_bus(func->dev->bus, func->dev, bus); - pci_do_scan_bus(child); + max = pci_do_scan_bus(child); + pci_bus_update_busn_res_end(child, max); } return 0; -- cgit v1.2.3 From 85019faf4a88477cc72f56b6e371955ea0bb7a2a Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:51:13 -0700 Subject: PCI: sgihp: register busn_res Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/sgi_hotplug.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index de573113c102..b0bb3b537bb0 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -398,11 +398,13 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) sn_io_slot_fixup(dev); if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { unsigned char sec_bus; + int max; pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_bus); new_bus = pci_add_new_bus(dev->bus, dev, sec_bus); - pci_scan_child_bus(new_bus); + max = pci_scan_child_bus(new_bus); + pci_bus_update_busn_res_end(new_bus, max); new_ppb = 1; } pci_dev_put(dev); -- cgit v1.2.3 From a8e4b9c101ae58cc64cda0201229d3318701a7f0 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 18 May 2012 13:46:34 -0600 Subject: PCI: add generic pci_hp_add_bridge() This creates a generic pci_hp_add_bridge() that can be used by several hotplug drivers. [bhelgaas: split out from pciehp patch] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug-pci.c | 23 +++++++++++++++++++++++ drivers/pci/pci.h | 1 + 2 files changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/hotplug-pci.c b/drivers/pci/hotplug-pci.c index d3509cdeb554..44088c4fe687 100644 --- a/drivers/pci/hotplug-pci.c +++ b/drivers/pci/hotplug-pci.c @@ -4,6 +4,29 @@ #include #include "pci.h" +int __ref pci_hp_add_bridge(struct pci_dev *dev) +{ + struct pci_bus *parent = dev->bus; + int pass, busnr, start = parent->busn_res.start; + int end = parent->busn_res.end; + + for (busnr = start; busnr <= end; busnr++) { + if (!pci_find_bus(pci_domain_nr(parent), busnr)) + break; + } + if (busnr-- > end) { + printk(KERN_ERR "No bus number available for hot-added bridge %s\n", + pci_name(dev)); + return -1; + } + for (pass = 0; pass < 2; pass++) + busnr = pci_scan_bridge(parent, dev, busnr, pass); + if (!dev->subordinate) + return -1; + + return 0; +} +EXPORT_SYMBOL_GPL(pci_hp_add_bridge); unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..f3e14ce8eab7 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -124,6 +124,7 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; } #endif /* Functions for PCI Hotplug drivers to use */ +int pci_hp_add_bridge(struct pci_dev *dev); extern unsigned int pci_do_scan_bus(struct pci_bus *bus); #ifdef HAVE_PCI_LEGACY -- cgit v1.2.3 From c6da81a4d3cb8d461f77c5f61843fcc9c18b6c2c Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: pciehp: use generic pci_hp_add_bridge() Use the new generic pci_hp_add_bridge() interface. [bhelgaas: split "add generic pci_hp_add_bridge()" into a separate patch] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/pciehp_pci.c | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index b898f06b588d..09cecaf450c5 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -34,29 +34,6 @@ #include "../pci.h" #include "pciehp.h" -static int __ref pciehp_add_bridge(struct pci_dev *dev) -{ - struct pci_bus *parent = dev->bus; - int pass, busnr, start = parent->busn_res.start; - int end = parent->busn_res.end; - - for (busnr = start; busnr <= end; busnr++) { - if (!pci_find_bus(pci_domain_nr(parent), busnr)) - break; - } - if (busnr-- > end) { - err("No bus number available for hot-added bridge %s\n", - pci_name(dev)); - return -1; - } - for (pass = 0; pass < 2; pass++) - busnr = pci_scan_bridge(parent, dev, busnr, pass); - if (!dev->subordinate) - return -1; - - return 0; -} - int pciehp_configure_device(struct slot *p_slot) { struct pci_dev *dev; @@ -85,9 +62,8 @@ int pciehp_configure_device(struct slot *p_slot) if (!dev) continue; if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || - (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { - pciehp_add_bridge(dev); - } + (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) + pci_hp_add_bridge(dev); pci_dev_put(dev); } -- cgit v1.2.3 From 073ae10c0f8284b6c6dd9708656802507349ef91 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: cpci_hotplug: use generic pci_hp_add_bridge() Use the new generic pci_hp_add_bridge() interface. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpci_hotplug_pci.c | 37 ++++++---------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 111b7d94c9a9..dcc75c785443 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -285,44 +285,19 @@ int __ref cpci_configure_slot(struct slot *slot) for (fn = 0; fn < 8; fn++) { struct pci_dev *dev; - dev = pci_get_slot(parent, PCI_DEVFN(PCI_SLOT(slot->devfn), fn)); + dev = pci_get_slot(parent, + PCI_DEVFN(PCI_SLOT(slot->devfn), fn)); if (!dev) continue; if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || - (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { - /* Find an unused bus number for the new bridge */ - struct pci_bus *child; - unsigned char busnr, start = parent->busn_res.start; - unsigned char end = parent->busn_res.end; - int max; - - for (busnr = start; busnr <= end; busnr++) { - if (!pci_find_bus(pci_domain_nr(parent), - busnr)) - break; - } - if (busnr >= end) { - err("No free bus for hot-added bridge\n"); - pci_dev_put(dev); - continue; - } - child = pci_add_new_bus(parent, dev, busnr); - if (!child) { - err("Cannot add new bus for %s\n", - pci_name(dev)); - pci_dev_put(dev); - continue; - } - max = pci_do_scan_bus(child); - pci_bus_update_busn_res_end(child, max); - pci_bus_size_bridges(child); - } + (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) + pci_hp_add_bridge(dev); pci_dev_put(dev); } - pci_bus_assign_resources(parent); + pci_assign_unassigned_bridge_resources(parent->self); + pci_bus_add_devices(parent); - pci_enable_bridges(parent); dbg("%s - exit", __func__); return 0; -- cgit v1.2.3 From 7d01f70ac6f48733d595f1a54aa7c4d2ae3fef0d Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: shpchp: use generic pci_hp_add_bridge() Use the new generic pci_hp_add_bridge() interface. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp_pci.c | 47 +++++++++++++--------------------------- 1 file changed, 15 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index 13069802b8c8..c627ed9957d1 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -37,9 +37,10 @@ int __ref shpchp_configure_device(struct slot *p_slot) { struct pci_dev *dev; - struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; - int num, fn; struct controller *ctrl = p_slot->ctrl; + struct pci_dev *bridge = ctrl->pci_dev; + struct pci_bus *parent = bridge->subordinate; + int num, fn; dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, 0)); if (dev) { @@ -61,41 +62,23 @@ int __ref shpchp_configure_device(struct slot *p_slot) if (!dev) continue; if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || - (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { - /* Find an unused bus number for the new bridge */ - struct pci_bus *child; - unsigned char busnr, start = parent->busn_res.start; - unsigned char end = parent->busn_res.end; - int max; - for (busnr = start; busnr <= end; busnr++) { - if (!pci_find_bus(pci_domain_nr(parent), - busnr)) - break; - } - if (busnr > end) { - ctrl_err(ctrl, - "No free bus for hot-added bridge\n"); - pci_dev_put(dev); - continue; - } - child = pci_add_new_bus(parent, dev, busnr); - if (!child) { - ctrl_err(ctrl, "Cannot add new bus for %s\n", - pci_name(dev)); - pci_dev_put(dev); - continue; - } - max = pci_do_scan_bus(child); - pci_bus_update_busn_res_end(child, max); - pci_bus_size_bridges(child); - } + (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) + pci_hp_add_bridge(dev); + pci_dev_put(dev); + } + + pci_assign_unassigned_bridge_resources(bridge); + + for (fn = 0; fn < 8; fn++) { + dev = pci_get_slot(parent, PCI_DEVFN(p_slot->device, fn)); + if (!dev) + continue; pci_configure_slot(dev); pci_dev_put(dev); } - pci_bus_assign_resources(parent); pci_bus_add_devices(parent); - pci_enable_bridges(parent); + return 0; } -- cgit v1.2.3 From 04de975e7840e6d9da3ef44ab414f3ee1b98d611 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: cpqhp: use generic pci_hp_add_bridge() Use the new generic pci_hp_add_bridge() interface. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/cpqphp_pci.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 24716725263f..09801c6945ce 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -83,7 +83,6 @@ static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iom int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) { - unsigned char bus; struct pci_bus *child; int num; @@ -106,11 +105,10 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) } if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - int max; - pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); - child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); - max = pci_do_scan_bus(child); - pci_bus_update_busn_res_end(child, max); + pci_hp_add_bridge(func->pci_dev); + child = func->pci_dev->subordinate; + if (child) + pci_bus_add_devices(child); } pci_dev_put(func->pci_dev); -- cgit v1.2.3 From 2d7abf32925f1a8aeeb37234c8ea7590ebbe62be Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: ibmhp: use generic pci_hp_add_bridge() Use the new generic pci_hp_add_bridge() interface. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/ibmphp_core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index a62f296e8b0d..7dccad5fc891 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -775,7 +775,6 @@ static u8 bus_structure_fixup(u8 busno) static int ibm_configure_device(struct pci_func *func) { - unsigned char bus; struct pci_bus *child; int num; int flag = 0; /* this is to make sure we don't double scan the bus, @@ -805,11 +804,10 @@ static int ibm_configure_device(struct pci_func *func) } } if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) { - int max; - pci_read_config_byte(func->dev, PCI_SECONDARY_BUS, &bus); - child = pci_add_new_bus(func->dev->bus, func->dev, bus); - max = pci_do_scan_bus(child); - pci_bus_update_busn_res_end(child, max); + pci_hp_add_bridge(func->dev); + child = func->dev->subordinate; + if (child) + pci_bus_add_devices(child); } return 0; -- cgit v1.2.3 From 69ba29b9f91317b0cb8a4891c0cc6270a6f77ec9 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: sgihp: use generic pci_hp_add_bridge() Use the new generic pci_hp_add_bridge() interface. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/sgi_hotplug.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index b0bb3b537bb0..f64ca92253da 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -397,15 +397,11 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot) else sn_io_slot_fixup(dev); if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - unsigned char sec_bus; - int max; - pci_read_config_byte(dev, PCI_SECONDARY_BUS, - &sec_bus); - new_bus = pci_add_new_bus(dev->bus, dev, - sec_bus); - max = pci_scan_child_bus(new_bus); - pci_bus_update_busn_res_end(new_bus, max); - new_ppb = 1; + pci_hp_add_bridge(dev); + if (dev->subordinate) { + new_bus = dev->subordinate; + new_ppb = 1; + } } pci_dev_put(dev); } -- cgit v1.2.3 From 06aef8cec7563c40c7d7501d13ec1ed12f5e495b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Thu, 17 May 2012 18:58:41 -0700 Subject: PCI: hotplug: remove pci_do_scan_bus() All callers of pci_do_scan_bus() are gone, so remove it. Note that pci_do_scan_bus() was exported, so out-of-tree modules could depend on it. [bhelgaas: changelog] Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug-pci.c | 15 --------------- drivers/pci/pci.h | 1 - 2 files changed, 16 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug-pci.c b/drivers/pci/hotplug-pci.c index 44088c4fe687..6258dc260d9f 100644 --- a/drivers/pci/hotplug-pci.c +++ b/drivers/pci/hotplug-pci.c @@ -27,18 +27,3 @@ int __ref pci_hp_add_bridge(struct pci_dev *dev) return 0; } EXPORT_SYMBOL_GPL(pci_hp_add_bridge); - -unsigned int __devinit pci_do_scan_bus(struct pci_bus *bus) -{ - unsigned int max; - - max = pci_scan_child_bus(bus); - - /* - * Make the discovered devices available. - */ - pci_bus_add_devices(bus); - - return max; -} -EXPORT_SYMBOL(pci_do_scan_bus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f3e14ce8eab7..1c56ea8110b1 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -125,7 +125,6 @@ static inline int pci_proc_detach_bus(struct pci_bus *bus) { return 0; } /* Functions for PCI Hotplug drivers to use */ int pci_hp_add_bridge(struct pci_dev *dev); -extern unsigned int pci_do_scan_bus(struct pci_bus *bus); #ifdef HAVE_PCI_LEGACY extern void pci_create_legacy_files(struct pci_bus *bus); -- cgit v1.2.3 From 638f293307b5787b69bf0a0bc915aed491efbec9 Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Tue, 22 May 2012 21:58:40 -0600 Subject: PCI: acpiphp: fix function 0 leak when disabling a slot Previously, we acquired two references to function 0, but only released one. [bhelgaas: split this out from "remove all functions" fix] Signed-off-by: Amos Kong Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp_glue.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 62d0ae4dfcad..c8f999102765 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -893,6 +893,7 @@ static int disable_device(struct acpiphp_slot *slot) pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0)); if (!pdev) goto err_exit; + pci_dev_put(pdev); list_for_each_entry(func, &slot->funcs, sibling) { if (func->bridge) { -- cgit v1.2.3 From ce29ca3ea40744f24c2b5d88431e8ac566d257cc Mon Sep 17 00:00:00 2001 From: Amos Kong Date: Wed, 23 May 2012 10:20:35 -0600 Subject: PCI: acpiphp: remove all functions in slot, even without ACPI _EJx When we add a device with acpiphp, we enumerate all functions in the slot with pci_scan_slot(), regardless of whether they have associated ACPI methods such as _EJ0. When removing the device, we previously removed only the functions with those ACPI methods. This patch makes the remove symmetric with the add: we remove all functions in the slot, whether they have associated ACPI methods or not. With qemu-kvm and SeaBIOS, we can build a multi-function device where only function 0 has _EJ0 and _ADR (see bugzilla below). Removing and re-adding that slot (including all functions of the device) works correctly with Windows guests. This patch makes it also work in Linux guests. [bhelgaas: restructure loop iteration, pull out of slot->funcs loop] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=43219 Signed-off-by: Amos Kong Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp_glue.c | 42 +++++++++++++++++++++++++++++--------- drivers/pci/search.c | 2 ++ 2 files changed, 34 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index c8f999102765..73af3374e915 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -878,6 +878,24 @@ static void disable_bridges(struct pci_bus *bus) } } +/* return first device in slot, acquiring a reference on it */ +static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot) +{ + struct pci_bus *bus = slot->bridge->pci_bus; + struct pci_dev *dev; + struct pci_dev *ret = NULL; + + down_read(&pci_bus_sem); + list_for_each_entry(dev, &bus->devices, bus_list) + if (PCI_SLOT(dev->devfn) == slot->device) { + ret = pci_dev_get(dev); + break; + } + up_read(&pci_bus_sem); + + return ret; +} + /** * disable_device - disable a slot * @slot: ACPI PHP slot @@ -902,18 +920,22 @@ static int disable_device(struct acpiphp_slot *slot) (u32)1, NULL, NULL); func->bridge = NULL; } + } - pdev = pci_get_slot(slot->bridge->pci_bus, - PCI_DEVFN(slot->device, func->function)); - if (pdev) { - pci_stop_bus_device(pdev); - if (pdev->subordinate) { - disable_bridges(pdev->subordinate); - pci_disable_device(pdev); - } - __pci_remove_bus_device(pdev); - pci_dev_put(pdev); + /* + * enable_device() enumerates all functions in this device via + * pci_scan_slot(), whether they have associated ACPI hotplug + * methods (_EJ0, etc.) or not. Therefore, we remove all functions + * here. + */ + while ((pdev = dev_in_slot(slot))) { + pci_stop_bus_device(pdev); + if (pdev->subordinate) { + disable_bridges(pdev->subordinate); + pci_disable_device(pdev); } + __pci_remove_bus_device(pdev); + pci_dev_put(pdev); } list_for_each_entry(func, &slot->funcs, sibling) { diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 9d75dc8ca602..993d4a0a2469 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -15,6 +15,8 @@ #include "pci.h" DECLARE_RWSEM(pci_bus_sem); +EXPORT_SYMBOL_GPL(pci_bus_sem); + /* * find the upstream PCIe-to-PCI bridge of a PCI device * if the device is PCIE, return NULL -- cgit v1.2.3 From 0450243096de90ff51c3a6c605410c5e28d79f8d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 13 Jun 2012 05:30:07 +0000 Subject: bonding: drop_monitor aware When packets are dropped in TX path, its better to use kfree_skb() instead of dev_kfree_skb() to give proper drop_monitor events. Also move the kfree_skb() call after read_unlock() in bond_alb_xmit() and bond_xmit_activebackup() Signed-off-by: Eric Dumazet Cc: Jay Vosburgh Signed-off-by: David S. Miller --- drivers/net/bonding/bond_3ad.c | 2 +- drivers/net/bonding/bond_alb.c | 6 +++--- drivers/net/bonding/bond_main.c | 18 +++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 3031e0413114..a030e635f001 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2454,7 +2454,7 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) out: if (res) { /* no suitable interface, frame not sent */ - dev_kfree_skb(skb); + kfree_skb(skb); } return NETDEV_TX_OK; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index ef3791a09ad8..e15cc11edbbe 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1346,12 +1346,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) } } + read_unlock(&bond->curr_slave_lock); + if (res) { /* no suitable interface, frame not sent */ - dev_kfree_skb(skb); + kfree_skb(skb); } - read_unlock(&bond->curr_slave_lock); - return NETDEV_TX_OK; } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index af506321500b..f5a40b925f5e 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -3990,7 +3990,7 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev out: if (res) { /* no suitable interface, frame not sent */ - dev_kfree_skb(skb); + kfree_skb(skb); } return NETDEV_TX_OK; @@ -4012,11 +4012,11 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev); + read_unlock(&bond->curr_slave_lock); + if (res) /* no suitable interface, frame not sent */ - dev_kfree_skb(skb); - - read_unlock(&bond->curr_slave_lock); + kfree_skb(skb); return NETDEV_TX_OK; } @@ -4055,7 +4055,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev) if (res) { /* no suitable interface, frame not sent */ - dev_kfree_skb(skb); + kfree_skb(skb); } return NETDEV_TX_OK; @@ -4093,7 +4093,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) res = bond_dev_queue_xmit(bond, skb2, tx_dev); if (res) { - dev_kfree_skb(skb2); + kfree_skb(skb2); continue; } } @@ -4107,7 +4107,7 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) out: if (res) /* no suitable interface, frame not sent */ - dev_kfree_skb(skb); + kfree_skb(skb); /* frame sent to all suitable interfaces */ return NETDEV_TX_OK; @@ -4213,7 +4213,7 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev pr_err("%s: Error: Unknown bonding mode %d\n", dev->name, bond->params.mode); WARN_ON_ONCE(1); - dev_kfree_skb(skb); + kfree_skb(skb); return NETDEV_TX_OK; } } @@ -4235,7 +4235,7 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev) if (bond->slave_cnt) ret = __bond_start_xmit(skb, dev); else - dev_kfree_skb(skb); + kfree_skb(skb); read_unlock(&bond->lock); -- cgit v1.2.3 From cd1834591fe9564720ac4b0193bf1c790fe89f0d Mon Sep 17 00:00:00 2001 From: Heinz Graalfs Date: Mon, 11 Jun 2012 16:06:59 +0200 Subject: KVM: s390: Perform early event mask processing during boot For processing under KVM it is required to detect the actual SCLP console type in order to set it as preferred console. Signed-off-by: Heinz Graalfs Acked-by: Heiko Carstens Acked-by: Peter Oberparleiter Signed-off-by: Cornelia Huck Signed-off-by: Marcelo Tosatti --- drivers/s390/char/sclp.c | 10 ---------- drivers/s390/char/sclp.h | 10 ++++++++++ drivers/s390/char/sclp_cmd.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/s390/kvm/kvm_virtio.c | 3 ++- 4 files changed, 50 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 30f29a0020a1..3fcc000efc53 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -654,16 +654,6 @@ sclp_remove_processed(struct sccb_header *sccb) EXPORT_SYMBOL(sclp_remove_processed); -struct init_sccb { - struct sccb_header header; - u16 _reserved; - u16 mask_length; - sccb_mask_t receive_mask; - sccb_mask_t send_mask; - sccb_mask_t sclp_receive_mask; - sccb_mask_t sclp_send_mask; -} __attribute__((packed)); - /* Prepare init mask request. Called while sclp_lock is locked. */ static inline void __sclp_make_init_req(u32 receive_mask, u32 send_mask) diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 49a1bb52bc87..d7e97ae9ef6d 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -88,6 +88,16 @@ struct sccb_header { u16 response_code; } __attribute__((packed)); +struct init_sccb { + struct sccb_header header; + u16 _reserved; + u16 mask_length; + sccb_mask_t receive_mask; + sccb_mask_t send_mask; + sccb_mask_t sclp_receive_mask; + sccb_mask_t sclp_send_mask; +} __attribute__((packed)); + extern u64 sclp_facilities; #define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL) #define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL) diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index 766cb7b19b40..71ea923c322d 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -48,6 +48,7 @@ struct read_info_sccb { u8 _reserved5[4096 - 112]; /* 112-4095 */ } __attribute__((packed, aligned(PAGE_SIZE))); +static struct init_sccb __initdata early_event_mask_sccb __aligned(PAGE_SIZE); static struct read_info_sccb __initdata early_read_info_sccb; static int __initdata early_read_info_sccb_valid; @@ -104,6 +105,19 @@ static void __init sclp_read_info_early(void) } } +static void __init sclp_event_mask_early(void) +{ + struct init_sccb *sccb = &early_event_mask_sccb; + int rc; + + do { + memset(sccb, 0, sizeof(*sccb)); + sccb->header.length = sizeof(*sccb); + sccb->mask_length = sizeof(sccb_mask_t); + rc = sclp_cmd_sync_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb); + } while (rc == -EBUSY); +} + void __init sclp_facilities_detect(void) { struct read_info_sccb *sccb; @@ -119,6 +133,30 @@ void __init sclp_facilities_detect(void) rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; rzm <<= 20; + + sclp_event_mask_early(); +} + +bool __init sclp_has_linemode(void) +{ + struct init_sccb *sccb = &early_event_mask_sccb; + + if (sccb->header.response_code != 0x20) + return 0; + if (sccb->sclp_send_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)) + return 1; + return 0; +} + +bool __init sclp_has_vt220(void) +{ + struct init_sccb *sccb = &early_event_mask_sccb; + + if (sccb->header.response_code != 0x20) + return 0; + if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK) + return 1; + return 0; } unsigned long long sclp_get_rnmax(void) diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index d74e9ae6dfb3..d3bdae49512f 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -468,7 +469,7 @@ static __init int early_put_chars(u32 vtermno, const char *buf, int count) static int __init s390_virtio_console_init(void) { - if (!MACHINE_IS_KVM) + if (sclp_has_vt220() || sclp_has_linemode()) return -ENODEV; return virtio_cons_early_init(early_put_chars); } -- cgit v1.2.3 From f7ceb362c4d2f4e613982148ddb143ad6a205485 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 12 Jun 2012 20:19:24 +0200 Subject: dma: coh901318: use devm allocation Allocate memory, region, remap and irq for device state using devm_* helpers to simplify memory accounting. Signed-off-by: Linus Walleij Signed-off-by: Vinod Koul --- drivers/dma/coh901318.c | 72 +++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c index e67b4e06a918..aa384e53b7ac 100644 --- a/drivers/dma/coh901318.c +++ b/drivers/dma/coh901318.c @@ -1438,34 +1438,32 @@ static int __init coh901318_probe(struct platform_device *pdev) io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) - goto err_get_resource; + return -ENODEV; /* Map DMA controller registers to virtual memory */ - if (request_mem_region(io->start, - resource_size(io), - pdev->dev.driver->name) == NULL) { - err = -EBUSY; - goto err_request_mem; - } + if (devm_request_mem_region(&pdev->dev, + io->start, + resource_size(io), + pdev->dev.driver->name) == NULL) + return -ENOMEM; pdata = pdev->dev.platform_data; if (!pdata) - goto err_no_platformdata; + return -ENODEV; - base = kmalloc(ALIGN(sizeof(struct coh901318_base), 4) + - pdata->max_channels * - sizeof(struct coh901318_chan), - GFP_KERNEL); + base = devm_kzalloc(&pdev->dev, + ALIGN(sizeof(struct coh901318_base), 4) + + pdata->max_channels * + sizeof(struct coh901318_chan), + GFP_KERNEL); if (!base) - goto err_alloc_coh_dma_channels; + return -ENOMEM; base->chans = ((void *)base) + ALIGN(sizeof(struct coh901318_base), 4); - base->virtbase = ioremap(io->start, resource_size(io)); - if (!base->virtbase) { - err = -ENOMEM; - goto err_no_ioremap; - } + base->virtbase = devm_ioremap(&pdev->dev, io->start, resource_size(io)); + if (!base->virtbase) + return -ENOMEM; base->dev = &pdev->dev; base->platform = pdata; @@ -1474,25 +1472,20 @@ static int __init coh901318_probe(struct platform_device *pdev) COH901318_DEBUGFS_ASSIGN(debugfs_dma_base, base); - platform_set_drvdata(pdev, base); - irq = platform_get_irq(pdev, 0); if (irq < 0) - goto err_no_irq; - - err = request_irq(irq, dma_irq_handler, IRQF_DISABLED, - "coh901318", base); - if (err) { - dev_crit(&pdev->dev, - "Cannot allocate IRQ for DMA controller!\n"); - goto err_request_irq; - } + return irq; + + err = devm_request_irq(&pdev->dev, irq, dma_irq_handler, IRQF_DISABLED, + "coh901318", base); + if (err) + return err; err = coh901318_pool_create(&base->pool, &pdev->dev, sizeof(struct coh901318_lli), 32); if (err) - goto err_pool_create; + return err; /* init channels for device transfers */ coh901318_base_init(&base->dma_slave, base->platform->chans_slave, @@ -1538,6 +1531,7 @@ static int __init coh901318_probe(struct platform_device *pdev) if (err) goto err_register_memcpy; + platform_set_drvdata(pdev, base); dev_info(&pdev->dev, "Initialized COH901318 DMA on virtual base 0x%08x\n", (u32) base->virtbase); @@ -1547,19 +1541,6 @@ static int __init coh901318_probe(struct platform_device *pdev) dma_async_device_unregister(&base->dma_slave); err_register_slave: coh901318_pool_destroy(&base->pool); - err_pool_create: - free_irq(platform_get_irq(pdev, 0), base); - err_request_irq: - err_no_irq: - iounmap(base->virtbase); - err_no_ioremap: - kfree(base); - err_alloc_coh_dma_channels: - err_no_platformdata: - release_mem_region(pdev->resource->start, - resource_size(pdev->resource)); - err_request_mem: - err_get_resource: return err; } @@ -1570,11 +1551,6 @@ static int __exit coh901318_remove(struct platform_device *pdev) dma_async_device_unregister(&base->dma_memcpy); dma_async_device_unregister(&base->dma_slave); coh901318_pool_destroy(&base->pool); - free_irq(platform_get_irq(pdev, 0), base); - iounmap(base->virtbase); - kfree(base); - release_mem_region(pdev->resource->start, - resource_size(pdev->resource)); return 0; } -- cgit v1.2.3 From 185095fb80ce57c0f3db8738e36ad7c02dc34d33 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Thu, 7 Jun 2012 02:23:37 +0000 Subject: e1000e: use more informative logging macros when netdev not yet registered Based on a report from Ethan Zhao, before calling register_netdev() the driver should be using logging macros that do not display the potentially confusing "(unregistered net_device)" yet still display the useful driver name and PCI bus/device/function. Reported-by: Ethan Zhao Signed-off-by: Bruce Allan Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000e/netdev.c | 11 +++++--- drivers/net/ethernet/intel/e1000e/param.c | 43 +++++++++++++++++++----------- 2 files changed, 34 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 31d37a2b5ba8..ba86b3f8a404 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -6238,7 +6238,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev, } if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw)) - e_info("PHY reset is blocked due to SOL/IDER session.\n"); + dev_info(&pdev->dev, + "PHY reset is blocked due to SOL/IDER session.\n"); /* Set initial default active device features */ netdev->features = (NETIF_F_SG | @@ -6288,7 +6289,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, if (e1000_validate_nvm_checksum(&adapter->hw) >= 0) break; if (i == 2) { - e_err("The NVM Checksum Is Not Valid\n"); + dev_err(&pdev->dev, "The NVM Checksum Is Not Valid\n"); err = -EIO; goto err_eeprom; } @@ -6298,13 +6299,15 @@ static int __devinit e1000_probe(struct pci_dev *pdev, /* copy the MAC address */ if (e1000e_read_mac_addr(&adapter->hw)) - e_err("NVM Read Error while reading MAC address\n"); + dev_err(&pdev->dev, + "NVM Read Error while reading MAC address\n"); memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len); memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); if (!is_valid_ether_addr(netdev->perm_addr)) { - e_err("Invalid MAC Address: %pM\n", netdev->perm_addr); + dev_err(&pdev->dev, "Invalid MAC Address: %pM\n", + netdev->perm_addr); err = -EIO; goto err_eeprom; } diff --git a/drivers/net/ethernet/intel/e1000e/param.c b/drivers/net/ethernet/intel/e1000e/param.c index 55cc1565bc2f..dfbfa7fd98c3 100644 --- a/drivers/net/ethernet/intel/e1000e/param.c +++ b/drivers/net/ethernet/intel/e1000e/param.c @@ -199,16 +199,19 @@ static int __devinit e1000_validate_option(unsigned int *value, case enable_option: switch (*value) { case OPTION_ENABLED: - e_info("%s Enabled\n", opt->name); + dev_info(&adapter->pdev->dev, "%s Enabled\n", + opt->name); return 0; case OPTION_DISABLED: - e_info("%s Disabled\n", opt->name); + dev_info(&adapter->pdev->dev, "%s Disabled\n", + opt->name); return 0; } break; case range_option: if (*value >= opt->arg.r.min && *value <= opt->arg.r.max) { - e_info("%s set to %i\n", opt->name, *value); + dev_info(&adapter->pdev->dev, "%s set to %i\n", + opt->name, *value); return 0; } break; @@ -220,7 +223,8 @@ static int __devinit e1000_validate_option(unsigned int *value, ent = &opt->arg.l.p[i]; if (*value == ent->i) { if (ent->str[0] != '\0') - e_info("%s\n", ent->str); + dev_info(&adapter->pdev->dev, "%s\n", + ent->str); return 0; } } @@ -230,8 +234,8 @@ static int __devinit e1000_validate_option(unsigned int *value, BUG(); } - e_info("Invalid %s value specified (%i) %s\n", opt->name, *value, - opt->err); + dev_info(&adapter->pdev->dev, "Invalid %s value specified (%i) %s\n", + opt->name, *value, opt->err); *value = opt->def; return -1; } @@ -251,8 +255,10 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) int bd = adapter->bd_number; if (bd >= E1000_MAX_NIC) { - e_notice("Warning: no configuration for board #%i\n", bd); - e_notice("Using defaults for all values\n"); + dev_notice(&adapter->pdev->dev, + "Warning: no configuration for board #%i\n", bd); + dev_notice(&adapter->pdev->dev, + "Using defaults for all values\n"); } { /* Transmit Interrupt Delay */ @@ -366,27 +372,32 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) * default values */ if (adapter->itr > 4) - e_info("%s set to default %d\n", opt.name, - adapter->itr); + dev_info(&adapter->pdev->dev, + "%s set to default %d\n", opt.name, + adapter->itr); } adapter->itr_setting = adapter->itr; switch (adapter->itr) { case 0: - e_info("%s turned off\n", opt.name); + dev_info(&adapter->pdev->dev, "%s turned off\n", + opt.name); break; case 1: - e_info("%s set to dynamic mode\n", opt.name); + dev_info(&adapter->pdev->dev, + "%s set to dynamic mode\n", opt.name); adapter->itr = 20000; break; case 3: - e_info("%s set to dynamic conservative mode\n", - opt.name); + dev_info(&adapter->pdev->dev, + "%s set to dynamic conservative mode\n", + opt.name); adapter->itr = 20000; break; case 4: - e_info("%s set to simplified (2000-8000 ints) mode\n", - opt.name); + dev_info(&adapter->pdev->dev, + "%s set to simplified (2000-8000 ints) mode\n", + opt.name); break; default: /* -- cgit v1.2.3 From 4f8a91ad9aafbc7c70097a16404fa89aa41c8deb Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Wed, 28 Mar 2012 11:42:45 +0000 Subject: ixgbe: align flow control DV macros with datasheet The flow control DV macros are used to calculate the flow control high and low thresholds. This patch annotates these macros slightly better and fixes the issues below. The macro variables are renamed LINK to _max_frame_link and TC to _max_frame_tc. This was to avoid confusion and make them more readable. It was found that people auditing the code read TC to be 'traffic class' in the 802.1Q definition instead of the max frame size of the tc. Hopefully it is clear now. This audit also found the following real deviations from the theoretical values. Fixed in this patch. * I multiplied the DV calculations by (36/25) which always evaluates to 1. This does not match the intended theoretical value of 1.44. * IXGBE_BT2KB added 1023 to account for rounding however this really should be 8 * 1023 - 1 to account for division by 8k. * x2 multiplication of max frame in DV calculations to account for updated hardware recommendations. With this patch the DV values are inline with the recommendations in the 82599 and 82598 data sheets. Its worth noting I did not see any dropped frames with flow control on in my experiments without this patch. However aligning with the hardware specs and recommendations seems like a good idea here to account for worst case scenarios. Signed-off-by: John Fastabend Tested-by: Ross Brattain Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 37 ++++++++++++++++----------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 204848d2448c..1085c0739a3c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -2419,7 +2419,7 @@ typedef u32 ixgbe_physical_layer; */ /* BitTimes (BT) conversion */ -#define IXGBE_BT2KB(BT) ((BT + 1023) / (8 * 1024)) +#define IXGBE_BT2KB(BT) ((BT + (8 * 1024 - 1)) / (8 * 1024)) #define IXGBE_B2BT(BT) (BT * 8) /* Calculate Delay to respond to PFC */ @@ -2450,24 +2450,31 @@ typedef u32 ixgbe_physical_layer; #define IXGBE_PCI_DELAY 10000 /* Calculate X540 delay value in bit times */ -#define IXGBE_FILL_RATE (36 / 25) - -#define IXGBE_DV_X540(LINK, TC) (IXGBE_FILL_RATE * \ - (IXGBE_B2BT(LINK) + IXGBE_PFC_D + \ - (2 * IXGBE_CABLE_DC) + \ - (2 * IXGBE_ID_X540) + \ - IXGBE_HD + IXGBE_B2BT(TC))) +#define IXGBE_DV_X540(_max_frame_link, _max_frame_tc) \ + ((36 * \ + (IXGBE_B2BT(_max_frame_link) + \ + IXGBE_PFC_D + \ + (2 * IXGBE_CABLE_DC) + \ + (2 * IXGBE_ID_X540) + \ + IXGBE_HD) / 25 + 1) + \ + 2 * IXGBE_B2BT(_max_frame_tc)) /* Calculate 82599, 82598 delay value in bit times */ -#define IXGBE_DV(LINK, TC) (IXGBE_FILL_RATE * \ - (IXGBE_B2BT(LINK) + IXGBE_PFC_D + \ - (2 * IXGBE_CABLE_DC) + (2 * IXGBE_ID) + \ - IXGBE_HD + IXGBE_B2BT(TC))) +#define IXGBE_DV(_max_frame_link, _max_frame_tc) \ + ((36 * \ + (IXGBE_B2BT(_max_frame_link) + \ + IXGBE_PFC_D + \ + (2 * IXGBE_CABLE_DC) + \ + (2 * IXGBE_ID) + \ + IXGBE_HD) / 25 + 1) + \ + 2 * IXGBE_B2BT(_max_frame_tc)) /* Calculate low threshold delay values */ -#define IXGBE_LOW_DV_X540(TC) (2 * IXGBE_B2BT(TC) + \ - (IXGBE_FILL_RATE * IXGBE_PCI_DELAY)) -#define IXGBE_LOW_DV(TC) (2 * IXGBE_LOW_DV_X540(TC)) +#define IXGBE_LOW_DV_X540(_max_frame_tc) \ + (2 * IXGBE_B2BT(_max_frame_tc) + \ + (36 * IXGBE_PCI_DELAY / 25) + 1) +#define IXGBE_LOW_DV(_max_frame_tc) \ + (2 * IXGBE_LOW_DV_X540(_max_frame_tc)) /* Software ATR hash keys */ #define IXGBE_ATR_BUCKET_HASH_KEY 0x3DAD14E2 -- cgit v1.2.3 From 6cbc52ef10ba3c7fbd8954270c025ffbf643ab05 Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 16 May 2012 07:06:38 +0000 Subject: ixgbe: do not compile ixgbe_sysfs.c when CONFIG_IXGBE_HWMON is not set ixgbe_sysfs.c is only needed when CONFIG_IXGBE_HWMON is configured in the kernel. Signed-off-by: Emil Tantilov Acked-by: Don Skidmore Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/Makefile | 4 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/Makefile b/drivers/net/ethernet/intel/ixgbe/Makefile index 0bdf06bc5c49..5fd5d04c26c9 100644 --- a/drivers/net/ethernet/intel/ixgbe/Makefile +++ b/drivers/net/ethernet/intel/ixgbe/Makefile @@ -34,11 +34,11 @@ obj-$(CONFIG_IXGBE) += ixgbe.o ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_ethtool.o \ ixgbe_82599.o ixgbe_82598.o ixgbe_phy.o ixgbe_sriov.o \ - ixgbe_mbx.o ixgbe_x540.o ixgbe_sysfs.o ixgbe_lib.o + ixgbe_mbx.o ixgbe_x540.o ixgbe_lib.o ixgbe-$(CONFIG_IXGBE_DCB) += ixgbe_dcb.o ixgbe_dcb_82598.o \ ixgbe_dcb_82599.o ixgbe_dcb_nl.o ixgbe-$(CONFIG_IXGBE_PTP) += ixgbe_ptp.o - +ixgbe-$(CONFIG_IXGBE_HWMON) += ixgbe_sysfs.o ixgbe-$(CONFIG_FCOE:m=y) += ixgbe_fcoe.o diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c index 1d80b1cefa6a..2334fce47018 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sysfs.c @@ -37,7 +37,6 @@ #include #include -#ifdef CONFIG_IXGBE_HWMON /* hwmon callback functions */ static ssize_t ixgbe_hwmon_show_location(struct device *dev, struct device_attribute *attr, @@ -241,5 +240,4 @@ err: exit: return rc; } -#endif /* CONFIG_IXGBE_HWMON */ -- cgit v1.2.3 From 0ede4a606af1778b24b84b7feed3c0bed2751a34 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 22 May 2012 06:08:32 +0000 Subject: ixgbe: ptp code cleanup This patch fixes two minor nits from Richard Cochran. The first is a case of ambitious line wrapping that wasn't necessary. The second is to re-order the flag checks for PPS support. Previously, the hardware test was done first, and the interrupt flag test was done second. Now, test the interrupt flag and use the unlikely macro. Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 8 +++----- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 13 +++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 17ad6a3c1be1..1675b662da06 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -790,12 +790,10 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_q_vector *q_vector, total_packets += tx_buffer->gso_segs; #ifdef CONFIG_IXGBE_PTP - if (unlikely(tx_buffer->tx_flags & - IXGBE_TX_FLAGS_TSTAMP)) - ixgbe_ptp_tx_hwtstamp(q_vector, - tx_buffer->skb); - + if (unlikely(tx_buffer->tx_flags & IXGBE_TX_FLAGS_TSTAMP)) + ixgbe_ptp_tx_hwtstamp(q_vector, tx_buffer->skb); #endif + /* free the skb */ dev_kfree_skb_any(tx_buffer->skb); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index ddc6a4d19302..174f41fd1d2f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -307,13 +307,14 @@ void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr) !(adapter->flags2 & IXGBE_FLAG2_PTP_PPS_ENABLED)) return; - switch (hw->mac.type) { - case ixgbe_mac_X540: - if (eicr & IXGBE_EICR_TIMESYNC) + if (unlikely(eicr & IXGBE_EICR_TIMESYNC)) { + switch (hw->mac.type) { + case ixgbe_mac_X540: ptp_clock_event(adapter->ptp_clock, &event); - break; - default: - break; + break; + default: + break; + } } } -- cgit v1.2.3 From c19197a7866fee6ff9985d9dc9962bc2ccbd3d7b Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 22 May 2012 06:08:37 +0000 Subject: ixgbe: PTP Fix hwtstamp mode settings When enabling the hwtstamp mode for Rx timestamping the V2 ptp event type specific modes (Delay Request and Sync) have been rolled into the V2 all event packet modes, in order to more accurately represent what hardware is doing. Hardware always timestamps the Path delay packets when a V2 mode is selected, regardless of what type was selected (in order to always support Path delay mode). However this means the user selected modes of timestamping only Sync or Delay Request is not truly supported. This patch correctly sets the mode for the hwtstamp config and returns to the user that all V2 event packets will be timestamped. Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 174f41fd1d2f..5ed8cffee178 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -540,6 +540,11 @@ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector, * type has to be specified. Matching the kind of event packet is * not supported, with the exception of "all V2 events regardless of * level 2 or 4". + * + * Since hardware always timestamps Path delay packets when timestamping V2 + * packets, regardless of the type specified in the register, only use V2 + * Event mode. This more accurately tells the user what the hardware is going + * to do anyways. */ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, struct ifreq *ifr, int cmd) @@ -583,27 +588,15 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, tsync_rx_mtrl = IXGBE_RXMTRL_V1_DELAY_REQ_MSG; is_l4 = true; break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: case HWTSTAMP_FILTER_PTP_V2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: - tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L2_L4_V2; - tsync_rx_mtrl = IXGBE_RXMTRL_V2_SYNC_MSG; - is_l2 = true; - is_l4 = true; - config.rx_filter = HWTSTAMP_FILTER_SOME; - break; case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L2_L4_V2; - tsync_rx_mtrl = IXGBE_RXMTRL_V2_DELAY_REQ_MSG; - is_l2 = true; - is_l4 = true; - config.rx_filter = HWTSTAMP_FILTER_SOME; - break; - case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_EVENT: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2; config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; is_l2 = true; -- cgit v1.2.3 From 1d1a79b5b94b0aa84e1e78dd9acdcffb12274848 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Tue, 22 May 2012 06:18:08 +0000 Subject: ixgbe: Check PTP Rx timestamps via BPF filter This patch fixes a potential Rx timestamp deadlock that causes the Rx timestamping to stall indefinitely. The issue could occur when a PTP packet is timestamped by hardware but never reaches the Rx queue. In order to prevent a permanent loss of timestamping, the RXSTMP(L/H) registers have to be read to unlock them. (This used to only occur when a packet that was timestamped reached the software.) However the registers can't be read early otherwise there is no way to correlate them to the packet. This patch introduces a filter function which can be used to determine if a packet should have been timestamped. Supplied with the filter setup by the hwtstamp ioctl, check to make sure the PTP protocol and message type match the expected values. If so, then read the timestamp registers (to free them.) At this point check the descriptor bit, if the bit is set then we know this packet correlates to the timestamp stored in the RXTSTAMP registers. Otherwise, assume that packet was dropped by the hardware, and ignore this timestamp value. However, we have at least unlocked the rxtstamp registers for future timestamping. Due to the way the driver handles skb data, it cannot be directly accessed. In order to work around this, a copy of the skb data into a linear buffer is made. From this buffer it becomes possible to read the data correctly Signed-off-by: Jacob Keller Reviewed-by: Richard Cochran Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe.h | 2 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 3 +- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 113 +++++++++++++++++++++++--- 3 files changed, 104 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 3ef3c5284e52..41f9f6e2a4c1 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -561,6 +561,7 @@ struct ixgbe_adapter { spinlock_t tmreg_lock; struct cyclecounter cc; struct timecounter tc; + int rx_hwtstamp_filter; u32 base_incval; u32 cycle_speed; #endif /* CONFIG_IXGBE_PTP */ @@ -718,6 +719,7 @@ extern void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter); extern void ixgbe_ptp_tx_hwtstamp(struct ixgbe_q_vector *q_vector, struct sk_buff *skb); extern void ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector, + union ixgbe_adv_rx_desc *rx_desc, struct sk_buff *skb); extern int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, struct ifreq *ifr, int cmd); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 1675b662da06..b0ddfd47e473 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1397,8 +1397,7 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring, ixgbe_rx_checksum(rx_ring, rx_desc, skb); #ifdef CONFIG_IXGBE_PTP - if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS)) - ixgbe_ptp_rx_hwtstamp(rx_ring->q_vector, skb); + ixgbe_ptp_rx_hwtstamp(rx_ring->q_vector, rx_desc, skb); #endif if ((dev->features & NETIF_F_HW_VLAN_RX) && diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 5ed8cffee178..cb7d1b2982c5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -26,6 +26,7 @@ *******************************************************************************/ #include "ixgbe.h" #include +#include /* * The 82599 and the X540 do not have true 64bit nanosecond scale @@ -100,6 +101,10 @@ #define NSECS_PER_SEC 1000000000ULL #endif +static struct sock_filter ptp_filter[] = { + PTP_FILTER +}; + /** * ixgbe_ptp_read - read raw cycle counter (to be used by time counter) * @cc - the cyclecounter structure @@ -425,6 +430,68 @@ void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter) } } +/** + * ixgbe_ptp_match - determine if this skb matches a ptp packet + * @skb: pointer to the skb + * @hwtstamp: pointer to the hwtstamp_config to check + * + * Determine whether the skb should have been timestamped, assuming the + * hwtstamp was set via the hwtstamp ioctl. Returns non-zero when the packet + * should have a timestamp waiting in the registers, and 0 otherwise. + * + * V1 packets have to check the version type to determine whether they are + * correct. However, we can't directly access the data because it might be + * fragmented in the SKB, in paged memory. In order to work around this, we + * use skb_copy_bits which will properly copy the data whether it is in the + * paged memory fragments or not. We have to copy the IP header as well as the + * message type. + */ +static int ixgbe_ptp_match(struct sk_buff *skb, int rx_filter) +{ + struct iphdr iph; + u8 msgtype; + unsigned int type, offset; + + if (rx_filter == HWTSTAMP_FILTER_NONE) + return 0; + + type = sk_run_filter(skb, ptp_filter); + + if (likely(rx_filter == HWTSTAMP_FILTER_PTP_V2_EVENT)) + return type & PTP_CLASS_V2; + + /* For the remaining cases actually check message type */ + switch (type) { + case PTP_CLASS_V1_IPV4: + skb_copy_bits(skb, OFF_IHL, &iph, sizeof(iph)); + offset = ETH_HLEN + (iph.ihl << 2) + UDP_HLEN + OFF_PTP_CONTROL; + break; + case PTP_CLASS_V1_IPV6: + offset = OFF_PTP6 + OFF_PTP_CONTROL; + break; + default: + /* other cases invalid or handled above */ + return 0; + } + + /* Make sure our buffer is long enough */ + if (skb->len < offset) + return 0; + + skb_copy_bits(skb, offset, &msgtype, sizeof(msgtype)); + + switch (rx_filter) { + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + return (msgtype == IXGBE_RXMTRL_V1_SYNC_MSG); + break; + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + return (msgtype == IXGBE_RXMTRL_V1_DELAY_REQ_MSG); + break; + default: + return 0; + } +} + /** * ixgbe_ptp_tx_hwtstamp - utility function which checks for TX time stamp * @q_vector: structure containing interrupt and ring information @@ -474,6 +541,7 @@ void ixgbe_ptp_tx_hwtstamp(struct ixgbe_q_vector *q_vector, /** * ixgbe_ptp_rx_hwtstamp - utility function which checks for RX time stamp * @q_vector: structure containing interrupt and ring information + * @rx_desc: the rx descriptor * @skb: particular skb to send timestamp with * * if the timestamp is valid, we convert it into the timecounter ns @@ -481,6 +549,7 @@ void ixgbe_ptp_tx_hwtstamp(struct ixgbe_q_vector *q_vector, * is passed up the network stack */ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector, + union ixgbe_adv_rx_desc *rx_desc, struct sk_buff *skb) { struct ixgbe_adapter *adapter; @@ -498,21 +567,33 @@ void ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector, hw = &adapter->hw; tsyncrxctl = IXGBE_READ_REG(hw, IXGBE_TSYNCRXCTL); + + /* Check if we have a valid timestamp and make sure the skb should + * have been timestamped */ + if (likely(!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID) || + !ixgbe_ptp_match(skb, adapter->rx_hwtstamp_filter))) + return; + + /* + * Always read the registers, in order to clear a possible fault + * because of stagnant RX timestamp values for a packet that never + * reached the queue. + */ regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPL); regval |= (u64)IXGBE_READ_REG(hw, IXGBE_RXSTMPH) << 32; /* - * If this bit is set, then the RX registers contain the time stamp. No - * other packet will be time stamped until we read these registers, so - * read the registers to make them available again. Because only one - * packet can be time stamped at a time, we know that the register - * values must belong to this one here and therefore we don't need to - * compare any of the additional attributes stored for it. + * If the timestamp bit is set in the packet's descriptor, we know the + * timestamp belongs to this packet. No other packet can be + * timestamped until the registers for timestamping have been read. + * Therefor only one packet with this bit can be in the queue at a + * time, and the rx timestamp values that were in the registers belong + * to this packet. * * If nothing went wrong, then it should have a skb_shared_tx that we * can turn into a skb_shared_hwtstamps. */ - if (!(tsyncrxctl & IXGBE_TSYNCRXCTL_VALID)) + if (unlikely(!ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_TS))) return; spin_lock_irqsave(&adapter->tmreg_lock, flags); @@ -598,19 +679,20 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2; - config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; is_l2 = true; is_l4 = true; + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_ALL: default: /* - * register RXMTRL must be set, therefore it is not - * possible to time stamp both V1 Sync and Delay_Req messages - * and hardware does not support timestamping all packets - * => return error + * register RXMTRL must be set in order to do V1 packets, + * therefore it is not possible to time stamp both V1 Sync and + * Delay_Req messages and hardware does not support + * timestamping all packets => return error */ + config.rx_filter = HWTSTAMP_FILTER_NONE; return -ERANGE; } @@ -620,6 +702,9 @@ int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, return 0; } + /* Store filter value for later use */ + adapter->rx_hwtstamp_filter = config.rx_filter; + /* define ethertype filter for timestamped packets */ if (is_l2) IXGBE_WRITE_REG(hw, IXGBE_ETQF(3), @@ -855,6 +940,10 @@ void ixgbe_ptp_init(struct ixgbe_adapter *adapter) return; } + /* initialize the ptp filter */ + if (ptp_filter_init(ptp_filter, ARRAY_SIZE(ptp_filter))) + e_dev_warn("ptp_filter_init failed\n"); + spin_lock_init(&adapter->tmreg_lock); ixgbe_ptp_start_cyclecounter(adapter); -- cgit v1.2.3 From c85251f8562095cd6fd63ae786354283c5318303 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 11 Jun 2012 13:36:12 -0700 Subject: ath6kl: fix fw capability parsing This patch fixes a bug where no capabilites are parsed when the number of firmware capability bits translate into fewer bytes than the host has knowledge of. Instead just process number of capability bytes as reported by the firmware. Signed-off-by: Thomas Pedersen Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 62e25ccd1016..f90b5db741cf 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1004,9 +1004,6 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) ar->hw.reserved_ram_size); break; case ATH6KL_FW_IE_CAPABILITIES: - if (ie_len < DIV_ROUND_UP(ATH6KL_FW_CAPABILITY_MAX, 8)) - break; - ath6kl_dbg(ATH6KL_DBG_BOOT, "found firmware capabilities ie (%zd B)\n", ie_len); @@ -1015,6 +1012,9 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) index = i / 8; bit = i % 8; + if (index == ie_len) + break; + if (data[index] & (1 << bit)) __set_bit(i, ar->fw_capabilities); } -- cgit v1.2.3 From daf67ce8cffd148308ae15c25fb06c1fc20c88f1 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 14 Jun 2012 21:35:26 +0300 Subject: iwlwifi: unlock on error path We introduced a lock here in ff1ffb850b ("iwlwifi: fix dynamic loading"). But we missed an error path which needs an unlock. Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-drv.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 095547b37a27..a175997e7829 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -911,8 +911,10 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) const struct iwl_op_mode_ops *ops = op->ops; drv->op_mode = ops->start(drv->trans, drv->cfg, &drv->fw); - if (!drv->op_mode) + if (!drv->op_mode) { + mutex_unlock(&iwlwifi_opmode_table_mtx); goto out_unbind; + } } else { load_module = true; } -- cgit v1.2.3 From 107a84e61cdd3406c842a0e4be7efffd3a05dba6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 14 Jun 2012 09:12:35 +0200 Subject: of: match by compatible property first When matching devices against an OF device ID table, the first string of the compatible property that is listed in the table should match, regardless of its position in the table. Cc: Grant Likely Cc: Rob Herring Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Thierry Reding Signed-off-by: Rob Herring --- drivers/of/base.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index d9bfd49b1935..eada3f4ef801 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -511,6 +511,22 @@ out: } EXPORT_SYMBOL(of_find_node_with_property); +static const struct of_device_id *of_match_compat(const struct of_device_id *matches, + const char *compat) +{ + while (matches->name[0] || matches->type[0] || matches->compatible[0]) { + const char *cp = matches->compatible; + int len = strlen(cp); + + if (len > 0 && of_compat_cmp(compat, cp, len) == 0) + return matches; + + matches++; + } + + return NULL; +} + /** * of_match_node - Tell if an device_node has a matching of_match structure * @matches: array of of device match structures to search in @@ -521,9 +537,18 @@ EXPORT_SYMBOL(of_find_node_with_property); const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) { + struct property *prop; + const char *cp; + if (!matches) return NULL; + of_property_for_each_string(node, "compatible", prop, cp) { + const struct of_device_id *match = of_match_compat(matches, cp); + if (match) + return match; + } + while (matches->name[0] || matches->type[0] || matches->compatible[0]) { int match = 1; if (matches->name[0]) @@ -532,10 +557,7 @@ const struct of_device_id *of_match_node(const struct of_device_id *matches, if (matches->type[0]) match &= node->type && !strcmp(matches->type, node->type); - if (matches->compatible[0]) - match &= of_device_is_compatible(node, - matches->compatible); - if (match) + if (match && !matches->compatible[0]) return matches; matches++; } -- cgit v1.2.3 From 7eb9ae0799b1e9f0b77733b432bc5f6f055b020b Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 14 Jun 2012 18:28:49 -0700 Subject: irq/apic: Use config_enabled(CONFIG_SMP) checks to clean up irq_set_affinity() for UP Move the ->irq_set_affinity() routines out of the #ifdef CONFIG_SMP sections and use config_enabled(CONFIG_SMP) checks inside those routines. Thus making those routines simple null stubs for !CONFIG_SMP and retaining those routines with no additional runtime overhead for CONFIG_SMP kernels. Cleans up the ifdef CONFIG_SMP in and around routines related to irq_set_affinity in io_apic and irq_remapping subsystems. Signed-off-by: Suresh Siddha Cc: torvalds@linux-foundation.org Cc: joerg.roedel@amd.com Cc: Sam Ravnborg Cc: Paul Gortmaker Link: http://lkml.kernel.org/r/1339723729.3475.63.camel@sbsiddha-desk.sc.intel.com Signed-off-by: Ingo Molnar --- drivers/iommu/intel_irq_remapping.c | 7 +++---- drivers/iommu/irq_remapping.c | 5 ++--- drivers/iommu/irq_remapping.h | 2 -- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index 853902a1b7db..e0b18f3ae9a8 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -902,7 +902,6 @@ static int intel_setup_ioapic_entry(int irq, return 0; } -#ifdef CONFIG_SMP /* * Migrate the IO-APIC irq in the presence of intr-remapping. * @@ -926,6 +925,9 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, struct irte irte; int err; + if (!config_enabled(CONFIG_SMP)) + return -EINVAL; + if (!cpumask_intersects(mask, cpu_online_mask)) return -EINVAL; @@ -963,7 +965,6 @@ intel_ioapic_set_affinity(struct irq_data *data, const struct cpumask *mask, cpumask_copy(data->affinity, mask); return 0; } -#endif static void intel_compose_msi_msg(struct pci_dev *pdev, unsigned int irq, unsigned int dest, @@ -1065,9 +1066,7 @@ struct irq_remap_ops intel_irq_remap_ops = { .reenable = reenable_irq_remapping, .enable_faulting = enable_drhd_fault_handling, .setup_ioapic_entry = intel_setup_ioapic_entry, -#ifdef CONFIG_SMP .set_affinity = intel_ioapic_set_affinity, -#endif .free_irq = free_irte, .compose_msi_msg = intel_compose_msi_msg, .msi_alloc_irq = intel_msi_alloc_irq, diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index 40cda8e98d87..1d29b1c66e72 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -111,16 +111,15 @@ int setup_ioapic_remapped_entry(int irq, vector, attr); } -#ifdef CONFIG_SMP int set_remapped_irq_affinity(struct irq_data *data, const struct cpumask *mask, bool force) { - if (!remap_ops || !remap_ops->set_affinity) + if (!config_enabled(CONFIG_SMP) || !remap_ops || + !remap_ops->set_affinity) return 0; return remap_ops->set_affinity(data, mask, force); } -#endif void free_remapped_irq(int irq) { diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h index be9d72950c51..b12974cc1dfe 100644 --- a/drivers/iommu/irq_remapping.h +++ b/drivers/iommu/irq_remapping.h @@ -59,11 +59,9 @@ struct irq_remap_ops { unsigned int, int, struct io_apic_irq_attr *); -#ifdef CONFIG_SMP /* Set the CPU affinity of a remapped interrupt */ int (*set_affinity)(struct irq_data *data, const struct cpumask *mask, bool force); -#endif /* Free an IRQ */ int (*free_irq)(int); -- cgit v1.2.3 From fbebb9fd22581b6422d60669c4ff86ce99d6cdba Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Sat, 16 Jun 2012 14:40:22 -0600 Subject: PCI: add infrastructure for devices with broken INTx masking pci_intx_mask_supported() assumes INTx masking is supported if the PCI_COMMAND_INTX_DISABLE bit is writable. But when that bit is set, some devices don't actually mask INTx or update PCI_STATUS_INTERRUPT as we expect. This patch adds a way for quirks to identify these broken devices. [bhelgaas: split out from Chelsio quirk addition] Signed-off-by: Jan Kiszka Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +++ drivers/pci/quirks.c | 10 ++++++++++ 2 files changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..9ae517a68360 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2876,6 +2876,9 @@ bool pci_intx_mask_supported(struct pci_dev *dev) bool mask_supported = false; u16 orig, new; + if (dev->broken_intx_masking) + return false; + pci_cfg_access_lock(dev); pci_read_config_word(dev, PCI_COMMAND, &orig); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 2a7521677541..cc13415416d7 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2929,6 +2929,16 @@ static void __devinit disable_igfx_irq(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); +/* + * Some devices may pass our check in pci_intx_mask_supported if + * PCI_COMMAND_INTX_DISABLE works though they actually do not properly + * support this feature. + */ +static void __devinit quirk_broken_intx_masking(struct pci_dev *dev) +{ + dev->broken_intx_masking = 1; +} + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { -- cgit v1.2.3 From 555069dad01c955f41593711ce2a688c668f8234 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 16 Jun 2012 15:45:41 +0000 Subject: bnx2: Dump all FTQ_CTL registers during tx_timeout to help debug tx timeouts reported in the field. Reviewed-by Benjamin Li Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 71 ++++++++++++++++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnx2.h | 41 +++++++++++++++++++++ 2 files changed, 112 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index ff5d3c1f1217..8eaab0ce220a 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -6405,6 +6406,75 @@ bnx2_reset_task(struct work_struct *work) rtnl_unlock(); } +#define BNX2_FTQ_ENTRY(ftq) { __stringify(ftq##FTQ_CTL), BNX2_##ftq##FTQ_CTL } + +static void +bnx2_dump_ftq(struct bnx2 *bp) +{ + int i; + u32 reg, bdidx, cid, valid; + struct net_device *dev = bp->dev; + static const struct ftq_reg { + char *name; + u32 off; + } ftq_arr[] = { + BNX2_FTQ_ENTRY(RV2P_P), + BNX2_FTQ_ENTRY(RV2P_T), + BNX2_FTQ_ENTRY(RV2P_M), + BNX2_FTQ_ENTRY(TBDR_), + BNX2_FTQ_ENTRY(TDMA_), + BNX2_FTQ_ENTRY(TXP_), + BNX2_FTQ_ENTRY(TXP_), + BNX2_FTQ_ENTRY(TPAT_), + BNX2_FTQ_ENTRY(RXP_C), + BNX2_FTQ_ENTRY(RXP_), + BNX2_FTQ_ENTRY(COM_COMXQ_), + BNX2_FTQ_ENTRY(COM_COMTQ_), + BNX2_FTQ_ENTRY(COM_COMQ_), + BNX2_FTQ_ENTRY(CP_CPQ_), + }; + + netdev_err(dev, "<--- start FTQ dump --->\n"); + for (i = 0; i < ARRAY_SIZE(ftq_arr); i++) + netdev_err(dev, "%s %08x\n", ftq_arr[i].name, + bnx2_reg_rd_ind(bp, ftq_arr[i].off)); + + netdev_err(dev, "CPU states:\n"); + for (reg = BNX2_TXP_CPU_MODE; reg <= BNX2_CP_CPU_MODE; reg += 0x40000) + netdev_err(dev, "%06x mode %x state %x evt_mask %x pc %x pc %x instr %x\n", + reg, bnx2_reg_rd_ind(bp, reg), + bnx2_reg_rd_ind(bp, reg + 4), + bnx2_reg_rd_ind(bp, reg + 8), + bnx2_reg_rd_ind(bp, reg + 0x1c), + bnx2_reg_rd_ind(bp, reg + 0x1c), + bnx2_reg_rd_ind(bp, reg + 0x20)); + + netdev_err(dev, "<--- end FTQ dump --->\n"); + netdev_err(dev, "<--- start TBDC dump --->\n"); + netdev_err(dev, "TBDC free cnt: %ld\n", + REG_RD(bp, BNX2_TBDC_STATUS) & BNX2_TBDC_STATUS_FREE_CNT); + netdev_err(dev, "LINE CID BIDX CMD VALIDS\n"); + for (i = 0; i < 0x20; i++) { + int j = 0; + + REG_WR(bp, BNX2_TBDC_BD_ADDR, i); + REG_WR(bp, BNX2_TBDC_CAM_OPCODE, + BNX2_TBDC_CAM_OPCODE_OPCODE_CAM_READ); + REG_WR(bp, BNX2_TBDC_COMMAND, BNX2_TBDC_COMMAND_CMD_REG_ARB); + while ((REG_RD(bp, BNX2_TBDC_COMMAND) & + BNX2_TBDC_COMMAND_CMD_REG_ARB) && j < 100) + j++; + + cid = REG_RD(bp, BNX2_TBDC_CID); + bdidx = REG_RD(bp, BNX2_TBDC_BIDX); + valid = REG_RD(bp, BNX2_TBDC_CAM_OPCODE); + netdev_err(dev, "%02x %06x %04lx %02x [%x]\n", + i, cid, bdidx & BNX2_TBDC_BDIDX_BDIDX, + bdidx >> 24, (valid >> 8) & 0x0ff); + } + netdev_err(dev, "<--- end TBDC dump --->\n"); +} + static void bnx2_dump_state(struct bnx2 *bp) { @@ -6434,6 +6504,7 @@ bnx2_tx_timeout(struct net_device *dev) { struct bnx2 *bp = netdev_priv(dev); + bnx2_dump_ftq(bp); bnx2_dump_state(bp); bnx2_dump_mcp_state(bp); diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index dc06bda73be7..29975857842e 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -4642,6 +4642,47 @@ struct l2_fhdr { #define BNX2_TBDR_FTQ_CTL_CUR_DEPTH (0x3ffL<<22) +/* + * tbdc definition + * offset: 0x5400 + */ +#define BNX2_TBDC_COMMAND 0x5400 +#define BNX2_TBDC_COMMAND_CMD_ENABLED (1UL<<0) +#define BNX2_TBDC_COMMAND_CMD_FLUSH (1UL<<1) +#define BNX2_TBDC_COMMAND_CMD_SOFT_RST (1UL<<2) +#define BNX2_TBDC_COMMAND_CMD_REG_ARB (1UL<<3) +#define BNX2_TBDC_COMMAND_WRCHK_RANGE_ERROR (1UL<<4) +#define BNX2_TBDC_COMMAND_WRCHK_ALL_ONES_ERROR (1UL<<5) +#define BNX2_TBDC_COMMAND_WRCHK_ALL_ZEROS_ERROR (1UL<<6) +#define BNX2_TBDC_COMMAND_WRCHK_ANY_ONES_ERROR (1UL<<7) +#define BNX2_TBDC_COMMAND_WRCHK_ANY_ZEROS_ERROR (1UL<<8) + +#define BNX2_TBDC_STATUS 0x5404 +#define BNX2_TBDC_STATUS_FREE_CNT (0x3fUL<<0) + +#define BNX2_TBDC_BD_ADDR 0x5424 + +#define BNX2_TBDC_BIDX 0x542c +#define BNX2_TBDC_BDIDX_BDIDX (0xffffUL<<0) +#define BNX2_TBDC_BDIDX_CMD (0xffUL<<24) + +#define BNX2_TBDC_CID 0x5430 + +#define BNX2_TBDC_CAM_OPCODE 0x5434 +#define BNX2_TBDC_CAM_OPCODE_OPCODE (0x7UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_SEARCH (0UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_CACHE_WRITE (1UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_INVALIDATE (2UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_CAM_WRITE (4UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_CAM_READ (5UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_RAM_WRITE (6UL<<0) +#define BNX2_TBDC_CAM_OPCODE_OPCODE_RAM_READ (7UL<<0) +#define BNX2_TBDC_CAM_OPCODE_SMASK_BDIDX (1UL<<4) +#define BNX2_TBDC_CAM_OPCODE_SMASK_CID (1UL<<5) +#define BNX2_TBDC_CAM_OPCODE_SMASK_CMD (1UL<<6) +#define BNX2_TBDC_CAM_OPCODE_WMT_FAILED (1UL<<7) +#define BNX2_TBDC_CAM_OPCODE_CAM_VALIDS (0xffUL<<8) + /* * tdma_reg definition -- cgit v1.2.3 From 13e63517f4bc425a17888e6497c59a9663c2b520 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 16 Jun 2012 15:45:42 +0000 Subject: bnx2: Dump additional BC_STATE during firmware sync timeout. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 1 + drivers/net/ethernet/broadcom/bnx2.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 8eaab0ce220a..da88de73e899 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -2473,6 +2473,7 @@ bnx2_dump_mcp_state(struct bnx2 *bp) bnx2_shmem_rd(bp, BNX2_BC_STATE_RESET_TYPE)); pr_cont(" condition[%08x]\n", bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION)); + DP_SHMEM_LINE(bp, BNX2_BC_RESET_TYPE); DP_SHMEM_LINE(bp, 0x3cc); DP_SHMEM_LINE(bp, 0x3dc); DP_SHMEM_LINE(bp, 0x3ec); diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index 29975857842e..f9cb639a4787 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -7355,6 +7355,8 @@ struct bnx2_rv2p_fw_file { #define BNX2_BC_STATE_RESET_TYPE_VALUE(msg) (BNX2_BC_STATE_RESET_TYPE_SIG | \ (msg)) +#define BNX2_BC_RESET_TYPE 0x000001c0 + #define BNX2_BC_STATE 0x000001c4 #define BNX2_BC_STATE_ERR_MASK 0x0000ff00 #define BNX2_BC_STATE_SIGN 0x42530000 -- cgit v1.2.3 From aefd90e41476223c95e3c84c7dd22a65a21b1e40 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 16 Jun 2012 15:45:43 +0000 Subject: bnx2: Read PCI function number from internal register so that it will work on any hypervisor. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 7 +++++-- drivers/net/ethernet/broadcom/bnx2.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index da88de73e899..c6d39438cbe3 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -7903,7 +7903,7 @@ bnx2_get_5709_media(struct bnx2 *bp) else strap = (val & BNX2_MISC_DUAL_MEDIA_CTRL_PHY_CTRL_STRAP) >> 8; - if (PCI_FUNC(bp->pdev->devfn) == 0) { + if (bp->func == 0) { switch (strap) { case 0x4: case 0x5: @@ -8202,9 +8202,12 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) reg = bnx2_reg_rd_ind(bp, BNX2_SHM_HDR_SIGNATURE); + if (bnx2_reg_rd_ind(bp, BNX2_MCP_TOE_ID) & BNX2_MCP_TOE_ID_FUNCTION_ID) + bp->func = 1; + if ((reg & BNX2_SHM_HDR_SIGNATURE_SIG_MASK) == BNX2_SHM_HDR_SIGNATURE_SIG) { - u32 off = PCI_FUNC(pdev->devfn) << 2; + u32 off = bp->func << 2; bp->shmem_base = bnx2_reg_rd_ind(bp, BNX2_SHM_HDR_ADDR_0 + off); } else diff --git a/drivers/net/ethernet/broadcom/bnx2.h b/drivers/net/ethernet/broadcom/bnx2.h index f9cb639a4787..af6451dec295 100644 --- a/drivers/net/ethernet/broadcom/bnx2.h +++ b/drivers/net/ethernet/broadcom/bnx2.h @@ -6971,6 +6971,8 @@ struct bnx2 { struct bnx2_irq irq_tbl[BNX2_MAX_MSIX_VEC]; int irq_nvecs; + u8 func; + u8 num_tx_rings; u8 num_rx_rings; -- cgit v1.2.3 From 260762c7d8030055a157b6f4ea7d1805a09e59e8 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Sat, 16 Jun 2012 15:45:44 +0000 Subject: bnx2: Update version 2.2.2 Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index c6d39438cbe3..9b69a628641c 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -58,8 +58,8 @@ #include "bnx2_fw.h" #define DRV_MODULE_NAME "bnx2" -#define DRV_MODULE_VERSION "2.2.1" -#define DRV_MODULE_RELDATE "Dec 18, 2011" +#define DRV_MODULE_VERSION "2.2.2" +#define DRV_MODULE_RELDATE "June 16, 2012" #define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-6.2.3.fw" #define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-6.0.15.fw" #define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-6.2.1b.fw" -- cgit v1.2.3 From d8493d210b69b2965236a8a02f5f6e2835ad5e30 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 15 Jun 2012 19:09:09 +0100 Subject: regulator: core: Actually use the data in _notifier_call_chain() Reported-by: Pankaj Jangra Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 3cbe3129ed3b..52a9bae10393 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2657,7 +2657,7 @@ static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { /* call rdev chain first */ - blocking_notifier_call_chain(&rdev->notifier, event, NULL); + blocking_notifier_call_chain(&rdev->notifier, event, data); } /** -- cgit v1.2.3 From 2f7baf89d3e4ed787989168cf31f2fdc04067586 Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Fri, 15 Jun 2012 11:27:36 -0700 Subject: regulator: core.c Pass voltage to notifier when setting voltage The voltage being set should be passed to the call in handler requesting the callback. Currently this is not done. The calling handler cannot call regulator_get_voltage() to get the information since the mutex is held by the regulator and deadlock occurs. Without this change the receiver of the call in cannot know what action to take. This is used, for example, to set PAD voltages when doing SD vccq voltage changes. [Review and spelling fix in the commit log from Pankaj Jangra] Signed-off-by: Philip Rakity Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 52a9bae10393..ebdd9e0e14d4 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2142,7 +2142,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, if (rdev->desc->ops->list_voltage) best_val = rdev->desc->ops->list_voltage(rdev, selector); else - best_val = -1; + best_val = _regulator_get_voltage(rdev); /* Call set_voltage_time_sel if successfully obtained old_selector */ if (_regulator_is_enabled(rdev) && ret == 0 && old_selector >= 0 && @@ -2165,9 +2165,9 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, udelay(delay); } - if (ret == 0) + if (ret == 0 && best_val >= 0) _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, - NULL); + (void *)best_val); trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val); -- cgit v1.2.3 From 8b96de31b0cf190fb6b21c4ab1ce310c430b72ae Mon Sep 17 00:00:00 2001 From: Philip Rakity Date: Thu, 14 Jun 2012 15:07:56 -0700 Subject: regulator: core.c Only delay when setting voltage requires this minor optimization: move delay code to where delay is set and thus where it is used vs in the main line path. Signed-off-by: Philip Rakity Acked-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ebdd9e0e14d4..663ebd534ef9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2155,14 +2155,14 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, delay); delay = 0; } - } - /* Insert any necessary delays */ - if (delay >= 1000) { - mdelay(delay / 1000); - udelay(delay % 1000); - } else if (delay) { - udelay(delay); + /* Insert any necessary delays */ + if (delay >= 1000) { + mdelay(delay / 1000); + udelay(delay % 1000); + } else if (delay) { + udelay(delay); + } } if (ret == 0 && best_val >= 0) -- cgit v1.2.3 From a967fbfaca0a979fc34c1097b37d824039a709b1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jun 2012 20:08:30 +0800 Subject: regulator: wm8350: Use wm8350_ldo_list_voltage instead of open code to verify selected voltage Call wm8350_ldo_list_voltage() instead of open code to verify selected voltage falls within specified range. Use wm8350_ldo_list_voltage() here is less error prone. wm8350_ldo_val_to_mvolts() is only used in wm8350_ldo_list_voltage now, so remove it and move the implementation to wm8350_ldo_list_voltage(). This patch also include below small changes in wm8350_ldo_map_voltage: 1. wm8350_ldo_map_voltage() returns selector, thus rename variable mV to sel. 2. Use DIV_ROUND_UP macro to calculate selector. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 52 +++++++++++++++--------------------- 1 file changed, 21 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 53bf3df02975..c4913be3e1b4 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -108,15 +108,6 @@ static int get_isink_val(int min_uA, int max_uA, u16 *setting) return -EINVAL; } -static inline int wm8350_ldo_val_to_mvolts(unsigned int val) -{ - if (val < 16) - return (val * 50) + 900; - else - return ((val - 16) * 100) + 1800; - -} - static inline unsigned int wm8350_ldo_mvolts_to_val(int mV) { if (mV < 1800) @@ -671,10 +662,22 @@ static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) return 0; } +static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector > WM8350_LDO1_VSEL_MASK) + return -EINVAL; + + if (selector < 16) + return (selector * 50000) + 900000; + else + return ((selector - 16) * 100000) + 1800000; +} + static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { - int mV; + int volt, sel; int min_mV = min_uV / 1000; int max_mV = max_uV / 1000; @@ -683,29 +686,16 @@ static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, if (max_mV < 900 || max_mV > 3300) return -EINVAL; - if (min_mV < 1800) { - /* step size is 50mV < 1800mV */ - mV = (min_mV - 851) / 50; - if (wm8350_ldo_val_to_mvolts(mV) > max_mV) - return -EINVAL; - BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); - } else { - /* step size is 100mV > 1800mV */ - mV = ((min_mV - 1701) / 100) + 16; - if (wm8350_ldo_val_to_mvolts(mV) > max_mV) - return -EINVAL; - BUG_ON(wm8350_ldo_val_to_mvolts(mV) < min_mV); - } - - return mV; -} + if (min_mV < 1800) /* step size is 50mV < 1800mV */ + sel = DIV_ROUND_UP(min_uV - 900, 50); + else /* step size is 100mV > 1800mV */ + sel = DIV_ROUND_UP(min_uV - 1800, 100) + 16; -static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - if (selector > WM8350_LDO1_VSEL_MASK) + volt = wm8350_ldo_list_voltage(rdev, sel); + if (volt < min_uV || volt > max_uV) return -EINVAL; - return wm8350_ldo_val_to_mvolts(selector) * 1000; + + return sel; } int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, -- cgit v1.2.3 From c705742201260afd08b05ff84674507735e2b16b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jun 2012 20:10:26 +0800 Subject: regulator: wm8350: Reuse map_voltage() to get selector of a given uV Reuse map_voltage() to get the selector of a given uV. Then we can remove wm8350_ldo_mvolts_to_val() and wm8350_dcdc_mvolts_to_val(). Also remove unused wm8350_dcdc_val_to_mvolts() function. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 127 ++++++++++++++--------------------- 1 file changed, 50 insertions(+), 77 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index c4913be3e1b4..7f0fa22ef2aa 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -108,24 +108,6 @@ static int get_isink_val(int min_uA, int max_uA, u16 *setting) return -EINVAL; } -static inline unsigned int wm8350_ldo_mvolts_to_val(int mV) -{ - if (mV < 1800) - return (mV - 900) / 50; - else - return ((mV - 1800) / 100) + 16; -} - -static inline int wm8350_dcdc_val_to_mvolts(unsigned int val) -{ - return (val * 25) + 850; -} - -static inline unsigned int wm8350_dcdc_mvolts_to_val(int mV) -{ - return (mV - 850) / 25; -} - static int wm8350_isink_set_current(struct regulator_dev *rdev, int min_uA, int max_uA) { @@ -353,19 +335,10 @@ EXPORT_SYMBOL_GPL(wm8350_isink_set_flash); static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int volt_reg, mV = uV / 1000, dcdc = rdev_get_id(rdev); + int sel, volt_reg, dcdc = rdev_get_id(rdev); u16 val; - dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, mV); - - if (mV && (mV < 850 || mV > 4025)) { - dev_err(wm8350->dev, - "DCDC%d suspend voltage %d mV out of range\n", - dcdc, mV); - return -EINVAL; - } - if (mV == 0) - mV = 850; + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, dcdc, uV / 1000); switch (dcdc) { case WM8350_DCDC_1: @@ -386,10 +359,13 @@ static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) return -EINVAL; } + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return -EINVAL; + /* all DCDCs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_DC1_VSEL_MASK; - wm8350_reg_write(wm8350, volt_reg, - val | wm8350_dcdc_mvolts_to_val(mV)); + wm8350_reg_write(wm8350, volt_reg, val | sel); return 0; } @@ -566,19 +542,49 @@ static int wm8350_dcdc_set_suspend_mode(struct regulator_dev *rdev, return 0; } +static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector > WM8350_LDO1_VSEL_MASK) + return -EINVAL; + + if (selector < 16) + return (selector * 50000) + 900000; + else + return ((selector - 16) * 100000) + 1800000; +} + +static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + int volt, sel; + int min_mV = min_uV / 1000; + int max_mV = max_uV / 1000; + + if (min_mV < 900 || min_mV > 3300) + return -EINVAL; + if (max_mV < 900 || max_mV > 3300) + return -EINVAL; + + if (min_mV < 1800) /* step size is 50mV < 1800mV */ + sel = DIV_ROUND_UP(min_uV - 900, 50); + else /* step size is 100mV > 1800mV */ + sel = DIV_ROUND_UP(min_uV - 1800, 100) + 16; + + volt = wm8350_ldo_list_voltage(rdev, sel); + if (volt < min_uV || volt > max_uV) + return -EINVAL; + + return sel; +} + static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); - int volt_reg, mV = uV / 1000, ldo = rdev_get_id(rdev); + int sel, volt_reg, ldo = rdev_get_id(rdev); u16 val; - dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, mV); - - if (mV < 900 || mV > 3300) { - dev_err(wm8350->dev, "LDO%d voltage %d mV out of range\n", - ldo, mV); - return -EINVAL; - } + dev_dbg(wm8350->dev, "%s %d mV %d\n", __func__, ldo, uV / 1000); switch (ldo) { case WM8350_LDO_1: @@ -597,10 +603,13 @@ static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) return -EINVAL; } + sel = wm8350_ldo_map_voltage(rdev, uV, uV); + if (sel < 0) + return -EINVAL; + /* all LDOs have same mV bits */ val = wm8350_reg_read(wm8350, volt_reg) & ~WM8350_LDO1_VSEL_MASK; - wm8350_reg_write(wm8350, volt_reg, - val | wm8350_ldo_mvolts_to_val(mV)); + wm8350_reg_write(wm8350, volt_reg, val | sel); return 0; } @@ -662,42 +671,6 @@ static int wm8350_ldo_set_suspend_disable(struct regulator_dev *rdev) return 0; } -static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - if (selector > WM8350_LDO1_VSEL_MASK) - return -EINVAL; - - if (selector < 16) - return (selector * 50000) + 900000; - else - return ((selector - 16) * 100000) + 1800000; -} - -static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV, - int max_uV) -{ - int volt, sel; - int min_mV = min_uV / 1000; - int max_mV = max_uV / 1000; - - if (min_mV < 900 || min_mV > 3300) - return -EINVAL; - if (max_mV < 900 || max_mV > 3300) - return -EINVAL; - - if (min_mV < 1800) /* step size is 50mV < 1800mV */ - sel = DIV_ROUND_UP(min_uV - 900, 50); - else /* step size is 100mV > 1800mV */ - sel = DIV_ROUND_UP(min_uV - 1800, 100) + 16; - - volt = wm8350_ldo_list_voltage(rdev, sel); - if (volt < min_uV || volt > max_uV) - return -EINVAL; - - return sel; -} - int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, u16 stop, u16 fault) { -- cgit v1.2.3 From ec0ab07587f0d2af208366158f5fd487311f1c56 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 14 Jun 2012 10:22:59 +0800 Subject: regulator: tps65023: Set n_voltages to 1 for fixed voltage For fixed voltage, the n_voltages should be 1 rather than 0. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65023-regulator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index f841bd0db6aa..df8b70d76f9e 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -324,7 +324,10 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->desc[i].name = info->name; tps->desc[i].id = i; - tps->desc[i].n_voltages = info->table_len; + if (info->fixed) + tps->desc[i].n_voltages = 1; + else + tps->desc[i].n_voltages = info->table_len; tps->desc[i].ops = (i > TPS65023_DCDC_3 ? &tps65023_ldo_ops : &tps65023_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; -- cgit v1.2.3 From c70ad9dcf08c839b018d8c440bd8c19aabbb08a8 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 20:27:14 +0800 Subject: regulator: wm831x-dcdc: Convert wm831x_buckp_ops to regulator_list_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm831x-dcdc.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 099da11e989f..fbcff56e0114 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -599,15 +599,6 @@ static struct platform_driver wm831x_buckv_driver = { * BUCKP specifics */ -static int wm831x_buckp_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - if (selector <= WM831X_BUCKP_MAX_SELECTOR) - return 850000 + (selector * 25000); - else - return -EINVAL; -} - static int wm831x_buckp_set_voltage_int(struct regulator_dev *rdev, int reg, int min_uV, int max_uV, int *selector) { @@ -620,7 +611,7 @@ static int wm831x_buckp_set_voltage_int(struct regulator_dev *rdev, int reg, else return -EINVAL; - if (wm831x_buckp_list_voltage(rdev, vsel) > max_uV) + if (regulator_list_voltage_linear(rdev, vsel) > max_uV) return -EINVAL; *selector = vsel; @@ -652,7 +643,7 @@ static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, static struct regulator_ops wm831x_buckp_ops = { .set_voltage = wm831x_buckp_set_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .list_voltage = wm831x_buckp_list_voltage, + .list_voltage = regulator_list_voltage_linear, .set_suspend_voltage = wm831x_buckp_set_suspend_voltage, .is_enabled = regulator_is_enabled_regmap, @@ -715,6 +706,8 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev) dcdc->desc.vsel_mask = WM831X_DC3_ON_VSEL_MASK; dcdc->desc.enable_reg = WM831X_DCDC_ENABLE; dcdc->desc.enable_mask = 1 << id; + dcdc->desc.min_uV = 850000; + dcdc->desc.uV_step = 25000; config.dev = pdev->dev.parent; if (pdata) -- cgit v1.2.3 From e08ef739feec5341fe9382a9f5173ddd56c95f93 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 14:35:32 +0800 Subject: regulator: tps62360: Simplify tps62360_set_voltage_time_sel implementation For linear mappings, we can use below equation to get the voltage difference between new_selector and old_selector: abs(new_selector - old_selector) * rdev->desc->uV_step Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps62360-regulator.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index e534269ed44a..d044a58640e7 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -179,17 +179,10 @@ static int tps62360_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { struct tps62360_chip *tps = rdev_get_drvdata(rdev); - int old_uV, new_uV; - old_uV = regulator_list_voltage_linear(rdev, old_selector); - if (old_uV < 0) - return old_uV; - - new_uV = regulator_list_voltage_linear(rdev, new_selector); - if (new_uV < 0) - return new_uV; - - return DIV_ROUND_UP(abs(old_uV - new_uV), tps->change_uv_per_us); + return DIV_ROUND_UP(abs(new_selector - old_selector) * + rdev->desc->uV_step, + tps->change_uv_per_us); } static int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode) -- cgit v1.2.3 From ee5e6253ac22210da892435711890fa2272daa41 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 15:39:34 +0800 Subject: regulator: mc13xxx: Fix voltage checking for mc13xxx_fixed_regulator_set_voltage The voltage range checking should be to ensure mc13xxx_regulators[id].voltages[0] falls with min_uV and max_uV. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/mc13xxx-regulator-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 4fa9704739bc..3e191715fb25 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -158,8 +158,8 @@ int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n", __func__, id, min_uV, max_uV); - if (min_uV >= mc13xxx_regulators[id].voltages[0] && - max_uV <= mc13xxx_regulators[id].voltages[0]) + if (min_uV <= mc13xxx_regulators[id].voltages[0] && + mc13xxx_regulators[id].voltages[0] <= max_uV) return 0; else return -EINVAL; -- cgit v1.2.3 From 34e74f39fa9a2c1489444266cc9e973dc1b3a419 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 15:41:48 +0800 Subject: regulator: mc13xxx: Convert to regulator_list_voltage_table Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/mc13783-regulator.c | 38 +++++++++++++-------------- drivers/regulator/mc13892-regulator.c | 42 +++++++++++++++--------------- drivers/regulator/mc13xxx-regulator-core.c | 30 +++++---------------- drivers/regulator/mc13xxx.h | 9 +++---- 4 files changed, 49 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 7dcdfa283e93..4932e3449fe1 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -93,78 +93,78 @@ /* Voltage Values */ -static const int mc13783_sw3_val[] = { +static const unsigned int mc13783_sw3_val[] = { 5000000, 5000000, 5000000, 5500000, }; -static const int mc13783_vaudio_val[] = { +static const unsigned int mc13783_vaudio_val[] = { 2775000, }; -static const int mc13783_viohi_val[] = { +static const unsigned int mc13783_viohi_val[] = { 2775000, }; -static const int mc13783_violo_val[] = { +static const unsigned int mc13783_violo_val[] = { 1200000, 1300000, 1500000, 1800000, }; -static const int mc13783_vdig_val[] = { +static const unsigned int mc13783_vdig_val[] = { 1200000, 1300000, 1500000, 1800000, }; -static const int mc13783_vgen_val[] = { +static const unsigned int mc13783_vgen_val[] = { 1200000, 1300000, 1500000, 1800000, 1100000, 2000000, 2775000, 2400000, }; -static const int mc13783_vrfdig_val[] = { +static const unsigned int mc13783_vrfdig_val[] = { 1200000, 1500000, 1800000, 1875000, }; -static const int mc13783_vrfref_val[] = { +static const unsigned int mc13783_vrfref_val[] = { 2475000, 2600000, 2700000, 2775000, }; -static const int mc13783_vrfcp_val[] = { +static const unsigned int mc13783_vrfcp_val[] = { 2700000, 2775000, }; -static const int mc13783_vsim_val[] = { +static const unsigned int mc13783_vsim_val[] = { 1800000, 2900000, 3000000, }; -static const int mc13783_vesim_val[] = { +static const unsigned int mc13783_vesim_val[] = { 1800000, 2900000, }; -static const int mc13783_vcam_val[] = { +static const unsigned int mc13783_vcam_val[] = { 1500000, 1800000, 2500000, 2550000, 2600000, 2750000, 2800000, 3000000, }; -static const int mc13783_vrfbg_val[] = { +static const unsigned int mc13783_vrfbg_val[] = { 1250000, }; -static const int mc13783_vvib_val[] = { +static const unsigned int mc13783_vvib_val[] = { 1300000, 1800000, 2000000, 3000000, }; -static const int mc13783_vmmc_val[] = { +static const unsigned int mc13783_vmmc_val[] = { 1600000, 1800000, 2000000, 2600000, 2700000, 2800000, 2900000, 3000000, }; -static const int mc13783_vrf_val[] = { +static const unsigned int mc13783_vrf_val[] = { 1500000, 1875000, 2700000, 2775000, }; -static const int mc13783_gpo_val[] = { +static const unsigned int mc13783_gpo_val[] = { 3100000, }; -static const int mc13783_pwgtdrv_val[] = { +static const unsigned int mc13783_pwgtdrv_val[] = { 5500000, }; @@ -328,7 +328,7 @@ static struct regulator_ops mc13783_gpo_regulator_ops = { .enable = mc13783_gpo_regulator_enable, .disable = mc13783_gpo_regulator_disable, .is_enabled = mc13783_gpo_regulator_is_enabled, - .list_voltage = mc13xxx_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .set_voltage = mc13xxx_fixed_regulator_set_voltage, .get_voltage = mc13xxx_fixed_regulator_get_voltage, }; diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index 970a233dbe46..6329723b5372 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -150,12 +150,12 @@ #define MC13892_USB1 50 #define MC13892_USB1_VUSBEN (1<<3) -static const int mc13892_vcoincell[] = { +static const unsigned int mc13892_vcoincell[] = { 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, }; -static const int mc13892_sw1[] = { +static const unsigned int mc13892_sw1[] = { 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, 1000000, 1025000, @@ -164,7 +164,7 @@ static const int mc13892_sw1[] = { 1350000, 1375000 }; -static const int mc13892_sw[] = { +static const unsigned int mc13892_sw[] = { 600000, 625000, 650000, 675000, 700000, 725000, 750000, 775000, 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, 1000000, 1025000, @@ -176,65 +176,65 @@ static const int mc13892_sw[] = { 1800000, 1825000, 1850000, 1875000 }; -static const int mc13892_swbst[] = { +static const unsigned int mc13892_swbst[] = { 5000000, }; -static const int mc13892_viohi[] = { +static const unsigned int mc13892_viohi[] = { 2775000, }; -static const int mc13892_vpll[] = { +static const unsigned int mc13892_vpll[] = { 1050000, 1250000, 1650000, 1800000, }; -static const int mc13892_vdig[] = { +static const unsigned int mc13892_vdig[] = { 1050000, 1250000, 1650000, 1800000, }; -static const int mc13892_vsd[] = { +static const unsigned int mc13892_vsd[] = { 1800000, 2000000, 2600000, 2700000, 2800000, 2900000, 3000000, 3150000, }; -static const int mc13892_vusb2[] = { +static const unsigned int mc13892_vusb2[] = { 2400000, 2600000, 2700000, 2775000, }; -static const int mc13892_vvideo[] = { +static const unsigned int mc13892_vvideo[] = { 2700000, 2775000, 2500000, 2600000, }; -static const int mc13892_vaudio[] = { +static const unsigned int mc13892_vaudio[] = { 2300000, 2500000, 2775000, 3000000, }; -static const int mc13892_vcam[] = { +static const unsigned int mc13892_vcam[] = { 2500000, 2600000, 2750000, 3000000, }; -static const int mc13892_vgen1[] = { +static const unsigned int mc13892_vgen1[] = { 1200000, 1500000, 2775000, 3150000, }; -static const int mc13892_vgen2[] = { +static const unsigned int mc13892_vgen2[] = { 1200000, 1500000, 1600000, 1800000, 2700000, 2800000, 3000000, 3150000, }; -static const int mc13892_vgen3[] = { +static const unsigned int mc13892_vgen3[] = { 1800000, 2900000, }; -static const int mc13892_vusb[] = { +static const unsigned int mc13892_vusb[] = { 3300000, }; -static const int mc13892_gpo[] = { +static const unsigned int mc13892_gpo[] = { 2750000, }; -static const int mc13892_pwgtdrv[] = { +static const unsigned int mc13892_pwgtdrv[] = { 5000000, }; @@ -394,7 +394,7 @@ static struct regulator_ops mc13892_gpo_regulator_ops = { .enable = mc13892_gpo_regulator_enable, .disable = mc13892_gpo_regulator_disable, .is_enabled = mc13892_gpo_regulator_is_enabled, - .list_voltage = mc13xxx_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .set_voltage = mc13xxx_fixed_regulator_set_voltage, .get_voltage = mc13xxx_fixed_regulator_get_voltage, }; @@ -436,7 +436,7 @@ static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev, u32 valread; int ret; - value = mc13892_regulators[id].voltages[selector]; + value = rdev->desc->volt_table[selector]; mc13xxx_lock(priv->mc13xxx); ret = mc13xxx_reg_read(priv->mc13xxx, @@ -470,7 +470,7 @@ err: static struct regulator_ops mc13892_sw_regulator_ops = { .is_enabled = mc13xxx_sw_regulator_is_enabled, - .list_voltage = mc13xxx_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .set_voltage_sel = mc13892_sw_regulator_set_voltage_sel, .get_voltage = mc13892_sw_regulator_get_voltage, }; diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 3e191715fb25..8a6b050ff5aa 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -80,20 +80,6 @@ static int mc13xxx_regulator_is_enabled(struct regulator_dev *rdev) return (val & mc13xxx_regulators[id].enable_bit) != 0; } -int mc13xxx_regulator_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - int id = rdev_get_id(rdev); - struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); - struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; - - if (selector >= mc13xxx_regulators[id].desc.n_voltages) - return -EINVAL; - - return mc13xxx_regulators[id].voltages[selector]; -} -EXPORT_SYMBOL_GPL(mc13xxx_regulator_list_voltage); - static int mc13xxx_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { @@ -135,14 +121,14 @@ static int mc13xxx_regulator_get_voltage(struct regulator_dev *rdev) BUG_ON(val >= mc13xxx_regulators[id].desc.n_voltages); - return mc13xxx_regulators[id].voltages[val]; + return rdev->desc->volt_table[val]; } struct regulator_ops mc13xxx_regulator_ops = { .enable = mc13xxx_regulator_enable, .disable = mc13xxx_regulator_disable, .is_enabled = mc13xxx_regulator_is_enabled, - .list_voltage = mc13xxx_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .set_voltage_sel = mc13xxx_regulator_set_voltage_sel, .get_voltage = mc13xxx_regulator_get_voltage, }; @@ -151,15 +137,13 @@ EXPORT_SYMBOL_GPL(mc13xxx_regulator_ops); int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { - struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); - struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; int id = rdev_get_id(rdev); dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n", __func__, id, min_uV, max_uV); - if (min_uV <= mc13xxx_regulators[id].voltages[0] && - mc13xxx_regulators[id].voltages[0] <= max_uV) + if (min_uV <= rdev->desc->volt_table[0] && + rdev->desc->volt_table[0] <= max_uV) return 0; else return -EINVAL; @@ -168,13 +152,11 @@ EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage); int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev) { - struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev); - struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators; int id = rdev_get_id(rdev); dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); - return mc13xxx_regulators[id].voltages[0]; + return rdev->desc->volt_table[0]; } EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_get_voltage); @@ -182,7 +164,7 @@ struct regulator_ops mc13xxx_fixed_regulator_ops = { .enable = mc13xxx_regulator_enable, .disable = mc13xxx_regulator_disable, .is_enabled = mc13xxx_regulator_is_enabled, - .list_voltage = mc13xxx_regulator_list_voltage, + .list_voltage = regulator_list_voltage_table, .set_voltage = mc13xxx_fixed_regulator_set_voltage, .get_voltage = mc13xxx_fixed_regulator_get_voltage, }; diff --git a/drivers/regulator/mc13xxx.h b/drivers/regulator/mc13xxx.h index 044aba4d28ec..9a8f72ce718e 100644 --- a/drivers/regulator/mc13xxx.h +++ b/drivers/regulator/mc13xxx.h @@ -22,7 +22,6 @@ struct mc13xxx_regulator { int vsel_shift; int vsel_mask; int hi_bit; - int const *voltages; }; struct mc13xxx_regulator_priv { @@ -35,8 +34,6 @@ struct mc13xxx_regulator_priv { extern int mc13xxx_sw_regulator(struct regulator_dev *rdev); extern int mc13xxx_sw_regulator_is_enabled(struct regulator_dev *rdev); -extern int mc13xxx_regulator_list_voltage(struct regulator_dev *rdev, - unsigned selector); extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector); extern int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev); @@ -68,6 +65,7 @@ extern struct regulator_ops mc13xxx_fixed_regulator_ops; .desc = { \ .name = #_name, \ .n_voltages = ARRAY_SIZE(_voltages), \ + .volt_table = _voltages, \ .ops = &_ops, \ .type = REGULATOR_VOLTAGE, \ .id = prefix ## _name, \ @@ -78,7 +76,6 @@ extern struct regulator_ops mc13xxx_fixed_regulator_ops; .vsel_reg = prefix ## _vsel_reg, \ .vsel_shift = prefix ## _vsel_reg ## _ ## _name ## VSEL,\ .vsel_mask = prefix ## _vsel_reg ## _ ## _name ## VSEL_M,\ - .voltages = _voltages, \ } #define MC13xxx_FIXED_DEFINE(prefix, _name, _reg, _voltages, _ops) \ @@ -86,6 +83,7 @@ extern struct regulator_ops mc13xxx_fixed_regulator_ops; .desc = { \ .name = #_name, \ .n_voltages = ARRAY_SIZE(_voltages), \ + .volt_table = _voltages, \ .ops = &_ops, \ .type = REGULATOR_VOLTAGE, \ .id = prefix ## _name, \ @@ -93,7 +91,6 @@ extern struct regulator_ops mc13xxx_fixed_regulator_ops; }, \ .reg = prefix ## _reg, \ .enable_bit = prefix ## _reg ## _ ## _name ## EN, \ - .voltages = _voltages, \ } #define MC13xxx_GPO_DEFINE(prefix, _name, _reg, _voltages, _ops) \ @@ -101,6 +98,7 @@ extern struct regulator_ops mc13xxx_fixed_regulator_ops; .desc = { \ .name = #_name, \ .n_voltages = ARRAY_SIZE(_voltages), \ + .volt_table = _voltages, \ .ops = &_ops, \ .type = REGULATOR_VOLTAGE, \ .id = prefix ## _name, \ @@ -108,7 +106,6 @@ extern struct regulator_ops mc13xxx_fixed_regulator_ops; }, \ .reg = prefix ## _reg, \ .enable_bit = prefix ## _reg ## _ ## _name ## EN, \ - .voltages = _voltages, \ } #define MC13xxx_DEFINE_SW(_name, _reg, _vsel_reg, _voltages, ops) \ -- cgit v1.2.3 From 7142e2138b088da429d94859df0ed05b1b82607c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 10:27:49 +0800 Subject: regulator: ab8500: Use regulator_list_voltage_linear for ab8500_regulator_fixed_ops Also removes set_voltage_time_sel callback from ab8500_regulator_fixed_ops because the voltage won't change. ( And ab8500_regulator_fixed_ops does not implement get_voltage_sel so set_voltage_time_sel won't be called ) Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 41 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 290c289a653d..c931c7c0ad1b 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -32,7 +32,6 @@ * @regulator_dev: regulator device * @max_uV: maximum voltage (for variable voltage supplies) * @min_uV: minimum voltage (for variable voltage supplies) - * @fixed_uV: typical voltage (for fixed voltage supplies) * @update_bank: bank to control on/off * @update_reg: register to control on/off * @update_mask: mask to enable/disable regulator @@ -48,7 +47,6 @@ struct ab8500_regulator_info { struct regulator_dev *regulator; int max_uV; int min_uV; - int fixed_uV; u8 update_bank; u8 update_reg; u8 update_mask; @@ -271,29 +269,9 @@ static struct regulator_ops ab8500_regulator_ops = { .set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel, }; -static int ab8500_fixed_list_voltage(struct regulator_dev *rdev, - unsigned selector) -{ - struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); - - if (info == NULL) { - dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); - return -EINVAL; - } - - return info->fixed_uV; -} - static int ab8500_fixed_get_voltage(struct regulator_dev *rdev) { - struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); - - if (info == NULL) { - dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); - return -EINVAL; - } - - return info->fixed_uV; + return rdev->desc->min_uV; } static struct regulator_ops ab8500_regulator_fixed_ops = { @@ -301,9 +279,8 @@ static struct regulator_ops ab8500_regulator_fixed_ops = { .disable = ab8500_regulator_disable, .is_enabled = ab8500_regulator_is_enabled, .get_voltage = ab8500_fixed_get_voltage, - .list_voltage = ab8500_fixed_list_voltage, + .list_voltage = regulator_list_voltage_linear, .enable_time = ab8500_regulator_enable_time, - .set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel, }; static struct ab8500_regulator_info @@ -408,9 +385,9 @@ static struct ab8500_regulator_info .id = AB8500_LDO_TVOUT, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 2000000, }, .delay = 10000, - .fixed_uV = 2000000, .update_bank = 0x03, .update_reg = 0x80, .update_mask = 0x82, @@ -424,8 +401,8 @@ static struct ab8500_regulator_info .id = AB8500_LDO_USB, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 3300000, }, - .fixed_uV = 3300000, .update_bank = 0x03, .update_reg = 0x82, .update_mask = 0x03, @@ -439,8 +416,8 @@ static struct ab8500_regulator_info .id = AB8500_LDO_AUDIO, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 2000000, }, - .fixed_uV = 2000000, .update_bank = 0x03, .update_reg = 0x83, .update_mask = 0x02, @@ -454,8 +431,8 @@ static struct ab8500_regulator_info .id = AB8500_LDO_ANAMIC1, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 2050000, }, - .fixed_uV = 2050000, .update_bank = 0x03, .update_reg = 0x83, .update_mask = 0x08, @@ -469,8 +446,8 @@ static struct ab8500_regulator_info .id = AB8500_LDO_ANAMIC2, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 2050000, }, - .fixed_uV = 2050000, .update_bank = 0x03, .update_reg = 0x83, .update_mask = 0x10, @@ -484,8 +461,8 @@ static struct ab8500_regulator_info .id = AB8500_LDO_DMIC, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 1800000, }, - .fixed_uV = 1800000, .update_bank = 0x03, .update_reg = 0x83, .update_mask = 0x04, @@ -499,8 +476,8 @@ static struct ab8500_regulator_info .id = AB8500_LDO_ANA, .owner = THIS_MODULE, .n_voltages = 1, + .min_uV = 1200000, }, - .fixed_uV = 1200000, .update_bank = 0x04, .update_reg = 0x06, .update_mask = 0x0c, -- cgit v1.2.3 From 07b9e329f99397d01e4789d64aa2f581a7c94b03 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 8 Jun 2012 10:29:21 +0800 Subject: regulator: ab8500: Remove min_uV and max_uV from struct ab8500_regulator_info The min_uV and max_uV are not really used in the code and misleading because the min_uV and max_uV settings does not match the value in the voltage table. For example, we have static const unsigned int ldo_vaux3_voltages[] = { 1200000, 1500000, 1800000, 2100000, 2500000, 2750000, 2790000, 2910000, }; With below min_uV/max_uV settings for AB8500_LDO_AUX3. .min_uV = 1100000, .max_uV = 3300000, The min_uV/max_uV for AB8500_LDO_AUX3 seems copy-paste from AB8500_LDO_AUX2 and is wrong. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index c931c7c0ad1b..13d424fc1c14 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -30,8 +30,6 @@ * @dev: device pointer * @desc: regulator description * @regulator_dev: regulator device - * @max_uV: maximum voltage (for variable voltage supplies) - * @min_uV: minimum voltage (for variable voltage supplies) * @update_bank: bank to control on/off * @update_reg: register to control on/off * @update_mask: mask to enable/disable regulator @@ -45,8 +43,6 @@ struct ab8500_regulator_info { struct device *dev; struct regulator_desc desc; struct regulator_dev *regulator; - int max_uV; - int min_uV; u8 update_bank; u8 update_reg; u8 update_mask; @@ -301,8 +297,6 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, }, - .min_uV = 1100000, - .max_uV = 3300000, .update_bank = 0x04, .update_reg = 0x09, .update_mask = 0x03, @@ -321,8 +315,6 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, }, - .min_uV = 1100000, - .max_uV = 3300000, .update_bank = 0x04, .update_reg = 0x09, .update_mask = 0x0c, @@ -341,8 +333,6 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), .volt_table = ldo_vaux3_voltages, }, - .min_uV = 1100000, - .max_uV = 3300000, .update_bank = 0x04, .update_reg = 0x0a, .update_mask = 0x03, @@ -361,8 +351,6 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), .volt_table = ldo_vintcore_voltages, }, - .min_uV = 1100000, - .max_uV = 3300000, .update_bank = 0x03, .update_reg = 0x80, .update_mask = 0x44, -- cgit v1.2.3 From 368a7887698c3e7ec30678853fa7dd1f0720863f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 11 Jun 2012 14:43:57 +0800 Subject: regulator: pcf50633: Convert to regulator_set_voltage_sel_regmap and map_voltage Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/pcf50633-regulator.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 3c9d14c0017b..092e5cb848a1 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -100,13 +100,12 @@ static unsigned int ldo_voltage_value(u8 bits) return 900 + (bits * 100); } -static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, - unsigned *selector) +static int pcf50633_regulator_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) { struct pcf50633 *pcf; int regulator_id, millivolts; - u8 volt_bits, regnr; + u8 volt_bits; pcf = rdev_get_drvdata(rdev); @@ -116,15 +115,11 @@ static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev, millivolts = min_uV / 1000; - regnr = rdev->desc->vsel_reg; - switch (regulator_id) { case PCF50633_REGULATOR_AUTO: volt_bits = auto_voltage_bits(millivolts); break; case PCF50633_REGULATOR_DOWN1: - volt_bits = down_voltage_bits(millivolts); - break; case PCF50633_REGULATOR_DOWN2: volt_bits = down_voltage_bits(millivolts); break; @@ -142,9 +137,7 @@ static int pcf50633_regulator_set_voltage(struct regulator_dev *rdev, return -EINVAL; } - *selector = volt_bits; - - return pcf50633_reg_write(pcf, regnr, volt_bits); + return volt_bits; } static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev, @@ -159,8 +152,6 @@ static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev, millivolts = auto_voltage_value(index); break; case PCF50633_REGULATOR_DOWN1: - millivolts = down_voltage_value(index); - break; case PCF50633_REGULATOR_DOWN2: millivolts = down_voltage_value(index); break; @@ -182,9 +173,10 @@ static int pcf50633_regulator_list_voltage(struct regulator_dev *rdev, } static struct regulator_ops pcf50633_regulator_ops = { - .set_voltage = pcf50633_regulator_set_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = pcf50633_regulator_list_voltage, + .map_voltage = pcf50633_regulator_map_voltage, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, .is_enabled = regulator_is_enabled_regmap, -- cgit v1.2.3 From 1bd1955a1f90ca29a3b8ac657028267c8d9de671 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 11 Jun 2012 10:14:28 +0800 Subject: regulator: ab3100: Convert fixed voltage to use regulator_list_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/ab3100.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index b088b6c228c8..16d420b2f7a8 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -43,15 +43,12 @@ * @dev: handle to the device * @plfdata: AB3100 platform data passed in at probe time * @regreg: regulator register number in the AB3100 - * @fixed_voltage: a fixed voltage for this regulator, if this - * 0 the voltages array is used instead. */ struct ab3100_regulator { struct regulator_dev *rdev; struct device *dev; struct ab3100_platform_data *plfdata; u8 regreg; - int fixed_voltage; }; /* The order in which registers are initialized */ @@ -121,15 +118,12 @@ static struct ab3100_regulator ab3100_regulators[AB3100_NUM_REGULATORS] = { { .regreg = AB3100_LDO_A, - .fixed_voltage = LDO_A_VOLTAGE, }, { .regreg = AB3100_LDO_C, - .fixed_voltage = LDO_C_VOLTAGE, }, { .regreg = AB3100_LDO_D, - .fixed_voltage = LDO_D_VOLTAGE, }, { .regreg = AB3100_LDO_E, @@ -246,10 +240,6 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg) u8 regval; int err; - /* Return the voltage for fixed regulators immediately */ - if (abreg->fixed_voltage) - return abreg->fixed_voltage; - /* * For variable types, read out setting and index into * supplied voltage list. @@ -382,11 +372,17 @@ static int ab3100_enable_time_regulator(struct regulator_dev *reg) return 0; } +static int ab3100_get_fixed_voltage_regulator(struct regulator_dev *reg) +{ + return reg->desc->min_uV; +} + static struct regulator_ops regulator_ops_fixed = { + .list_voltage = regulator_list_voltage_linear, .enable = ab3100_enable_regulator, .disable = ab3100_disable_regulator, .is_enabled = ab3100_is_enabled_regulator, - .get_voltage = ab3100_get_voltage_regulator, + .get_voltage = ab3100_get_fixed_voltage_regulator, .enable_time = ab3100_enable_time_regulator, }; @@ -430,22 +426,28 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .name = "LDO_A", .id = AB3100_LDO_A, .ops = ®ulator_ops_fixed, + .n_voltages = 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .min_uV = LDO_A_VOLTAGE, }, { .name = "LDO_C", .id = AB3100_LDO_C, .ops = ®ulator_ops_fixed, + .n_voltages = 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .min_uV = LDO_C_VOLTAGE, }, { .name = "LDO_D", .id = AB3100_LDO_D, .ops = ®ulator_ops_fixed, + .n_voltages = 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .min_uV = LDO_D_VOLTAGE, }, { .name = "LDO_E", -- cgit v1.2.3 From 0a4796896bda399a63090dad267896ea93d44fa7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 12 Jun 2012 10:46:43 +0800 Subject: regulator: wm831x-ldo: Convert to regulator_set_voltage_sel_regmap and map_voltage Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm831x-ldo.c | 129 ++++++++++++----------------------------- 1 file changed, 38 insertions(+), 91 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index f23d020e9503..04d8a413c260 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -78,13 +78,10 @@ static int wm831x_gp_ldo_list_voltage(struct regulator_dev *rdev, return -EINVAL; } -static int wm831x_gp_ldo_set_voltage_int(struct regulator_dev *rdev, int reg, - int min_uV, int max_uV, - unsigned *selector) +static int wm831x_gp_ldo_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) { - struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - struct wm831x *wm831x = ldo->wm831x; - int vsel, ret; + int volt, vsel; if (min_uV < 900000) vsel = 0; @@ -94,36 +91,25 @@ static int wm831x_gp_ldo_set_voltage_int(struct regulator_dev *rdev, int reg, vsel = ((min_uV - 1700000) / 100000) + WM831X_GP_LDO_SELECTOR_LOW + 1; - ret = wm831x_gp_ldo_list_voltage(rdev, vsel); - if (ret < 0) - return ret; - if (ret < min_uV || ret > max_uV) + volt = wm831x_gp_ldo_list_voltage(rdev, vsel); + if (volt < min_uV || volt > max_uV) return -EINVAL; - *selector = vsel; - - return wm831x_set_bits(wm831x, reg, WM831X_LDO1_ON_VSEL_MASK, vsel); -} - -static int wm831x_gp_ldo_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, - unsigned *selector) -{ - struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - int reg = ldo->base + WM831X_LDO_ON_CONTROL; - - return wm831x_gp_ldo_set_voltage_int(rdev, reg, min_uV, max_uV, - selector); + return vsel; } static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - int reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; - unsigned int selector; + struct wm831x *wm831x = ldo->wm831x; + int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; - return wm831x_gp_ldo_set_voltage_int(rdev, reg, uV, uV, &selector); + sel = wm831x_gp_ldo_map_voltage(rdev, uV, uV); + if (sel < 0) + return sel; + + return wm831x_set_bits(wm831x, reg, WM831X_LDO1_ON_VSEL_MASK, sel); } static unsigned int wm831x_gp_ldo_get_mode(struct regulator_dev *rdev) @@ -243,8 +229,9 @@ static unsigned int wm831x_gp_ldo_get_optimum_mode(struct regulator_dev *rdev, static struct regulator_ops wm831x_gp_ldo_ops = { .list_voltage = wm831x_gp_ldo_list_voltage, + .map_voltage = wm831x_gp_ldo_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage = wm831x_gp_ldo_set_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage, .get_mode = wm831x_gp_ldo_get_mode, .set_mode = wm831x_gp_ldo_set_mode, @@ -384,13 +371,10 @@ static int wm831x_aldo_list_voltage(struct regulator_dev *rdev, return -EINVAL; } -static int wm831x_aldo_set_voltage_int(struct regulator_dev *rdev, int reg, - int min_uV, int max_uV, - unsigned *selector) +static int wm831x_aldo_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) { - struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - struct wm831x *wm831x = ldo->wm831x; - int vsel, ret; + int volt, vsel; if (min_uV < 1000000) vsel = 0; @@ -400,35 +384,26 @@ static int wm831x_aldo_set_voltage_int(struct regulator_dev *rdev, int reg, vsel = ((min_uV - 1700000) / 100000) + WM831X_ALDO_SELECTOR_LOW + 1; - ret = wm831x_aldo_list_voltage(rdev, vsel); - if (ret < 0) - return ret; - if (ret < min_uV || ret > max_uV) + volt = wm831x_aldo_list_voltage(rdev, vsel); + if (volt < min_uV || volt > max_uV) return -EINVAL; - *selector = vsel; - - return wm831x_set_bits(wm831x, reg, WM831X_LDO7_ON_VSEL_MASK, vsel); -} - -static int wm831x_aldo_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) -{ - struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - int reg = ldo->base + WM831X_LDO_ON_CONTROL; + return vsel; - return wm831x_aldo_set_voltage_int(rdev, reg, min_uV, max_uV, - selector); } static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - int reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; - unsigned int selector; + struct wm831x *wm831x = ldo->wm831x; + int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL; + + sel = wm831x_aldo_map_voltage(rdev, uV, uV); + if (sel < 0) + return sel; - return wm831x_aldo_set_voltage_int(rdev, reg, uV, uV, &selector); + return wm831x_set_bits(wm831x, reg, WM831X_LDO7_ON_VSEL_MASK, sel); } static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev) @@ -506,8 +481,9 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev) static struct regulator_ops wm831x_aldo_ops = { .list_voltage = wm831x_aldo_list_voltage, + .map_voltage = wm831x_aldo_map_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage = wm831x_aldo_set_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_suspend_voltage = wm831x_aldo_set_suspend_voltage, .get_mode = wm831x_aldo_get_mode, .set_mode = wm831x_aldo_set_mode, @@ -628,47 +604,18 @@ static struct platform_driver wm831x_aldo_driver = { #define WM831X_ALIVE_LDO_MAX_SELECTOR 0xf -static int wm831x_alive_ldo_set_voltage_int(struct regulator_dev *rdev, - int reg, - int min_uV, int max_uV, - unsigned *selector) -{ - struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - struct wm831x *wm831x = ldo->wm831x; - int vsel, ret; - - vsel = (min_uV - 800000) / 50000; - - ret = regulator_list_voltage_linear(rdev, vsel); - if (ret < 0) - return ret; - if (ret < min_uV || ret > max_uV) - return -EINVAL; - - *selector = vsel; - - return wm831x_set_bits(wm831x, reg, WM831X_LDO11_ON_VSEL_MASK, vsel); -} - -static int wm831x_alive_ldo_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, - unsigned *selector) -{ - struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - int reg = ldo->base + WM831X_ALIVE_LDO_ON_CONTROL; - - return wm831x_alive_ldo_set_voltage_int(rdev, reg, min_uV, max_uV, - selector); -} - static int wm831x_alive_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm831x_ldo *ldo = rdev_get_drvdata(rdev); - int reg = ldo->base + WM831X_ALIVE_LDO_SLEEP_CONTROL; - unsigned selector; + struct wm831x *wm831x = ldo->wm831x; + int sel, reg = ldo->base + WM831X_ALIVE_LDO_SLEEP_CONTROL; + + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return sel; - return wm831x_alive_ldo_set_voltage_int(rdev, reg, uV, uV, &selector); + return wm831x_set_bits(wm831x, reg, WM831X_LDO11_ON_VSEL_MASK, sel); } static int wm831x_alive_ldo_get_status(struct regulator_dev *rdev) @@ -692,7 +639,7 @@ static struct regulator_ops wm831x_alive_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, - .set_voltage = wm831x_alive_ldo_set_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_suspend_voltage = wm831x_alive_ldo_set_suspend_voltage, .get_status = wm831x_alive_ldo_get_status, -- cgit v1.2.3 From d580cb5e78763eb3d8149e0d60a0a73d355d27c5 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 20:27:58 +0800 Subject: regulator: wm831x-dcdc: Convert wm831x_buckp_ops to regulator_set_voltage_sel_regmap Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm831x-dcdc.c | 44 +++++++++-------------------------------- 1 file changed, 9 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index fbcff56e0114..91b5b0a727ef 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -599,51 +599,25 @@ static struct platform_driver wm831x_buckv_driver = { * BUCKP specifics */ -static int wm831x_buckp_set_voltage_int(struct regulator_dev *rdev, int reg, - int min_uV, int max_uV, int *selector) +static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); struct wm831x *wm831x = dcdc->wm831x; - u16 vsel; - - if (min_uV <= 34000000) - vsel = (min_uV - 850000) / 25000; - else - return -EINVAL; - - if (regulator_list_voltage_linear(rdev, vsel) > max_uV) - return -EINVAL; - - *selector = vsel; - - return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, vsel); -} - -static int wm831x_buckp_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, - unsigned *selector) -{ - struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); - u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; - - return wm831x_buckp_set_voltage_int(rdev, reg, min_uV, max_uV, - selector); -} - -static int wm831x_buckp_set_suspend_voltage(struct regulator_dev *rdev, - int uV) -{ - struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; - unsigned selector; + int sel; + + sel = regulator_map_voltage_linear(rdev, uV, uV); + if (sel < 0) + return sel; - return wm831x_buckp_set_voltage_int(rdev, reg, uV, uV, &selector); + return wm831x_set_bits(wm831x, reg, WM831X_DC3_ON_VSEL_MASK, sel); } static struct regulator_ops wm831x_buckp_ops = { - .set_voltage = wm831x_buckp_set_voltage, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, .set_suspend_voltage = wm831x_buckp_set_suspend_voltage, .is_enabled = regulator_is_enabled_regmap, -- cgit v1.2.3 From b5fb77e0188dc85630b395eb0faddc4987be6ddb Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 13 Jun 2012 20:29:17 +0800 Subject: regulator: wm831x-dcdc: Convert wm831x_buckv_ops to set_voltage_sel and map_voltage Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/wm831x-dcdc.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 91b5b0a727ef..7413885be01b 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -215,8 +215,8 @@ static int wm831x_buckv_list_voltage(struct regulator_dev *rdev, return -EINVAL; } -static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV) +static int wm831x_buckv_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) { u16 vsel; @@ -251,20 +251,14 @@ static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state) return 0; } -static int wm831x_buckv_set_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) +static int wm831x_buckv_set_voltage_sel(struct regulator_dev *rdev, + unsigned vsel) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); struct wm831x *wm831x = dcdc->wm831x; int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG; int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL; - int vsel, ret; - - vsel = wm831x_buckv_select_min_voltage(rdev, min_uV, max_uV); - if (vsel < 0) - return vsel; - - *selector = vsel; + int ret; /* If this value is already set then do a GPIO update if we can */ if (dcdc->dvs_gpio && dcdc->on_vsel == vsel) @@ -315,7 +309,7 @@ static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev, u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; int vsel; - vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV); + vsel = wm831x_buckv_map_voltage(rdev, uV, uV); if (vsel < 0) return vsel; @@ -373,9 +367,10 @@ static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev) } static struct regulator_ops wm831x_buckv_ops = { - .set_voltage = wm831x_buckv_set_voltage, + .set_voltage_sel = wm831x_buckv_set_voltage_sel, .get_voltage_sel = wm831x_buckv_get_voltage_sel, .list_voltage = wm831x_buckv_list_voltage, + .map_voltage = wm831x_buckv_map_voltage, .set_suspend_voltage = wm831x_buckv_set_suspend_voltage, .set_current_limit = wm831x_buckv_set_current_limit, .get_current_limit = wm831x_buckv_get_current_limit, -- cgit v1.2.3 From 6f0b2c696ca340cc2da381fe693fda3f8fdb2149 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Mon, 11 Jun 2012 17:41:08 +0530 Subject: regulator: Add ramp_delay configuration to constraints For some hardwares ramp_delay for BUCKs is a configurable parameter which can be configured through DT or board file.This patch adds ramp_delay to regulator constraints and allow user to configure it for regulators which supports this feature, through DT or board file. It will provide two ways of setting the ramp_delay for a regulator: First, by setting it as constraints in board file(for configurable regulators) and set_machine_constraints() will take care of setting it on hardware by calling(the provided) .set_ramp_delay() operation(callback). Second, by setting it as data in regulator_desc(as fixed/default ramp_delay rate) for a regulator in driver. regulator_set_voltage_time_sel() will give preference to constraints->ramp_delay while reading ramp_delay rate for regulator. Similarly users should also take care accordingly while refering ramp_delay rate(in case of implementing their private .set_voltage_time_sel() callbacks for different regulators). [Rewrote subject for 80 columns -- broonie] Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/core.c | 23 +++++++++++++++++++---- drivers/regulator/of_regulator.c | 6 +++++- 2 files changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6ffca9b32388..b615ae6606db 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -967,6 +967,14 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + if (rdev->constraints->ramp_delay && ops->set_ramp_delay) { + ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay); + if (ret < 0) { + rdev_err(rdev, "failed to set ramp_delay\n"); + goto out; + } + } + print_constraints(rdev); return 0; out: @@ -2296,10 +2304,17 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { - if (rdev->desc->ramp_delay && rdev->desc->uV_step) - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - rdev->desc->ramp_delay * 1000); + if (rdev->desc->uV_step) { + if (rdev->constraints->ramp_delay) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->constraints->ramp_delay * 1000); + if (rdev->desc->ramp_delay) + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + rdev->desc->ramp_delay * 1000); + rdev_warn(rdev, "ramp_delay not set\n"); + } return 0; } diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 56593b75168a..e2a731079066 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -20,7 +20,7 @@ static void of_get_regulation_constraints(struct device_node *np, struct regulator_init_data **init_data) { const __be32 *min_uV, *max_uV, *uV_offset; - const __be32 *min_uA, *max_uA; + const __be32 *min_uA, *max_uA, *ramp_delay; struct regulation_constraints *constraints = &(*init_data)->constraints; constraints->name = of_get_property(np, "regulator-name", NULL); @@ -60,6 +60,10 @@ static void of_get_regulation_constraints(struct device_node *np, constraints->always_on = true; else /* status change should be possible if not always on. */ constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + + ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); + if (ramp_delay) + constraints->min_uV = be32_to_cpu(*ramp_delay); } /** -- cgit v1.2.3 From fc3ebd788e894b4dd6c9524cb3874eeeb1e862d6 Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Fri, 15 Jun 2012 11:23:56 +0100 Subject: regmap: Move lock out from internal function _regmap_update_bits(). Locks are moved to regmap_update_bits(), which allows to reenter internal function _regmap_update_bits() from inside of regmap read/write routines. Signed-off-by: Krystian Garbaciak Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index c89aa01fb1de..652017991da6 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -982,11 +982,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, int ret; unsigned int tmp, orig; - map->lock(map); - ret = _regmap_read(map, reg, &orig); if (ret != 0) - goto out; + return ret; tmp = orig & ~mask; tmp |= val & mask; @@ -998,9 +996,6 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, *change = false; } -out: - map->unlock(map); - return ret; } @@ -1018,7 +1013,13 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val) { bool change; - return _regmap_update_bits(map, reg, mask, val, &change); + int ret; + + map->lock(map); + ret = _regmap_update_bits(map, reg, mask, val, &change); + map->unlock(map); + + return ret; } EXPORT_SYMBOL_GPL(regmap_update_bits); @@ -1038,7 +1039,12 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change) { - return _regmap_update_bits(map, reg, mask, val, change); + int ret; + + map->lock(map); + ret = _regmap_update_bits(map, reg, mask, val, change); + map->unlock(map); + return ret; } EXPORT_SYMBOL_GPL(regmap_update_bits_check); -- cgit v1.2.3 From 6863ca6227598d15c372f1e03449bbb4cfbcca7f Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Fri, 15 Jun 2012 11:23:56 +0100 Subject: regmap: Add support for register indirect addressing. Devices with register paging or indirectly accessed registers can configure register mapping to map those on virtual address range. During access to virtually mapped register range, indirect addressing is processed automatically, in following steps: 1. selector for page or indirect register is updated (when needed); 2. register in data window is accessed. Configuration should provide minimum and maximum register for virtual range, details of selector field for page selection, minimum and maximum register of data window for indirect access. Virtual range registers are managed by cache as well as direct access registers. In order to make indirect access more efficient, selector register should be declared as non-volatile, if possible. struct regmap_config is extended with the following: struct regmap_range_cfg *ranges; unsigned int n_ranges; [Also reordered debugfs init to later on since the cleanup code was conflicting with the new cleanup code for ranges anyway -- broonie] Signed-off-by: Krystian Garbaciak Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 17 ++++ drivers/base/regmap/regmap.c | 201 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 213 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index b986b8660b0c..80f9ab9c3aa4 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -95,6 +95,9 @@ struct regmap { /* if set, converts bulk rw to single rw */ bool use_single_rw; + + struct rb_root range_tree; + void *selector_work_buf; /* Scratch buffer used for selector */ }; struct regcache_ops { @@ -115,6 +118,20 @@ bool regmap_precious(struct regmap *map, unsigned int reg); int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val); +struct regmap_range_node { + struct rb_node node; + + unsigned int range_min; + unsigned int range_max; + + unsigned int selector_reg; + unsigned int selector_mask; + int selector_shift; + + unsigned int window_start; + unsigned int window_len; +}; + #ifdef CONFIG_DEBUG_FS extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_init(struct regmap *map, const char *name); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 652017991da6..83a0166420a4 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -15,12 +15,17 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include #include "internal.h" +static int _regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change); + bool regmap_writeable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) @@ -208,6 +213,67 @@ static void dev_get_regmap_release(struct device *dev, void *res) */ } +static bool _regmap_range_add(struct regmap *map, + struct regmap_range_node *data) +{ + struct rb_root *root = &map->range_tree; + struct rb_node **new = &(root->rb_node), *parent = NULL; + + while (*new) { + struct regmap_range_node *this = + container_of(*new, struct regmap_range_node, node); + + parent = *new; + if (data->range_max < this->range_min) + new = &((*new)->rb_left); + else if (data->range_min > this->range_max) + new = &((*new)->rb_right); + else + return false; + } + + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); + + return true; +} + +static struct regmap_range_node *_regmap_range_lookup(struct regmap *map, + unsigned int reg) +{ + struct rb_node *node = map->range_tree.rb_node; + + while (node) { + struct regmap_range_node *this = + container_of(node, struct regmap_range_node, node); + + if (reg < this->range_min) + node = node->rb_left; + else if (reg > this->range_max) + node = node->rb_right; + else + return this; + } + + return NULL; +} + +static void regmap_range_exit(struct regmap *map) +{ + struct rb_node *next; + struct regmap_range_node *range_node; + + next = rb_first(&map->range_tree); + while (next) { + range_node = rb_entry(next, struct regmap_range_node, node); + next = rb_next(&range_node->node); + rb_erase(&range_node->node, &map->range_tree); + kfree(range_node); + } + + kfree(map->selector_work_buf); +} + /** * regmap_init(): Initialise register map * @@ -227,6 +293,7 @@ struct regmap *regmap_init(struct device *dev, { struct regmap *map, **m; int ret = -EINVAL; + int i, j; if (!bus || !config) goto err; @@ -364,27 +431,88 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - regmap_debugfs_init(map, config->name); + map->range_tree = RB_ROOT; + for (i = 0; i < config->n_ranges; i++) { + const struct regmap_range_cfg *range_cfg = &config->ranges[i]; + struct regmap_range_node *new; + + /* Sanity check */ + if (range_cfg->range_max < range_cfg->range_min || + range_cfg->range_max > map->max_register || + range_cfg->selector_reg > map->max_register || + range_cfg->window_len == 0) + goto err_range; + + /* Make sure, that this register range has no selector + or data window within its boundary */ + for (j = 0; j < config->n_ranges; j++) { + unsigned sel_reg = config->ranges[j].selector_reg; + unsigned win_min = config->ranges[j].window_start; + unsigned win_max = win_min + + config->ranges[j].window_len - 1; + + if (range_cfg->range_min <= sel_reg && + sel_reg <= range_cfg->range_max) { + goto err_range; + } + + if (!(win_max < range_cfg->range_min || + win_min > range_cfg->range_max)) { + goto err_range; + } + } + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (new == NULL) { + ret = -ENOMEM; + goto err_range; + } + + new->range_min = range_cfg->range_min; + new->range_max = range_cfg->range_max; + new->selector_reg = range_cfg->selector_reg; + new->selector_mask = range_cfg->selector_mask; + new->selector_shift = range_cfg->selector_shift; + new->window_start = range_cfg->window_start; + new->window_len = range_cfg->window_len; + + if (_regmap_range_add(map, new) == false) { + kfree(new); + goto err_range; + } + + if (map->selector_work_buf == NULL) { + map->selector_work_buf = + kzalloc(map->format.buf_size, GFP_KERNEL); + if (map->selector_work_buf == NULL) { + ret = -ENOMEM; + goto err_range; + } + } + } ret = regcache_init(map, config); if (ret < 0) - goto err_debugfs; + goto err_range; + + regmap_debugfs_init(map, config->name); /* Add a devres resource for dev_get_regmap() */ m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); if (!m) { ret = -ENOMEM; - goto err_cache; + goto err_debugfs; } *m = map; devres_add(dev, m); return map; -err_cache: - regcache_exit(map); err_debugfs: regmap_debugfs_exit(map); + regcache_exit(map); +err_range: + regmap_range_exit(map); kfree(map->work_buf); err_map: kfree(map); @@ -481,6 +609,7 @@ void regmap_exit(struct regmap *map) { regcache_exit(map); regmap_debugfs_exit(map); + regmap_range_exit(map); if (map->bus->free_context) map->bus->free_context(map->bus_context); kfree(map->work_buf); @@ -526,6 +655,56 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name) } EXPORT_SYMBOL_GPL(dev_get_regmap); +static int _regmap_select_page(struct regmap *map, unsigned int *reg, + unsigned int val_num) +{ + struct regmap_range_node *range; + void *orig_work_buf; + unsigned int win_offset; + unsigned int win_page; + bool page_chg; + int ret; + + range = _regmap_range_lookup(map, *reg); + if (range) { + win_offset = (*reg - range->range_min) % range->window_len; + win_page = (*reg - range->range_min) / range->window_len; + + if (val_num > 1) { + /* Bulk write shouldn't cross range boundary */ + if (*reg + val_num - 1 > range->range_max) + return -EINVAL; + + /* ... or single page boundary */ + if (val_num > range->window_len - win_offset) + return -EINVAL; + } + + /* It is possible to have selector register inside data window. + In that case, selector register is located on every page and + it needs no page switching, when accessed alone. */ + if (val_num > 1 || + range->window_start + win_offset != range->selector_reg) { + /* Use separate work_buf during page switching */ + orig_work_buf = map->work_buf; + map->work_buf = map->selector_work_buf; + + ret = _regmap_update_bits(map, range->selector_reg, + range->selector_mask, + win_page << range->selector_shift, + &page_chg); + if (ret < 0) + return ret; + + map->work_buf = orig_work_buf; + } + + *reg = range->window_start + win_offset; + } + + return 0; +} + static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { @@ -563,6 +742,10 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, } } + ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); + if (ret < 0) + return ret; + map->format.format_reg(map->work_buf, reg, map->reg_shift); u8[0] |= map->write_flag_mask; @@ -626,6 +809,10 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); if (map->format.format_write) { + ret = _regmap_select_page(map, ®, 1); + if (ret < 0) + return ret; + map->format.format_write(map, reg, val); trace_regmap_hw_write_start(map->dev, reg, 1); @@ -783,6 +970,10 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; + ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); + if (ret < 0) + return ret; + map->format.format_reg(map->work_buf, reg, map->reg_shift); /* -- cgit v1.2.3 From 79504d708d8be4131fbb115912521739c11db750 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 17 Jun 2012 02:04:50 +0000 Subject: bnx2x: correct LPI pass-through configuration Commit c8c60d88c59cbb48737732ba948663a3efe882aa contained an incorrect logic which enabled a buffer overflow when accessing an array during LPI pass-through configuration. This patch fixes this issue by removing that logic altogether. Signed-off-by: Yuval Mintz Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index c7c814db027d..91aa565d4374 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -4057,18 +4057,12 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, MDIO_WC_REG_DIGITAL4_MISC3, val | 0x8080); /* Enable LPI pass through */ - if ((params->eee_mode & EEE_MODE_ADV_LPI) && - (phy->flags & FLAGS_EEE_10GBT) && - (!(params->eee_mode & EEE_MODE_ENABLE_LPI) || - bnx2x_eee_calc_timer(params)) && - (params->req_duplex[bnx2x_phy_selection(params)] == DUPLEX_FULL)) { - DP(NETIF_MSG_LINK, "Configure WC for LPI pass through\n"); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_EEE_COMBO_CONTROL0, - 0x7c); - bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL4_MISC5, 0xc000); - } + DP(NETIF_MSG_LINK, "Configure WC for LPI pass through\n"); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_EEE_COMBO_CONTROL0, + 0x7c); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL4_MISC5, 0xc000); /* 10G XFI Full Duplex */ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, -- cgit v1.2.3 From a7e2eaadd0edf6d71637c0d99aca404b0da8c1be Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 12 Jun 2012 23:58:16 +0000 Subject: net: lpc_eth: free skbs in start_xmit Transmitted skbs can be freed immediately in lpc_eth_hard_start_xmit() instead of at TX completion, since driver copies the frames in DMA area. Signed-off-by: Eric Dumazet Tested-by: Roland Stigge Signed-off-by: David S. Miller --- drivers/net/ethernet/nxp/lpc_eth.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 083d6715335c..028dde4d4979 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -440,7 +440,7 @@ struct netdata_local { spinlock_t lock; void __iomem *net_base; u32 msg_enable; - struct sk_buff *skb[ENET_TX_DESC]; + unsigned int skblen[ENET_TX_DESC]; unsigned int last_tx_idx; unsigned int num_used_tx_buffs; struct mii_bus *mii_bus; @@ -903,12 +903,11 @@ err_out: static void __lpc_handle_xmit(struct net_device *ndev) { struct netdata_local *pldat = netdev_priv(ndev); - struct sk_buff *skb; u32 txcidx, *ptxstat, txstat; txcidx = readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); while (pldat->last_tx_idx != txcidx) { - skb = pldat->skb[pldat->last_tx_idx]; + unsigned int skblen = pldat->skblen[pldat->last_tx_idx]; /* A buffer is available, get buffer status */ ptxstat = &pldat->tx_stat_v[pldat->last_tx_idx]; @@ -945,9 +944,8 @@ static void __lpc_handle_xmit(struct net_device *ndev) } else { /* Update stats */ ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; + ndev->stats.tx_bytes += skblen; } - dev_kfree_skb_irq(skb); txcidx = readl(LPC_ENET_TXCONSUMEINDEX(pldat->net_base)); } @@ -1132,7 +1130,7 @@ static int lpc_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) memcpy(pldat->tx_buff_v + txidx * ENET_MAXF_SIZE, skb->data, len); /* Save the buffer and increment the buffer counter */ - pldat->skb[txidx] = skb; + pldat->skblen[txidx] = len; pldat->num_used_tx_buffs++; /* Start transmit */ @@ -1147,6 +1145,7 @@ static int lpc_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) spin_unlock_irq(&pldat->lock); + dev_kfree_skb(skb); return NETDEV_TX_OK; } -- cgit v1.2.3 From 303e56f2d2868f1bcdd3b72c3296d02a7af28962 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 13 Jun 2012 19:37:06 +0300 Subject: iwlwifi: check that we have enough bits to track the TX queues Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index e620af3d592d..bba3edfb3488 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1352,6 +1352,9 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_dvm_cmd_strings; + WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < + priv->cfg->base_params->num_of_queues); + ucode_flags = fw->ucode_capa.flags; #ifndef CONFIG_IWLWIFI_P2P -- cgit v1.2.3 From e9d364de1aed8f7e81b3e67fdf3015533bfc76b1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Wed, 13 Jun 2012 14:16:40 +0300 Subject: iwlwifi: print the scratch of all the buffers stuck in a queue Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index cc935168ae52..c6f53af639c6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -296,6 +296,7 @@ static void iwlagn_free_dma_ptr(struct iwl_trans *trans, static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) { struct iwl_tx_queue *txq = (void *)data; + struct iwl_queue *q = &txq->q; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); u32 scd_sram_addr = trans_pcie->scd_base_addr + @@ -346,6 +347,14 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); } + for (i = q->read_ptr; i != q->write_ptr; + i = iwl_queue_inc_wrap(i, q->n_bd)) { + struct iwl_tx_cmd *tx_cmd = + (struct iwl_tx_cmd *)txq->entries[i].cmd->payload; + IWL_ERR(trans, "scratch %d = 0x%08x\n", i, + get_unaligned_le32(&tx_cmd->scratch)); + } + iwl_op_mode_nic_error(trans->op_mode); } -- cgit v1.2.3 From 901787c1674ee5d35c7c3713efbc009c72f43971 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 17 Jun 2012 16:00:22 +0300 Subject: iwlwifi: comment context requirements of the op_mode A few op_mode of the op_mode API functions have requirements on the running context of the caller. Document that. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-op-mode.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index cd9ef114d3a3..64886f95664f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -111,22 +111,25 @@ struct iwl_cfg; * May sleep * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the * HCMD the this Rx responds to. - * Must be atomic. + * Must be atomic and called with BH disabled. * @queue_full: notifies that a HW queue is full. - * Must be atomic + * Must be atomic and called with BH disabled. * @queue_not_full: notifies that a HW queue is not full any more. - * Must be atomic + * Must be atomic and called with BH disabled. * @hw_rf_kill:notifies of a change in the HW rf kill switch. True means that * the radio is killed. Must be atomic. * @free_skb: allows the transport layer to free skbs that haven't been * reclaimed by the op_mode. This can happen when the driver is freed and * there are Tx packets pending in the transport layer. * Must be atomic - * @nic_error: error notification. Must be atomic - * @cmd_queue_full: Called when the command queue gets full. Must be atomic. + * @nic_error: error notification. Must be atomic and must be called with BH + * disabled. + * @cmd_queue_full: Called when the command queue gets full. Must be atomic and + * called with BH disabled. * @nic_config: configure NIC, called before firmware is started. * May sleep - * @wimax_active: invoked when WiMax becomes active. Must be atomic. + * @wimax_active: invoked when WiMax becomes active. Must be atomic and called + * with BH disabled. */ struct iwl_op_mode_ops { struct iwl_op_mode *(*start)(struct iwl_trans *trans, @@ -165,7 +168,6 @@ struct iwl_op_mode { static inline void iwl_op_mode_stop(struct iwl_op_mode *op_mode) { might_sleep(); - op_mode->ops->stop(op_mode); } -- cgit v1.2.3 From 24172f39b02e5fcd35a4f721bde5bc9b61534736 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 17 Jun 2012 16:04:25 +0300 Subject: iwlwifi: disable BH before the call to iwl_op_mode_nic_error This is required by the op_mode API. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index c6f53af639c6..e20880acdd34 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -2026,7 +2026,9 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, if (!trans->op_mode) return -EAGAIN; + local_bh_disable(); iwl_op_mode_nic_error(trans->op_mode); + local_bh_enable(); return count; } -- cgit v1.2.3 From 3595c003f962bcf089f83e8ea44fd3169ad60ca3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 17 Jun 2012 16:12:42 +0300 Subject: iwlwifi: don't disable interrupt in iwl_abort_notification_waits This is not needed since notif_wait_lock is never accessed from IRQ. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-notif-wait.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c index 5cfed29b1b18..c61f2070f15a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c +++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c @@ -121,13 +121,12 @@ EXPORT_SYMBOL_GPL(iwl_notification_wait_notify); void iwl_abort_notification_waits(struct iwl_notif_wait_data *notif_wait) { - unsigned long flags; struct iwl_notification_wait *wait_entry; - spin_lock_irqsave(¬if_wait->notif_wait_lock, flags); + spin_lock(¬if_wait->notif_wait_lock); list_for_each_entry(wait_entry, ¬if_wait->notif_waits, list) wait_entry->aborted = true; - spin_unlock_irqrestore(¬if_wait->notif_wait_lock, flags); + spin_unlock(¬if_wait->notif_wait_lock); wake_up_all(¬if_wait->notif_waitq); } -- cgit v1.2.3 From ae8baec22878be1f8d14b5bb00ac83f7954a9832 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 14 Jun 2012 12:27:56 +0300 Subject: iwlwifi: don't disable interrupt while starting tx This is really not needed, we already have a lock inside the accesses to the prph. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index e20880acdd34..42f369d15f48 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1046,15 +1046,12 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, /* * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - * must be called under the irq lock and with MAC access */ static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) { struct iwl_trans_pcie __maybe_unused *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - lockdep_assert_held(&trans_pcie->irq_lock); - iwl_write_prph(trans, SCD_TXFACT, mask); } @@ -1062,12 +1059,9 @@ static void iwl_tx_start(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 a; - unsigned long flags; int i, chan; u32 reg_val; - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - /* make sure all queue are not stopped/used */ memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); @@ -1118,8 +1112,6 @@ static void iwl_tx_start(struct iwl_trans *trans) iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - /* Enable L1-Active */ iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); -- cgit v1.2.3 From 086ccd4379ad6c9c7c7713ef5f61b9d12e995147 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jun 2012 13:59:02 +0800 Subject: regulator: Fix setting constraints->ramp_delay in of_get_regulation_constraints Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index e2a731079066..68dc3d43dd37 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -63,7 +63,7 @@ static void of_get_regulation_constraints(struct device_node *np, ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); if (ramp_delay) - constraints->min_uV = be32_to_cpu(*ramp_delay); + constraints->ramp_delay = be32_to_cpu(*ramp_delay); } /** -- cgit v1.2.3 From 398715ab9414b3b7741c8239c254111f5016821c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jun 2012 10:11:28 +0800 Subject: regulator: core: Support table based mapping in regulator_set_voltage_time_sel For table based mapping, we can calculate voltage difference by below equation: abs(rdev->desc->volt_table[new_selector] - rdev->desc->volt_table[old_selector]) Thus we can make regulator_set_voltage_time_sel work for table based mapping. regulator_set_voltage_time_sel() only supports linear or table based mapping. In case it is misused, also warn if neither linear nor table based mapping is used with regulator_set_voltage_time_sel(). Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b615ae6606db..73a3d874ca6e 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2296,25 +2296,38 @@ EXPORT_SYMBOL_GPL(regulator_set_voltage_time); * Provided with the starting and target voltage selectors, this function * returns time in microseconds required to rise or fall to this new voltage * - * Drivers providing uV_step in their regulator_desc and ramp_delay in - * regulation_constraints can use this as their set_voltage_time_sel() - * operation. + * Drivers providing uV_step or volt_table in their regulator_desc and + * ramp_delay in regulation_constraints can use this as their + * set_voltage_time_sel() operation. */ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int old_selector, unsigned int new_selector) { - if (rdev->desc->uV_step) { - if (rdev->constraints->ramp_delay) - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - rdev->constraints->ramp_delay * 1000); - if (rdev->desc->ramp_delay) - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - rdev->desc->ramp_delay * 1000); + unsigned int ramp_delay = 0; + + if (rdev->constraints->ramp_delay) + ramp_delay = rdev->constraints->ramp_delay; + else if (rdev->desc->ramp_delay) + ramp_delay = rdev->desc->ramp_delay; + + if (ramp_delay == 0) { rdev_warn(rdev, "ramp_delay not set\n"); + return 0; } + + if (rdev->desc->uV_step) { + return DIV_ROUND_UP(rdev->desc->uV_step * + abs(new_selector - old_selector), + ramp_delay * 1000); + } else if (rdev->desc->volt_table) { + return DIV_ROUND_UP(abs(rdev->desc->volt_table[new_selector] - + rdev->desc->volt_table[old_selector]), + ramp_delay * 1000); + } else { + rdev_warn(rdev, "Unsupported voltage mapping settings\n"); + } + return 0; } -- cgit v1.2.3 From ea38d13fd1666bc030cb1c0feec5b0da2f89f9b2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jun 2012 14:03:16 +0800 Subject: regulator: core: Change the unit of ramp_delay from mV/uS to uV/uS This change makes it possible to set ramp_delay with 0.xxx mV/uS without truncation issue. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 73a3d874ca6e..ce0a3462e0de 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2319,11 +2319,11 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, if (rdev->desc->uV_step) { return DIV_ROUND_UP(rdev->desc->uV_step * abs(new_selector - old_selector), - ramp_delay * 1000); + ramp_delay); } else if (rdev->desc->volt_table) { return DIV_ROUND_UP(abs(rdev->desc->volt_table[new_selector] - rdev->desc->volt_table[old_selector]), - ramp_delay * 1000); + ramp_delay); } else { rdev_warn(rdev, "Unsupported voltage mapping settings\n"); } -- cgit v1.2.3 From 74ea0e599215330e8964401508a5d7ec41ef15b0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 15 Jun 2012 19:04:33 +0100 Subject: regulator: tps65910: Don't use 0 as NULL Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 6bf864b4bdf6..e1b01ec82301 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -1136,7 +1136,7 @@ static inline struct tps65910_board *tps65910_parse_dt_reg_data( struct of_regulator_match **tps65910_reg_matches) { *tps65910_reg_matches = NULL; - return 0; + return NULL; } #endif -- cgit v1.2.3 From 1ccd7a2a33f2b47e46c51f4501e9623a51d28090 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:13 +0200 Subject: HID: uhid: introduce user-space I/O driver support for HID This adds a dummy driver that will support user-space I/O drivers for the HID subsystem. This allows to write transport-level drivers like USB-HID and Bluetooth-HID in user-space. Low-Energy Bluetooth needs this to feed HID data that is parsed in user-space back into the kernel. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 21 +++++++++++++ drivers/hid/Makefile | 1 + drivers/hid/uhid.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 drivers/hid/uhid.c (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e9c68fedfcff..8cca0af2dbd1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -59,6 +59,27 @@ config HIDRAW If unsure, say Y. +config UHID + tristate "User-space I/O driver support for HID subsystem" + depends on HID + default n + ---help--- + Say Y here if you want to provide HID I/O Drivers from user-space. + This allows to write I/O drivers in user-space and feed the data from + the device into the kernel. The kernel parses the HID reports, loads the + corresponding HID Device Driver or provides input devices on top of your + user-space device. + + This driver cannot be used to parse HID-reports in user-space and write + special HID-drivers. You should use hidraw for that. + Instead, this driver allows to write the transport-layer driver in + user-space like USB-HID and Bluetooth-HID do in kernel-space. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called uhid. + source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ca6cc9f0485c..d7061928325e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DEBUG_FS endif obj-$(CONFIG_HID) += hid.o +obj-$(CONFIG_UHID) += uhid.o obj-$(CONFIG_HID_GENERIC) += hid-generic.o diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c new file mode 100644 index 000000000000..5b02d6cb0e60 --- /dev/null +++ b/drivers/hid/uhid.c @@ -0,0 +1,88 @@ +/* + * User-space I/O driver support for HID subsystem + * Copyright (c) 2012 David Herrmann + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UHID_NAME "uhid" + +static struct miscdevice uhid_misc; + +static int uhid_char_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int uhid_char_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t uhid_char_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static ssize_t uhid_char_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + return 0; +} + +static unsigned int uhid_char_poll(struct file *file, poll_table *wait) +{ + return 0; +} + +static const struct file_operations uhid_fops = { + .owner = THIS_MODULE, + .open = uhid_char_open, + .release = uhid_char_release, + .read = uhid_char_read, + .write = uhid_char_write, + .poll = uhid_char_poll, + .llseek = no_llseek, +}; + +static struct miscdevice uhid_misc = { + .fops = &uhid_fops, + .minor = MISC_DYNAMIC_MINOR, + .name = UHID_NAME, +}; + +static int __init uhid_init(void) +{ + return misc_register(&uhid_misc); +} + +static void __exit uhid_exit(void) +{ + misc_deregister(&uhid_misc); +} + +module_init(uhid_init); +module_exit(uhid_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann "); +MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); -- cgit v1.2.3 From ace3d8614ab0e6544f5f85921085b55b915fe9aa Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:14 +0200 Subject: HID: uhid: add internal message buffer When receiving messages from the HID subsystem, we need to process them and store them in an internal buffer so user-space can read() on the char device to retrieve the messages. This adds a static buffer for 32 messages to each uhid device. Each message is dynamically allocated so the uhid_device structure does not get too big. uhid_queue() adds a message to the buffer. If the buffer is full, the message is discarded. uhid_queue_event() is an helper for messages without payload. This also adds a public header: uhid.h. It contains the declarations for the user-space API. It is built around "struct uhid_event" which contains a type field which specifies the event type and each event can then add a variable-length payload. For now, there is only a dummy event but later patches will add new event types and payloads. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5b02d6cb0e60..05ef4b05a63e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -25,16 +25,81 @@ #include #define UHID_NAME "uhid" +#define UHID_BUFSIZE 32 + +struct uhid_device { + struct hid_device *hid; + + wait_queue_head_t waitq; + spinlock_t qlock; + __u8 head; + __u8 tail; + struct uhid_event *outq[UHID_BUFSIZE]; +}; static struct miscdevice uhid_misc; +static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev) +{ + __u8 newhead; + + newhead = (uhid->head + 1) % UHID_BUFSIZE; + + if (newhead != uhid->tail) { + uhid->outq[uhid->head] = ev; + uhid->head = newhead; + wake_up_interruptible(&uhid->waitq); + } else { + hid_warn(uhid->hid, "Output queue is full\n"); + kfree(ev); + } +} + +static int uhid_queue_event(struct uhid_device *uhid, __u32 event) +{ + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = event; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { + struct uhid_device *uhid; + + uhid = kzalloc(sizeof(*uhid), GFP_KERNEL); + if (!uhid) + return -ENOMEM; + + spin_lock_init(&uhid->qlock); + init_waitqueue_head(&uhid->waitq); + + file->private_data = uhid; + nonseekable_open(inode, file); + return 0; } static int uhid_char_release(struct inode *inode, struct file *file) { + struct uhid_device *uhid = file->private_data; + unsigned int i; + + for (i = 0; i < UHID_BUFSIZE; ++i) + kfree(uhid->outq[i]); + + kfree(uhid); + return 0; } -- cgit v1.2.3 From 1f9dec1e0164b48da9b268a02197f38caa69b118 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:15 +0200 Subject: HID: uhid: allow poll()'ing on uhid devices As long as the internal buffer is not empty, we return POLLIN to user-space. uhid->head and uhid->tail are no atomics so the comparison may return inexact results. However, this doesn't matter here as user-space would need to poll() in two threads simultaneously to trigger this. And in this case it doesn't matter if a cached result is returned or the exact new result as user-space does not know which thread returns first from poll() and the following read(). So it is safe to compare the values without locking. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 05ef4b05a63e..b1a477f8260c 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -117,6 +117,13 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, static unsigned int uhid_char_poll(struct file *file, poll_table *wait) { + struct uhid_device *uhid = file->private_data; + + poll_wait(file, &uhid->waitq, wait); + + if (uhid->head != uhid->tail) + return POLLIN | POLLRDNORM; + return 0; } -- cgit v1.2.3 From d937ae5fae17e63aaa97f029be221a6516b25475 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:16 +0200 Subject: HID: uhid: implement read() on uhid devices User-space can use read() to get a single event from uhid devices. read() does never return multiple events. This allows us to extend the event structure and still keep backwards compatibility. If user-space wants to get multiple events in one syscall, they should use the readv()/writev() syscalls which are supported by uhid. This introduces a new lock which helps us synchronizing simultaneous reads from user-space. We also correctly return -EINVAL/-EFAULT only on errors and retry the read() when some other thread captured the event faster than we did. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index b1a477f8260c..93860826d629 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -28,6 +28,7 @@ #define UHID_BUFSIZE 32 struct uhid_device { + struct mutex devlock; struct hid_device *hid; wait_queue_head_t waitq; @@ -81,6 +82,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) if (!uhid) return -ENOMEM; + mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); @@ -106,7 +108,49 @@ static int uhid_char_release(struct inode *inode, struct file *file) static ssize_t uhid_char_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + unsigned long flags; + size_t len; + + /* they need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + +try_again: + if (file->f_flags & O_NONBLOCK) { + if (uhid->head == uhid->tail) + return -EAGAIN; + } else { + ret = wait_event_interruptible(uhid->waitq, + uhid->head != uhid->tail); + if (ret) + return ret; + } + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + if (uhid->head == uhid->tail) { + mutex_unlock(&uhid->devlock); + goto try_again; + } else { + len = min(count, sizeof(**uhid->outq)); + if (copy_to_user(buffer, &uhid->outq[uhid->tail], len)) { + ret = -EFAULT; + } else { + kfree(uhid->outq[uhid->tail]); + uhid->outq[uhid->tail] = NULL; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid->tail = (uhid->tail + 1) % UHID_BUFSIZE; + spin_unlock_irqrestore(&uhid->qlock, flags); + } + } + + mutex_unlock(&uhid->devlock); + return ret ? ret : len; } static ssize_t uhid_char_write(struct file *file, const char __user *buffer, -- cgit v1.2.3 From 6664ef72a47459f883d3409ca9b2fa200015704b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:17 +0200 Subject: HID: uhid: implement write() on uhid devices Similar to read() you can only write() a single event with one call to an uhid device. To write multiple events use writev() which is supported by uhid. We currently always return -EOPNOTSUPP but other events will be added in later patches. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 93860826d629..31e8379cfd15 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -30,6 +30,7 @@ struct uhid_device { struct mutex devlock; struct hid_device *hid; + struct uhid_event input_buf; wait_queue_head_t waitq; spinlock_t qlock; @@ -156,7 +157,35 @@ try_again: static ssize_t uhid_char_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - return 0; + struct uhid_device *uhid = file->private_data; + int ret; + size_t len; + + /* we need at least the "type" member of uhid_event */ + if (count < sizeof(__u32)) + return -EINVAL; + + ret = mutex_lock_interruptible(&uhid->devlock); + if (ret) + return ret; + + memset(&uhid->input_buf, 0, sizeof(uhid->input_buf)); + len = min(count, sizeof(uhid->input_buf)); + if (copy_from_user(&uhid->input_buf, buffer, len)) { + ret = -EFAULT; + goto unlock; + } + + switch (uhid->input_buf.type) { + default: + ret = -EOPNOTSUPP; + } + +unlock: + mutex_unlock(&uhid->devlock); + + /* return "count" not "len" to not confuse the caller */ + return ret ? ret : count; } static unsigned int uhid_char_poll(struct file *file, poll_table *wait) -- cgit v1.2.3 From d365c6cfd337a2bccdc65eacce271a311ea1072c Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:18 +0200 Subject: HID: uhid: add UHID_CREATE and UHID_DESTROY events UHID_CREATE and UHID_DESTROY are used to create and destroy a device on an open uhid char-device. Internally, we allocate and register an HID device with the HID core and immediately start the device. From now on events may be received or sent to the device. The UHID_CREATE event has a payload similar to the data used by Bluetooth-HIDP when creating a new connection. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 31e8379cfd15..61ee7cc32ccf 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -29,6 +29,11 @@ struct uhid_device { struct mutex devlock; + bool running; + + __u8 *rd_data; + uint rd_size; + struct hid_device *hid; struct uhid_event input_buf; @@ -75,6 +80,136 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) return 0; } +static int uhid_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_stop(struct hid_device *hid) +{ +} + +static int uhid_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void uhid_hid_close(struct hid_device *hid) +{ +} + +static int uhid_hid_input(struct input_dev *input, unsigned int type, + unsigned int code, int value) +{ + return 0; +} + +static int uhid_hid_parse(struct hid_device *hid) +{ + return 0; +} + +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + return 0; +} + +static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) +{ + return 0; +} + +static struct hid_ll_driver uhid_hid_driver = { + .start = uhid_hid_start, + .stop = uhid_hid_stop, + .open = uhid_hid_open, + .close = uhid_hid_close, + .hidinput_input_event = uhid_hid_input, + .parse = uhid_hid_parse, +}; + +static int uhid_dev_create(struct uhid_device *uhid, + const struct uhid_event *ev) +{ + struct hid_device *hid; + int ret; + + if (uhid->running) + return -EALREADY; + + uhid->rd_size = ev->u.create.rd_size; + if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) + return -EINVAL; + + uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); + if (!uhid->rd_data) + return -ENOMEM; + + if (copy_from_user(uhid->rd_data, ev->u.create.rd_data, + uhid->rd_size)) { + ret = -EFAULT; + goto err_free; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_free; + } + + strncpy(hid->name, ev->u.create.name, 127); + hid->name[127] = 0; + strncpy(hid->phys, ev->u.create.phys, 63); + hid->phys[63] = 0; + strncpy(hid->uniq, ev->u.create.uniq, 63); + hid->uniq[63] = 0; + + hid->ll_driver = &uhid_hid_driver; + hid->hid_get_raw_report = uhid_hid_get_raw; + hid->hid_output_raw_report = uhid_hid_output_raw; + hid->bus = ev->u.create.bus; + hid->vendor = ev->u.create.vendor; + hid->product = ev->u.create.product; + hid->version = ev->u.create.version; + hid->country = ev->u.create.country; + hid->driver_data = uhid; + hid->dev.parent = uhid_misc.this_device; + + uhid->hid = hid; + uhid->running = true; + + ret = hid_add_device(hid); + if (ret) { + hid_err(hid, "Cannot register HID device\n"); + goto err_hid; + } + + return 0; + +err_hid: + hid_destroy_device(hid); + uhid->hid = NULL; + uhid->running = false; +err_free: + kfree(uhid->rd_data); + return ret; +} + +static int uhid_dev_destroy(struct uhid_device *uhid) +{ + if (!uhid->running) + return -EINVAL; + + uhid->running = false; + + hid_destroy_device(uhid->hid); + kfree(uhid->rd_data); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -86,6 +221,7 @@ static int uhid_char_open(struct inode *inode, struct file *file) mutex_init(&uhid->devlock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + uhid->running = false; file->private_data = uhid; nonseekable_open(inode, file); @@ -98,6 +234,8 @@ static int uhid_char_release(struct inode *inode, struct file *file) struct uhid_device *uhid = file->private_data; unsigned int i; + uhid_dev_destroy(uhid); + for (i = 0; i < UHID_BUFSIZE; ++i) kfree(uhid->outq[i]); @@ -177,6 +315,12 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, } switch (uhid->input_buf.type) { + case UHID_CREATE: + ret = uhid_dev_create(uhid, &uhid->input_buf); + break; + case UHID_DESTROY: + ret = uhid_dev_destroy(uhid); + break; default: ret = -EOPNOTSUPP; } -- cgit v1.2.3 From 5e87a36ae375297b71cc21ac7e32846832bcfb34 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:19 +0200 Subject: HID: uhid: allow feeding input data into uhid devices This adds a new event type UHID_INPUT which allows user-space to feed raw HID reports into the HID subsystem. We copy the data into kernel memory and directly feed it into the HID core. There is no error handling of the events couldn't be parsed so user-space should consider all events successfull unless read() returns an error. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 61ee7cc32ccf..3d1ebda122e5 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -210,6 +210,17 @@ static int uhid_dev_destroy(struct uhid_device *uhid) return 0; } +static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) +{ + if (!uhid->running) + return -EINVAL; + + hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input.data, + min_t(size_t, ev->u.input.size, UHID_DATA_MAX), 0); + + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -321,6 +332,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_DESTROY: ret = uhid_dev_destroy(uhid); break; + case UHID_INPUT: + ret = uhid_dev_input(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } -- cgit v1.2.3 From 037c061bca06cbfe9998476fb593090300fbbe87 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:20 +0200 Subject: HID: uhid: forward hid report-descriptor to hid core When the uhid_hid_parse callback is called we simply forward it to hid_parse_report() with the data that we got in the UHID_CREATE event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 3d1ebda122e5..0d011db30a46 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -106,7 +106,9 @@ static int uhid_hid_input(struct input_dev *input, unsigned int type, static int uhid_hid_parse(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); } static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, -- cgit v1.2.3 From ec4b7dea453e0f9fd0fbf1761b2d01eff64f264b Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:21 +0200 Subject: HID: uhid: add UHID_START and UHID_STOP events We send UHID_START and UHID_STOP events to user-space when the HID core starts/stops the device. This notifies user-space about driver readiness and data-I/O can start now. This directly forwards the callbacks from hid-core to user-space. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0d011db30a46..2e7f3a023975 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -82,11 +82,17 @@ static int uhid_queue_event(struct uhid_device *uhid, __u32 event) static int uhid_hid_start(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_START); } static void uhid_hid_stop(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + hid->claimed = 0; + uhid_queue_event(uhid, UHID_STOP); } static int uhid_hid_open(struct hid_device *hid) -- cgit v1.2.3 From e7191474a5459e6ba7039fadca6a32f3a5731909 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:22 +0200 Subject: HID: uhid: forward open/close events to user-space HID core notifies us with *_open/*_close callbacks when there is an actual user of our device. We forward these to user-space so they can react on this. This allows user-space to skip I/O unless they receive an OPEN event. When they receive a CLOSE event they can stop I/O again to save energy. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 2e7f3a023975..0226ba3f5307 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -97,11 +97,16 @@ static void uhid_hid_stop(struct hid_device *hid) static int uhid_hid_open(struct hid_device *hid) { - return 0; + struct uhid_device *uhid = hid->driver_data; + + return uhid_queue_event(uhid, UHID_OPEN); } static void uhid_hid_close(struct hid_device *hid) { + struct uhid_device *uhid = hid->driver_data; + + uhid_queue_event(uhid, UHID_CLOSE); } static int uhid_hid_input(struct input_dev *input, unsigned int type, -- cgit v1.2.3 From f80e13601c51a836b2aac583b8a3b4327c0c27ce Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:23 +0200 Subject: HID: uhid: forward output request to user-space If the hid-driver wants to send standardized data to the device it uses a linux input_event. We forward this to the user-space transport-level driver so they can perform the requested action on the device. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0226ba3f5307..4dd693e1c8b8 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -112,6 +112,24 @@ static void uhid_hid_close(struct hid_device *hid) static int uhid_hid_input(struct input_dev *input, unsigned int type, unsigned int code, int value) { + struct hid_device *hid = input_get_drvdata(input); + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT_EV; + ev->u.output_ev.type = type; + ev->u.output_ev.code = code; + ev->u.output_ev.value = value; + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; } -- cgit v1.2.3 From 3b3baa82e4306b5160692643fab2fa322ceb94f9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:24 +0200 Subject: HID: uhid: forward raw output reports to user-space Some drivers that use non-standard HID features require raw output reports sent to the device. We now forward these requests directly to user-space so the transport-level driver can correctly send it to the device or handle it correspondingly. There is no way to signal back whether the transmission was successful, moreover, there might be lots of messages coming out from the driver flushing the output-queue. However, there is currently no driver that causes this so we are safe. If some drivers need to transmit lots of data this way, we need a method to synchronize this and can implement another UHID_OUTPUT_SYNC event. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 4dd693e1c8b8..421c492dc824 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -149,7 +149,39 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 rtype; + unsigned long flags; + struct uhid_event *ev; + + switch (report_type) { + case HID_FEATURE_REPORT: + rtype = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + rtype = UHID_OUTPUT_REPORT; + break; + default: + return -EINVAL; + } + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = rtype; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; } static struct hid_ll_driver uhid_hid_driver = { -- cgit v1.2.3 From fcfcf0deb89ece6eb9ae23768fec1bc1718f9b7f Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 10 Jun 2012 15:16:25 +0200 Subject: HID: uhid: implement feature requests HID standard allows sending a feature request to the device which is answered by an HID report. uhid implements this by sending a UHID_FEATURE event to user-space which then must answer with UHID_FEATURE_ANSWER. If it doesn't do this in a timely manner, the request is discarded silently. We serialize the feature requests, that is, there is always only a single active feature-request sent to user-space, other requests have to wait. HIDP and USB-HID do it the same way. Because we discard feature-requests silently, we must make sure to match a response to the corresponding request. We use sequence-IDs for this so user-space must copy the ID from the request into the answer. Feature-answers are ignored if they do not contain the same ID as the currently pending feature request. Internally, we must make sure that feature-requests are synchronized with UHID_DESTROY and close() events. We must not dead-lock when closing the HID device, either, so we have to use separate locks. Signed-off-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 421c492dc824..ea560bfa033d 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -42,6 +42,12 @@ struct uhid_device { __u8 head; __u8 tail; struct uhid_event *outq[UHID_BUFSIZE]; + + struct mutex report_lock; + wait_queue_head_t report_wait; + atomic_t report_done; + atomic_t report_id; + struct uhid_event report_buf; }; static struct miscdevice uhid_misc; @@ -143,7 +149,84 @@ static int uhid_hid_parse(struct hid_device *hid) static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, __u8 *buf, size_t count, unsigned char rtype) { - return 0; + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t len; + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; } static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, @@ -265,7 +348,11 @@ static int uhid_dev_destroy(struct uhid_device *uhid) if (!uhid->running) return -EINVAL; + /* clear "running" before setting "report_done" */ uhid->running = false; + smp_wmb(); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); hid_destroy_device(uhid->hid); kfree(uhid->rd_data); @@ -284,6 +371,31 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev) return 0; } +static int uhid_dev_feature_answer(struct uhid_device *uhid, + struct uhid_event *ev) +{ + unsigned long flags; + + if (!uhid->running) + return -EINVAL; + + spin_lock_irqsave(&uhid->qlock, flags); + + /* id for old report; drop it silently */ + if (atomic_read(&uhid->report_id) != ev->u.feature_answer.id) + goto unlock; + if (atomic_read(&uhid->report_done)) + goto unlock; + + memcpy(&uhid->report_buf, ev, sizeof(*ev)); + atomic_set(&uhid->report_done, 1); + wake_up_interruptible(&uhid->report_wait); + +unlock: + spin_unlock_irqrestore(&uhid->qlock, flags); + return 0; +} + static int uhid_char_open(struct inode *inode, struct file *file) { struct uhid_device *uhid; @@ -293,9 +405,12 @@ static int uhid_char_open(struct inode *inode, struct file *file) return -ENOMEM; mutex_init(&uhid->devlock); + mutex_init(&uhid->report_lock); spin_lock_init(&uhid->qlock); init_waitqueue_head(&uhid->waitq); + init_waitqueue_head(&uhid->report_wait); uhid->running = false; + atomic_set(&uhid->report_done, 1); file->private_data = uhid; nonseekable_open(inode, file); @@ -398,6 +513,9 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, case UHID_INPUT: ret = uhid_dev_input(uhid, &uhid->input_buf); break; + case UHID_FEATURE_ANSWER: + ret = uhid_dev_feature_answer(uhid, &uhid->input_buf); + break; default: ret = -EOPNOTSUPP; } -- cgit v1.2.3 From eb6476441bc2fecf6232a87d0313a85f8e3da7f4 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 14 Jun 2012 14:23:02 +0300 Subject: iwlwifi: protect use_ict with irq_lock This variable was accessed without taking the lock. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/rx.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index d6860c070c16..be143eb4aa4f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -867,24 +867,23 @@ void iwl_disable_ict(struct iwl_trans *trans) spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } +/* legacy (non-ICT) ISR. Assumes that trans_pcie->irq_lock is held */ static irqreturn_t iwl_isr(int irq, void *data) { struct iwl_trans *trans = data; - struct iwl_trans_pcie *trans_pcie; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 inta, inta_mask; - unsigned long flags; #ifdef CONFIG_IWLWIFI_DEBUG u32 inta_fh; #endif + + lockdep_assert_held(&trans_pcie->irq_lock); + if (!trans) return IRQ_NONE; trace_iwlwifi_dev_irq(trans->dev); - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. * If we have something to service, the tasklet will re-enable ints. @@ -907,7 +906,7 @@ static irqreturn_t iwl_isr(int irq, void *data) /* Hardware disappeared. It might have already raised * an interrupt */ IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); - goto unplugged; + return IRQ_HANDLED; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -926,18 +925,13 @@ static irqreturn_t iwl_isr(int irq, void *data) !trans_pcie->inta) iwl_enable_interrupts(trans); - unplugged: - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_HANDLED; - - none: +none: /* re-enable interrupts here since we don't have anything to service. */ /* only Re-enable if disabled by irq and no schedules tasklet. */ if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return IRQ_NONE; } @@ -963,15 +957,19 @@ irqreturn_t iwl_isr_ict(int irq, void *data) trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + /* dram interrupt table not set yet, * use legacy interrupt. */ - if (!trans_pcie->use_ict) - return iwl_isr(irq, data); + if (unlikely(!trans_pcie->use_ict)) { + irqreturn_t ret = iwl_isr(irq, data); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + return ret; + } trace_iwlwifi_dev_irq(trans->dev); - spin_lock_irqsave(&trans_pcie->irq_lock, flags); /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. -- cgit v1.2.3 From dada03ca73b699337781ae36e1ce4c8fec2da40a Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 17 Jun 2012 15:42:37 +0300 Subject: iwlwifi: disable early power Off reset for all NICs This feature needs to be disabled for all NICs. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/devices.c | 13 ------------- drivers/net/wireless/iwlwifi/dvm/main.c | 13 ++++++++++++- 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 0521a6be09d2..349c205d5f62 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -250,17 +250,6 @@ struct iwl_lib_ops iwl2030_lib = { */ /* NIC configuration for 5000 series */ -static void iwl5000_nic_config(struct iwl_priv *priv) -{ - /* W/A : NIC is stuck in a reset state after Early PCIe power off - * (PCIe power is lost before PERST# is asserted), - * causing ME FW to lose ownership and not being able to obtain it back. - */ - iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); -} - static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { .min_nrg_cck = 100, .auto_corr_min_ofdm = 90, @@ -433,14 +422,12 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, struct iwl_lib_ops iwl5000_lib = { .set_hw_params = iwl5000_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, - .nic_config = iwl5000_nic_config, .temperature = iwlagn_temperature, }; struct iwl_lib_ops iwl5150_lib = { .set_hw_params = iwl5150_hw_set_hw_params, .set_channel_switch = iwl5000_hw_channel_switch, - .nic_config = iwl5000_nic_config, .temperature = iwl5150_temperature, }; diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index bba3edfb3488..c642c217e29f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -51,11 +51,13 @@ #include "iwl-op-mode.h" #include "iwl-drv.h" #include "iwl-modparams.h" +#include "iwl-prph.h" #include "dev.h" #include "calib.h" #include "agn.h" + /****************************************************************************** * * module boiler plate @@ -2076,7 +2078,16 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode) CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); - priv->lib->nic_config(priv); + /* W/A : NIC is stuck in a reset state after Early PCIe power off + * (PCIe power is lost before PERST# is asserted), + * causing ME FW to lose ownership and not being able to obtain it back. + */ + iwl_set_bits_mask_prph(priv->trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + + if (priv->lib->nic_config) + priv->lib->nic_config(priv); } static void iwl_wimax_active(struct iwl_op_mode *op_mode) -- cgit v1.2.3 From 601968b3bb1176b4cee47faba2d7a104422de8e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 14 Jun 2012 21:22:17 +0200 Subject: iwlwifi: delay ROC if doing internal reset scan When the device is doing an internal radio reset scan, ROC can be rejected to the supplicant with busy status which confuses it. One option would be to queue the ROC and handle it later, but since the radio reset scan is very quick we can just wait for it to finish instead. Also add a warning since we shouldn't run into the case of having a scan active when requesting a ROC in any other case since mac80211 will not scan while ROC or ROC while scanning. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 9d2374862314..c57726348ffe 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1036,8 +1036,18 @@ static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); if (test_bit(STATUS_SCAN_HW, &priv->status)) { - err = -EBUSY; - goto out; + /* mac80211 should not scan while ROC or ROC while scanning */ + if (WARN_ON_ONCE(priv->scan_type != IWL_SCAN_RADIO_RESET)) { + err = -EBUSY; + goto out; + } + + iwl_scan_cancel_timeout(priv, 100); + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + err = -EBUSY; + goto out; + } } priv->hw_roc_channel = channel; -- cgit v1.2.3 From 230a5a1c41464f7fe5b676c21280ae4effa222c8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 15 Jun 2012 18:25:08 +0100 Subject: regulator: Fix double free in devm_regulator_put() Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 663ebd534ef9..729b20d1c1de 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1461,9 +1461,7 @@ void devm_regulator_put(struct regulator *regulator) rc = devres_release(regulator->dev, devm_regulator_release, devm_regulator_match, regulator); - if (rc == 0) - regulator_put(regulator); - else + if (rc != 0) WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_regulator_put); -- cgit v1.2.3 From 0e8e5c34cf1a8beaaf0a6a05c053592693bf8cb4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Jun 2012 17:23:13 +0100 Subject: regulator: twl: Remove references to 32kHz clock from DT bindings Due to the lack of a generic clock API we'd had the 32kHz clock in the regulator driver but this is definitely a Linux-specific thing and now we have a clock API hopefully the code can be moved elsewhere. Try to avoid getting DTs deployed relying on the 32kHz clock by removing it from the bindings, grep seems to tell me it's not currently used anyway. Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index a1d07e517db3..ca308cb8e676 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -1106,7 +1106,6 @@ static u8 twl_get_smps_mult(void) #define TWL6030_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6030, label) #define TWL6025_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6025, label) #define TWLFIXED_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLFIXED, label) -#define TWLRES_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLRES, label) #define TWLSMPS_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLSMPS, label) static const struct of_device_id twl_of_match[] __devinitconst = { @@ -1154,7 +1153,6 @@ static const struct of_device_id twl_of_match[] __devinitconst = { TWLFIXED_OF_MATCH("ti,twl6030-vusb", VUSB), TWLFIXED_OF_MATCH("ti,twl6030-v1v8", V1V8), TWLFIXED_OF_MATCH("ti,twl6030-v2v1", V2V1), - TWLRES_OF_MATCH("ti,twl6030-clk32kg", CLK32KG), TWLSMPS_OF_MATCH("ti,twl6025-smps3", SMPS3), TWLSMPS_OF_MATCH("ti,twl6025-smps4", SMPS4), TWLSMPS_OF_MATCH("ti,twl6025-vio", VIO), -- cgit v1.2.3 From 25976a796cca22d6b0b2e9f821fa00fc4d98daa0 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Sun, 17 Jun 2012 18:52:34 +0800 Subject: HID: select NEW_LEDS for driver of Lenovo ThinkPad USB Keyboard with TrackPoint commit 'HID: Driver for Lenovo Keyboard with Trackpoint' (c1dcad2d32d0252e8a3023d20311b52a187ecda3) introduced a compiling error due to unmetting dependency of CONFIG_LEDS_CLASS. on i386: ERROR: "led_brightness_set" [drivers/leds/led-class.ko] undefined! ERROR: "leds_list" [drivers/leds/led-class.ko] undefined! ERROR: "leds_list_lock" [drivers/leds/led-class.ko] undefined! Reported-by: Randy Dunlap Signed-off-by: Bryan Wu Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ca7e76cab83d..cbf0e0344e3e 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -271,6 +271,7 @@ config HID_LCPOWER config HID_LENOVO_TPKBD tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" depends on USB_HID + select NEW_LEDS select LEDS_CLASS ---help--- Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. -- cgit v1.2.3 From 1a8b294ce6b0cdab438175eed45ebb088bdd659a Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 18 Jun 2012 17:08:08 +0200 Subject: HID: uhid: silence gcc warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc is giving me: drivers/hid/uhid.c: In function ‘uhid_hid_get_raw’: drivers/hid/uhid.c:157: warning: ‘len’ may be used uninitialized in this function which is clearly bogus, as - when used as memcpy() argument, it's initialized properly - the code is structured in a way that either 'ret' or 'len' is always initialized, so the return statement always has an initialized value. Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index ea560bfa033d..119b7e6405be 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -154,7 +154,7 @@ static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, struct uhid_event *ev; unsigned long flags; int ret; - size_t len; + size_t uninitialized_var(len); struct uhid_feature_answer_req *req; if (!uhid->running) -- cgit v1.2.3 From de509f9f08726077ffd2f2d5e4b63e17cb516938 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 7 Jun 2012 10:30:59 +0200 Subject: PCI: add Chelsio T310 10GbE NIC broken INTx masking quirk According to http://thread.gmane.org/gmane.comp.emulators.kvm.devel/91388 the T310 does not properly support INTx masking as it fails to keep the PCI_STATUS_INTERRUPT bit updated once the interrupt is masked. Mark this adapter as broken so that pci_intx_mask_supported won't report it as compatible. [bhelgaas: use HEADER, not FINAL, which is currently broken for hotplug] Tested-by: Alexey Kardashevskiy Signed-off-by: Jan Kiszka Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index cc13415416d7..b2b66d21105f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2938,6 +2938,8 @@ static void __devinit quirk_broken_intx_masking(struct pci_dev *dev) { dev->broken_intx_masking = 1; } +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030, + quirk_broken_intx_masking); static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) -- cgit v1.2.3 From 0bdb3b213ac64f9a16e59d57660174543eaa01f0 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 7 Jun 2012 11:01:59 -0600 Subject: PCI: add Ralink RT2800 broken INTx masking quirk Passes pci_intx_mask_supported test but continues to send interrupts as discovered through VFIO-based device assignment. http://www.spinics.net/lists/kvm/msg73738.html [bhelgaas: use HEADER, not FINAL, which is currently broken for hotplug] Tested-by: Andreas Hartmann Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b2b66d21105f..2bbea6428d2e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2940,6 +2940,8 @@ static void __devinit quirk_broken_intx_masking(struct pci_dev *dev) } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CHELSIO, 0x0030, quirk_broken_intx_masking); +DECLARE_PCI_FIXUP_HEADER(0x1814, 0x0601, /* Ralink RT2800 802.11n PCI */ + quirk_broken_intx_masking); static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) -- cgit v1.2.3 From 4d146ad7388a9cca2a890d7449aeb767565068d7 Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Sat, 9 Jun 2012 20:00:19 -0300 Subject: [media] pms: fix build error in pms_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a compiler breakage introduced by commit 8173090acb33: drivers/media/video/pms.c: In function ‘pms_probe’: drivers/media/video/pms.c:1047:2: error: implicit declaration of function ‘kzalloc’ [-Werror=implicit-function-declaration] drivers/media/video/pms.c:1116:2: error: implicit declaration of function ‘kfree’ [-Werror=implicit-function-declaration] Signed-off-by: Fengguang Wu Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pms.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index af2d9086d7e8..77f9c92186f4 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From b31525d16b50fe0eb33545afbc0be1a03f2896e3 Mon Sep 17 00:00:00 2001 From: "stigge@antcom.de" Date: Mon, 18 Jun 2012 10:14:42 +0000 Subject: net: lpc_eth: Driver cleanup This patch removes some nowadays superfluous definitions (one unused define and an obsolete function forward declaration) and corrects a netdev_err() to netdev_dbg(). Signed-off-by: Roland Stigge Signed-off-by: Alexandre Pereira da Silva Signed-off-by: David S. Miller --- drivers/net/ethernet/nxp/lpc_eth.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c index 028dde4d4979..e7d2496a4738 100644 --- a/drivers/net/ethernet/nxp/lpc_eth.c +++ b/drivers/net/ethernet/nxp/lpc_eth.c @@ -52,7 +52,6 @@ #define MODNAME "lpc-eth" #define DRV_VERSION "1.00" -#define PHYDEF_ADDR 0x00 #define ENET_MAXF_SIZE 1536 #define ENET_RX_DESC 48 @@ -416,9 +415,6 @@ static bool use_iram_for_net(struct device *dev) #define TXDESC_CONTROL_LAST (1 << 30) #define TXDESC_CONTROL_INT (1 << 31) -static int lpc_eth_hard_start_xmit(struct sk_buff *skb, - struct net_device *ndev); - /* * Structure of a TX/RX descriptors and RX status */ @@ -1441,7 +1437,7 @@ static int lpc_eth_drv_probe(struct platform_device *pdev) res->start); netdev_dbg(ndev, "IO address size :%d\n", res->end - res->start + 1); - netdev_err(ndev, "IO address (mapped) :0x%p\n", + netdev_dbg(ndev, "IO address (mapped) :0x%p\n", pldat->net_base); netdev_dbg(ndev, "IRQ number :%d\n", ndev->irq); netdev_dbg(ndev, "DMA buffer size :%d\n", pldat->dma_buff_size); -- cgit v1.2.3 From 632a5b01db8cd4068a06f8a67720ea4f9b00d9b3 Mon Sep 17 00:00:00 2001 From: Krystian Garbaciak Date: Mon, 18 Jun 2012 13:04:29 +0100 Subject: regmap: Fix work_buf switching for page update during virtual range access. After page update, orginal work_buf has to be restored regardless of the result. Signed-off-by: Krystian Garbaciak Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 83a0166420a4..d912eb2d19ce 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -693,10 +693,11 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, range->selector_mask, win_page << range->selector_shift, &page_chg); - if (ret < 0) - return ret; map->work_buf = orig_work_buf; + + if (ret < 0) + return ret; } *reg = range->window_start + win_offset; -- cgit v1.2.3 From 2c19ad43d1a4b0e376a0c764e3c2350afd018fac Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 19 Jun 2012 14:19:57 +0800 Subject: regulator: mc13xxx: Remove mc13xxx_sw_regulator_is_enabled function If .is_enabled callback is not implemented, regulator core assumes that the regulator is always on. Thus we don't need mc13xxx_sw_regulator_is_enabled function. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/mc13892-regulator.c | 1 - drivers/regulator/mc13xxx-regulator-core.c | 6 ------ drivers/regulator/mc13xxx.h | 1 - 3 files changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index 6329723b5372..b388b746452e 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -469,7 +469,6 @@ err: } static struct regulator_ops mc13892_sw_regulator_ops = { - .is_enabled = mc13xxx_sw_regulator_is_enabled, .list_voltage = regulator_list_voltage_table, .set_voltage_sel = mc13892_sw_regulator_set_voltage_sel, .get_voltage = mc13892_sw_regulator_get_voltage, diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index 8a6b050ff5aa..d6eda28ca5d0 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -170,12 +170,6 @@ struct regulator_ops mc13xxx_fixed_regulator_ops = { }; EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops); -int mc13xxx_sw_regulator_is_enabled(struct regulator_dev *rdev) -{ - return 1; -} -EXPORT_SYMBOL_GPL(mc13xxx_sw_regulator_is_enabled); - #ifdef CONFIG_OF int __devinit mc13xxx_get_num_regulators_dt(struct platform_device *pdev) { diff --git a/drivers/regulator/mc13xxx.h b/drivers/regulator/mc13xxx.h index 9a8f72ce718e..8343a25888d2 100644 --- a/drivers/regulator/mc13xxx.h +++ b/drivers/regulator/mc13xxx.h @@ -33,7 +33,6 @@ struct mc13xxx_regulator_priv { }; extern int mc13xxx_sw_regulator(struct regulator_dev *rdev); -extern int mc13xxx_sw_regulator_is_enabled(struct regulator_dev *rdev); extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector); extern int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev); -- cgit v1.2.3 From ba3bd8a301c76838faded10dbe2ecdfdc2eb5d7e Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 19 Jun 2012 17:12:18 +0800 Subject: regulator: tps65023: Convert to regulator_list_voltage_table In current mapping table settings, min_uV/max_uV are always equal to volt_table[0] and volt_table[table_len -1]. Thus remove min_uV and max_uV from struct tps_info. The table based mapping with table_len == 1 means fixed voltage. So we don't need fixed flag to differentiate if it is fixed or not. This patch adds DCDC_FIXED_3300000_VSEL_table and DCDC_FIXED_1800000_VSEL_table for fixed voltage cases. So we can convert tps65023_dcdc_ops to regulator_list_voltage_table. This makes the code simpler. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65023-regulator.c | 153 +++++++++++---------------------- 1 file changed, 49 insertions(+), 104 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index df8b70d76f9e..45f4eeb221f7 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -91,48 +91,53 @@ #define TPS65023_MAX_REG_ID TPS65023_LDO_2 /* Supported voltage values for regulators */ -static const u16 VCORE_VSEL_table[] = { - 800, 825, 850, 875, - 900, 925, 950, 975, - 1000, 1025, 1050, 1075, - 1100, 1125, 1150, 1175, - 1200, 1225, 1250, 1275, - 1300, 1325, 1350, 1375, - 1400, 1425, 1450, 1475, - 1500, 1525, 1550, 1600, +static const unsigned int VCORE_VSEL_table[] = { + 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1600000, +}; + +static const unsigned int DCDC_FIXED_3300000_VSEL_table[] = { + 3300000, +}; + +static const unsigned int DCDC_FIXED_1800000_VSEL_table[] = { + 1800000, }; /* Supported voltage values for LDO regulators for tps65020 */ -static const u16 TPS65020_LDO1_VSEL_table[] = { - 1000, 1050, 1100, 1300, - 1800, 2500, 3000, 3300, +static const unsigned int TPS65020_LDO1_VSEL_table[] = { + 1000000, 1050000, 1100000, 1300000, + 1800000, 2500000, 3000000, 3300000, }; -static const u16 TPS65020_LDO2_VSEL_table[] = { - 1000, 1050, 1100, 1300, - 1800, 2500, 3000, 3300, +static const unsigned int TPS65020_LDO2_VSEL_table[] = { + 1000000, 1050000, 1100000, 1300000, + 1800000, 2500000, 3000000, 3300000, }; /* Supported voltage values for LDO regulators * for tps65021 and tps65023 */ -static const u16 TPS65023_LDO1_VSEL_table[] = { - 1000, 1100, 1300, 1800, - 2200, 2600, 2800, 3150, +static const unsigned int TPS65023_LDO1_VSEL_table[] = { + 1000000, 1100000, 1300000, 1800000, + 2200000, 2600000, 2800000, 3150000, }; -static const u16 TPS65023_LDO2_VSEL_table[] = { - 1050, 1200, 1300, 1800, - 2500, 2800, 3000, 3300, +static const unsigned int TPS65023_LDO2_VSEL_table[] = { + 1050000, 1200000, 1300000, 1800000, + 2500000, 2800000, 3000000, 3300000, }; /* Regulator specific details */ struct tps_info { const char *name; - unsigned min_uV; - unsigned max_uV; - bool fixed; u8 table_len; - const u16 *table; + const unsigned int *table; }; /* PMIC details */ @@ -164,9 +169,9 @@ static int tps65023_dcdc_get_voltage(struct regulator_dev *dev) if (ret != 0) return ret; data &= (tps->info[dcdc]->table_len - 1); - return tps->info[dcdc]->table[data] * 1000; + return tps->info[dcdc]->table[data]; } else - return tps->info[dcdc]->min_uV; + return tps->info[dcdc]->table[0]; } static int tps65023_dcdc_set_voltage_sel(struct regulator_dev *dev, @@ -208,7 +213,7 @@ static int tps65023_ldo_get_voltage(struct regulator_dev *dev) data >>= (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1)); data &= (tps->info[ldo]->table_len - 1); - return tps->info[ldo]->table[data] * 1000; + return tps->info[ldo]->table[data]; } static int tps65023_ldo_set_voltage_sel(struct regulator_dev *dev, @@ -222,39 +227,6 @@ static int tps65023_ldo_set_voltage_sel(struct regulator_dev *dev, selector << TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_index)); } -static int tps65023_dcdc_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct tps_pmic *tps = rdev_get_drvdata(dev); - int dcdc = rdev_get_id(dev); - - if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) - return -EINVAL; - - if (dcdc == tps->core_regulator) { - if (selector >= tps->info[dcdc]->table_len) - return -EINVAL; - else - return tps->info[dcdc]->table[selector] * 1000; - } else - return tps->info[dcdc]->min_uV; -} - -static int tps65023_ldo_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct tps_pmic *tps = rdev_get_drvdata(dev); - int ldo = rdev_get_id(dev); - - if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) - return -EINVAL; - - if (selector >= tps->info[ldo]->table_len) - return -EINVAL; - else - return tps->info[ldo]->table[selector] * 1000; -} - /* Operations permitted on VDCDCx */ static struct regulator_ops tps65023_dcdc_ops = { .is_enabled = regulator_is_enabled_regmap, @@ -262,7 +234,7 @@ static struct regulator_ops tps65023_dcdc_ops = { .disable = regulator_disable_regmap, .get_voltage = tps65023_dcdc_get_voltage, .set_voltage_sel = tps65023_dcdc_set_voltage_sel, - .list_voltage = tps65023_dcdc_list_voltage, + .list_voltage = regulator_list_voltage_table, }; /* Operations permitted on LDOx */ @@ -272,7 +244,7 @@ static struct regulator_ops tps65023_ldo_ops = { .disable = regulator_disable_regmap, .get_voltage = tps65023_ldo_get_voltage, .set_voltage_sel = tps65023_ldo_set_voltage_sel, - .list_voltage = tps65023_ldo_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static struct regmap_config tps65023_regmap_config = { @@ -324,10 +296,8 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->desc[i].name = info->name; tps->desc[i].id = i; - if (info->fixed) - tps->desc[i].n_voltages = 1; - else - tps->desc[i].n_voltages = info->table_len; + tps->desc[i].n_voltages = info->table_len; + tps->desc[i].volt_table = info->table; tps->desc[i].ops = (i > TPS65023_DCDC_3 ? &tps65023_ldo_ops : &tps65023_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; @@ -387,35 +357,26 @@ static int __devexit tps_65023_remove(struct i2c_client *client) static const struct tps_info tps65020_regs[] = { { .name = "VDCDC1", - .min_uV = 3300000, - .max_uV = 3300000, - .fixed = 1, + .table_len = ARRAY_SIZE(DCDC_FIXED_3300000_VSEL_table), + .table = DCDC_FIXED_3300000_VSEL_table, }, { .name = "VDCDC2", - .min_uV = 1800000, - .max_uV = 1800000, - .fixed = 1, + .table_len = ARRAY_SIZE(DCDC_FIXED_1800000_VSEL_table), + .table = DCDC_FIXED_1800000_VSEL_table, }, { .name = "VDCDC3", - .min_uV = 800000, - .max_uV = 1600000, .table_len = ARRAY_SIZE(VCORE_VSEL_table), .table = VCORE_VSEL_table, }, - { .name = "LDO1", - .min_uV = 1000000, - .max_uV = 3150000, .table_len = ARRAY_SIZE(TPS65020_LDO1_VSEL_table), .table = TPS65020_LDO1_VSEL_table, }, { .name = "LDO2", - .min_uV = 1050000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(TPS65020_LDO2_VSEL_table), .table = TPS65020_LDO2_VSEL_table, }, @@ -424,34 +385,26 @@ static const struct tps_info tps65020_regs[] = { static const struct tps_info tps65021_regs[] = { { .name = "VDCDC1", - .min_uV = 3300000, - .max_uV = 3300000, - .fixed = 1, + .table_len = ARRAY_SIZE(DCDC_FIXED_3300000_VSEL_table), + .table = DCDC_FIXED_3300000_VSEL_table, }, { .name = "VDCDC2", - .min_uV = 1800000, - .max_uV = 1800000, - .fixed = 1, + .table_len = ARRAY_SIZE(DCDC_FIXED_1800000_VSEL_table), + .table = DCDC_FIXED_1800000_VSEL_table, }, { .name = "VDCDC3", - .min_uV = 800000, - .max_uV = 1600000, .table_len = ARRAY_SIZE(VCORE_VSEL_table), .table = VCORE_VSEL_table, }, { .name = "LDO1", - .min_uV = 1000000, - .max_uV = 3150000, .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), .table = TPS65023_LDO1_VSEL_table, }, { .name = "LDO2", - .min_uV = 1050000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), .table = TPS65023_LDO2_VSEL_table, }, @@ -460,34 +413,26 @@ static const struct tps_info tps65021_regs[] = { static const struct tps_info tps65023_regs[] = { { .name = "VDCDC1", - .min_uV = 800000, - .max_uV = 1600000, .table_len = ARRAY_SIZE(VCORE_VSEL_table), .table = VCORE_VSEL_table, }, { .name = "VDCDC2", - .min_uV = 3300000, - .max_uV = 3300000, - .fixed = 1, + .table_len = ARRAY_SIZE(DCDC_FIXED_3300000_VSEL_table), + .table = DCDC_FIXED_3300000_VSEL_table, }, { .name = "VDCDC3", - .min_uV = 1800000, - .max_uV = 1800000, - .fixed = 1, + .table_len = ARRAY_SIZE(DCDC_FIXED_1800000_VSEL_table), + .table = DCDC_FIXED_1800000_VSEL_table, }, { .name = "LDO1", - .min_uV = 1000000, - .max_uV = 3150000, .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), .table = TPS65023_LDO1_VSEL_table, }, { .name = "LDO2", - .min_uV = 1050000, - .max_uV = 3300000, .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), .table = TPS65023_LDO2_VSEL_table, }, -- cgit v1.2.3 From a133829e66d25e1ce293a30bcc3fb8eb653a1519 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 19 Jun 2012 17:13:13 +0800 Subject: regulator: tps65023: Convert to get_voltage_sel Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65023-regulator.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 45f4eeb221f7..4b6f1b2b9481 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -155,7 +155,7 @@ struct tps_driver_data { u8 core_regulator; }; -static int tps65023_dcdc_get_voltage(struct regulator_dev *dev) +static int tps65023_dcdc_get_voltage_sel(struct regulator_dev *dev) { struct tps_pmic *tps = rdev_get_drvdata(dev); int ret; @@ -169,9 +169,9 @@ static int tps65023_dcdc_get_voltage(struct regulator_dev *dev) if (ret != 0) return ret; data &= (tps->info[dcdc]->table_len - 1); - return tps->info[dcdc]->table[data]; + return data; } else - return tps->info[dcdc]->table[0]; + return 0; } static int tps65023_dcdc_set_voltage_sel(struct regulator_dev *dev, @@ -198,7 +198,7 @@ out: return ret; } -static int tps65023_ldo_get_voltage(struct regulator_dev *dev) +static int tps65023_ldo_get_voltage_sel(struct regulator_dev *dev) { struct tps_pmic *tps = rdev_get_drvdata(dev); int data, ldo = rdev_get_id(dev); @@ -213,7 +213,7 @@ static int tps65023_ldo_get_voltage(struct regulator_dev *dev) data >>= (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1)); data &= (tps->info[ldo]->table_len - 1); - return tps->info[ldo]->table[data]; + return data; } static int tps65023_ldo_set_voltage_sel(struct regulator_dev *dev, @@ -232,7 +232,7 @@ static struct regulator_ops tps65023_dcdc_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .get_voltage = tps65023_dcdc_get_voltage, + .get_voltage_sel = tps65023_dcdc_get_voltage_sel, .set_voltage_sel = tps65023_dcdc_set_voltage_sel, .list_voltage = regulator_list_voltage_table, }; @@ -242,7 +242,7 @@ static struct regulator_ops tps65023_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .get_voltage = tps65023_ldo_get_voltage, + .get_voltage_sel = tps65023_ldo_get_voltage_sel, .set_voltage_sel = tps65023_ldo_set_voltage_sel, .list_voltage = regulator_list_voltage_table, }; -- cgit v1.2.3 From 852abad238da723efac29f3e78dd768c18d6a062 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Tue, 19 Jun 2012 13:23:42 +0530 Subject: regulator: max77686: Convert driver to use regulator_set_voltage_time_sel. This patch converts the driver to use regulator_set_voltage_time_sel() as .set_voltage_time_sel() callback. It also sets ramp_delay as 100000 uV/us for LDOs & normal BUCKs and 27500 uV/us(default/reset value) for BUCKs[2/3/4] in regulator_desc[]. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 42 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 2dd4ac91d283..a29eee33dd7b 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -40,6 +40,8 @@ #define MAX77686_LDO_LOW_UVSTEP 25000 #define MAX77686_BUCK_MINUV 750000 #define MAX77686_BUCK_UVSTEP 50000 +#define MAX77686_RAMP_DELAY 100000 /* uV/us */ +#define MAX77686_DVS_RAMP_DELAY 27500 /* uV/us */ #define MAX77686_DVS_MINUV 600000 #define MAX77686_DVS_UVSTEP 12500 @@ -66,28 +68,8 @@ struct max77686_data { struct device *dev; struct max77686_dev *iodev; struct regulator_dev **rdev; - int ramp_delay; /* in mV/us */ }; -static int max77686_set_dvs_voltage_time_sel(struct regulator_dev *rdev, - unsigned int old_selector, unsigned int new_selector) -{ - struct max77686_data *max77686 = rdev_get_drvdata(rdev); - int ramp_rate[] = {13, 27, 55, 100}; - - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - ramp_rate[max77686->ramp_delay] * 1000); -} - -static int max77686_set_voltage_time_sel(struct regulator_dev *rdev, - unsigned int old_selector, unsigned int new_selector) -{ - /* Unconditionally 100 mV/us */ - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), 100 * 1000); -} - static struct regulator_ops max77686_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -96,7 +78,7 @@ static struct regulator_ops max77686_ops = { .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .set_voltage_time_sel = max77686_set_voltage_time_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, }; static struct regulator_ops max77686_buck_dvs_ops = { @@ -107,7 +89,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .set_voltage_time_sel = max77686_set_dvs_voltage_time_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, }; #define regulator_desc_ldo(num) { \ @@ -118,6 +100,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .owner = THIS_MODULE, \ .min_uV = MAX77686_LDO_MINUV, \ .uV_step = MAX77686_LDO_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ .n_voltages = MAX77686_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ .vsel_mask = MAX77686_VSEL_MASK, \ @@ -133,6 +116,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .owner = THIS_MODULE, \ .min_uV = MAX77686_LDO_LOW_MINUV, \ .uV_step = MAX77686_LDO_LOW_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ .n_voltages = MAX77686_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_LDO1CTRL1 + num - 1, \ .vsel_mask = MAX77686_VSEL_MASK, \ @@ -148,6 +132,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .owner = THIS_MODULE, \ .min_uV = MAX77686_BUCK_MINUV, \ .uV_step = MAX77686_BUCK_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ .n_voltages = MAX77686_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_BUCK5OUT + (num - 5) * 2, \ .vsel_mask = MAX77686_VSEL_MASK, \ @@ -162,6 +147,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .owner = THIS_MODULE, \ .min_uV = MAX77686_BUCK_MINUV, \ .uV_step = MAX77686_BUCK_UVSTEP, \ + .ramp_delay = MAX77686_RAMP_DELAY, \ .n_voltages = MAX77686_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_BUCK1OUT, \ .vsel_mask = MAX77686_VSEL_MASK, \ @@ -176,6 +162,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .owner = THIS_MODULE, \ .min_uV = MAX77686_DVS_MINUV, \ .uV_step = MAX77686_DVS_UVSTEP, \ + .ramp_delay = MAX77686_DVS_RAMP_DELAY, \ .n_voltages = MAX77686_DVS_VSEL_MASK + 1, \ .vsel_reg = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10, \ .vsel_mask = MAX77686_DVS_VSEL_MASK, \ @@ -255,17 +242,6 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) max77686->iodev = iodev; platform_set_drvdata(pdev, max77686); - max77686->ramp_delay = RAMP_RATE_NO_CTRL; /* Set 0x3 for RAMP */ - regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_BUCK2CTRL1, MAX77686_RAMP_RATE_MASK, - max77686->ramp_delay << 6); - regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_BUCK3CTRL1, MAX77686_RAMP_RATE_MASK, - max77686->ramp_delay << 6); - regmap_update_bits(max77686->iodev->regmap, - MAX77686_REG_BUCK4CTRL1, MAX77686_RAMP_RATE_MASK, - max77686->ramp_delay << 6); - for (i = 0; i < MAX77686_REGULATORS; i++) { config.dev = max77686->dev; config.regmap = iodev->regmap; -- cgit v1.2.3 From c848bc8538cd29e92223d5e72e8fb60e6f2e176e Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Mon, 18 Jun 2012 09:49:20 +0900 Subject: regulator: s5m8767a: Support AP watchdog reset operation The S5M8767A can't know status of ap reset. So, After AP watchdog reset, AP can't boot normally. Problem can be happened like below condition. - AP Bootable lowest voltage(vdd_arm): 0.9v - AP DVFS voltage table: 0.8v, 0.9v, 1.0v - During AP works on lowest voltage(0.8V), watchdog reset is asserted - AP can't boot, because vdd arm is still 0.8v Solution - Basic concept: After ap watchdog reset, GPIO configuration is changed by default value - S5M8767A has function of voltage control with gpio (8 levels with 3 gpios) - Set bootable voltage on level 0 -> can work with default gpio configuration - In the probing, Change voltage control level from level 0 to level 1 - Execute normal dvfs operation on level 1 - After watchdog reset, ap gpio is set by default value - PMIC operation mode is changed by ap reset (level1 -> level0) - Regardless of previous vdd_arm voltage, AP always can be booted. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 137 ++++++++++++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index a4a3c7eefd1f..fd89574c405c 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -41,6 +41,7 @@ struct s5m8767_info { u8 buck3_vol[8]; u8 buck4_vol[8]; int buck_gpios[3]; + int buck_ds[3]; int buck_gpioindex; }; @@ -283,17 +284,17 @@ static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg) reg = S5M8767_REG_BUCK1CTRL2; break; case S5M8767_BUCK2: - reg = S5M8767_REG_BUCK2DVS1; + reg = S5M8767_REG_BUCK2DVS2; if (s5m8767->buck2_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK3: - reg = S5M8767_REG_BUCK3DVS1; + reg = S5M8767_REG_BUCK3DVS2; if (s5m8767->buck3_gpiodvs) reg += s5m8767->buck_gpioindex; break; case S5M8767_BUCK4: - reg = S5M8767_REG_BUCK4DVS1; + reg = S5M8767_REG_BUCK4DVS2; if (s5m8767->buck4_gpiodvs) reg += s5m8767->buck_gpioindex; break; @@ -512,7 +513,7 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) struct regulator_config config = { }; struct regulator_dev **rdev; struct s5m8767_info *s5m8767; - int i, ret, size; + int i, ret, size, buck_init; if (!pdata) { dev_err(pdev->dev.parent, "Platform data not supplied\n"); @@ -563,12 +564,37 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767->buck_gpios[0] = pdata->buck_gpios[0]; s5m8767->buck_gpios[1] = pdata->buck_gpios[1]; s5m8767->buck_gpios[2] = pdata->buck_gpios[2]; + s5m8767->buck_ds[0] = pdata->buck_ds[0]; + s5m8767->buck_ds[1] = pdata->buck_ds[1]; + s5m8767->buck_ds[2] = pdata->buck_ds[2]; + s5m8767->ramp_delay = pdata->buck_ramp_delay; s5m8767->buck2_ramp = pdata->buck2_ramp_enable; s5m8767->buck3_ramp = pdata->buck3_ramp_enable; s5m8767->buck4_ramp = pdata->buck4_ramp_enable; s5m8767->opmode = pdata->opmode; + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck2_init, + pdata->buck2_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK2DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck3_init, + pdata->buck3_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK3DVS2, buck_init); + + buck_init = s5m8767_convert_voltage_to_sel(&buck_voltage_val2, + pdata->buck4_init, + pdata->buck4_init + + buck_voltage_val2.step); + + s5m_reg_write(s5m8767->iodev, S5M8767_REG_BUCK4DVS2, buck_init); + for (i = 0; i < 8; i++) { if (s5m8767->buck2_gpiodvs) { s5m8767->buck2_vol[i] = @@ -598,48 +624,71 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) } } - if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || - pdata->buck4_gpiodvs) { - if (gpio_is_valid(pdata->buck_gpios[0]) && - gpio_is_valid(pdata->buck_gpios[1]) && - gpio_is_valid(pdata->buck_gpios[2])) { - ret = gpio_request(pdata->buck_gpios[0], - "S5M8767 SET1"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET1\n"); - - ret = gpio_request(pdata->buck_gpios[1], - "S5M8767 SET2"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET2\n"); - - ret = gpio_request(pdata->buck_gpios[2], - "S5M8767 SET3"); - if (ret == -EBUSY) - dev_warn(&pdev->dev, "Duplicated gpio request for SET3\n"); - /* SET1 GPIO */ - gpio_direction_output(pdata->buck_gpios[0], - (s5m8767->buck_gpioindex >> 2) & 0x1); - /* SET2 GPIO */ - gpio_direction_output(pdata->buck_gpios[1], - (s5m8767->buck_gpioindex >> 1) & 0x1); - /* SET3 GPIO */ - gpio_direction_output(pdata->buck_gpios[2], - (s5m8767->buck_gpioindex >> 0) & 0x1); - ret = 0; - } else { - dev_err(&pdev->dev, "GPIO NOT VALID\n"); - ret = -EINVAL; - return ret; - } + if (gpio_is_valid(pdata->buck_gpios[0]) && + gpio_is_valid(pdata->buck_gpios[1]) && + gpio_is_valid(pdata->buck_gpios[2])) { + ret = gpio_request(pdata->buck_gpios[0], "S5M8767 SET1"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET1\n"); + + ret = gpio_request(pdata->buck_gpios[1], "S5M8767 SET2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET2\n"); + + ret = gpio_request(pdata->buck_gpios[2], "S5M8767 SET3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request" + " for SET3\n"); + /* SET1 GPIO */ + gpio_direction_output(pdata->buck_gpios[0], + (s5m8767->buck_gpioindex >> 2) & 0x1); + /* SET2 GPIO */ + gpio_direction_output(pdata->buck_gpios[1], + (s5m8767->buck_gpioindex >> 1) & 0x1); + /* SET3 GPIO */ + gpio_direction_output(pdata->buck_gpios[2], + (s5m8767->buck_gpioindex >> 0) & 0x1); + ret = 0; + + } else { + dev_err(&pdev->dev, "GPIO NOT VALID\n"); + ret = -EINVAL; + return ret; } - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, - (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, - (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); - s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, - (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), 1 << 1); + ret = gpio_request(pdata->buck_ds[0], "S5M8767 DS2"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS2\n"); + + ret = gpio_request(pdata->buck_ds[1], "S5M8767 DS3"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS3\n"); + + ret = gpio_request(pdata->buck_ds[2], "S5M8767 DS4"); + if (ret == -EBUSY) + dev_warn(&pdev->dev, "Duplicated gpio request for DS4\n"); + + /* DS2 GPIO */ + gpio_direction_output(pdata->buck_ds[0], 0x0); + /* DS3 GPIO */ + gpio_direction_output(pdata->buck_ds[1], 0x0); + /* DS4 GPIO */ + gpio_direction_output(pdata->buck_ds[2], 0x0); + + if (pdata->buck2_gpiodvs || pdata->buck3_gpiodvs || + pdata->buck4_gpiodvs) { + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK2CTRL, + (pdata->buck2_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK3CTRL, + (pdata->buck3_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + s5m_reg_update(s5m8767->iodev, S5M8767_REG_BUCK4CTRL, + (pdata->buck4_gpiodvs) ? (1 << 1) : (0 << 1), + 1 << 1); + } /* Initialize GPIO DVS registers */ for (i = 0; i < 8; i++) { -- cgit v1.2.3 From e90a84473ee941c0056c773923e9cc90a550c266 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 19 Jun 2012 17:14:31 +0800 Subject: regulator: tps65023: Convert tps65023_ldo_ops to regulator_[get|set]_voltage_sel_regmap Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65023-regulator.c | 51 +++++++++------------------------- 1 file changed, 13 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 4b6f1b2b9481..6998d579d07b 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -69,10 +69,6 @@ #define TPS65023_REG_CTRL2_DCDC1 BIT(1) #define TPS65023_REG_CTRL2_DCDC3 BIT(0) -/* LDO_CTRL bitfields */ -#define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4) -#define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0x0F << ((ldo_id)*4)) - /* Number of step-down converters available */ #define TPS65023_NUM_DCDC 3 /* Number of LDO voltage regulators available */ @@ -198,35 +194,6 @@ out: return ret; } -static int tps65023_ldo_get_voltage_sel(struct regulator_dev *dev) -{ - struct tps_pmic *tps = rdev_get_drvdata(dev); - int data, ldo = rdev_get_id(dev); - int ret; - - if (ldo < TPS65023_LDO_1 || ldo > TPS65023_LDO_2) - return -EINVAL; - - ret = regmap_read(tps->regmap, TPS65023_REG_LDO_CTRL, &data); - if (ret != 0) - return ret; - - data >>= (TPS65023_LDO_CTRL_LDOx_SHIFT(ldo - TPS65023_LDO_1)); - data &= (tps->info[ldo]->table_len - 1); - return data; -} - -static int tps65023_ldo_set_voltage_sel(struct regulator_dev *dev, - unsigned selector) -{ - struct tps_pmic *tps = rdev_get_drvdata(dev); - int ldo_index = rdev_get_id(dev) - TPS65023_LDO_1; - - return regmap_update_bits(tps->regmap, TPS65023_REG_LDO_CTRL, - TPS65023_LDO_CTRL_LDOx_MASK(ldo_index), - selector << TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_index)); -} - /* Operations permitted on VDCDCx */ static struct regulator_ops tps65023_dcdc_ops = { .is_enabled = regulator_is_enabled_regmap, @@ -242,8 +209,8 @@ static struct regulator_ops tps65023_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .get_voltage_sel = tps65023_ldo_get_voltage_sel, - .set_voltage_sel = tps65023_ldo_set_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_table, }; @@ -304,13 +271,21 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->desc[i].owner = THIS_MODULE; tps->desc[i].enable_reg = TPS65023_REG_REG_CTRL; - if (i == TPS65023_LDO_1) + switch (i) { + case TPS65023_LDO_1: + tps->desc[i].vsel_reg = TPS65023_REG_LDO_CTRL; + tps->desc[i].vsel_mask = 0x07; tps->desc[i].enable_mask = 1 << 1; - else if (i == TPS65023_LDO_2) + break; + case TPS65023_LDO_2: + tps->desc[i].vsel_reg = TPS65023_REG_LDO_CTRL; + tps->desc[i].vsel_mask = 0x70; tps->desc[i].enable_mask = 1 << 2; - else /* DCDCx */ + break; + default: /* DCDCx */ tps->desc[i].enable_mask = 1 << (TPS65023_NUM_REGULATOR - i); + } config.dev = &client->dev; config.init_data = init_data; -- cgit v1.2.3 From 5b92da0443c2585e31b64e86c2e1b8e22845d4bb Mon Sep 17 00:00:00 2001 From: Federico Vaga Date: Thu, 14 Jun 2012 13:43:42 +0200 Subject: c_can_pci: generic module for C_CAN/D_CAN on PCI Signed-off-by: Federico Vaga Acked-by: Giancarlo Asnaghi Cc: Alan Cox Acked-by: Wolfgang Grandegger Acked-by: Bhupesh Sharma [mkl: fix call to pci_iounmap] Signed-off-by: Marc Kleine-Budde --- drivers/net/can/c_can/Kconfig | 7 ++ drivers/net/can/c_can/Makefile | 1 + drivers/net/can/c_can/c_can_pci.c | 236 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 drivers/net/can/c_can/c_can_pci.c (limited to 'drivers') diff --git a/drivers/net/can/c_can/Kconfig b/drivers/net/can/c_can/Kconfig index 25d371cf98dd..3b83bafcd947 100644 --- a/drivers/net/can/c_can/Kconfig +++ b/drivers/net/can/c_can/Kconfig @@ -13,4 +13,11 @@ config CAN_C_CAN_PLATFORM boards from ST Microelectronics (http://www.st.com) like the SPEAr1310 and SPEAr320 evaluation boards & TI (www.ti.com) boards like am335x, dm814x, dm813x and dm811x. + +config CAN_C_CAN_PCI + tristate "Generic PCI Bus based C_CAN/D_CAN driver" + depends on PCI + ---help--- + This driver adds support for the C_CAN/D_CAN chips connected + to the PCI bus. endif diff --git a/drivers/net/can/c_can/Makefile b/drivers/net/can/c_can/Makefile index 9273f6d5c4b7..ad1cc842170a 100644 --- a/drivers/net/can/c_can/Makefile +++ b/drivers/net/can/c_can/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_CAN_C_CAN) += c_can.o obj-$(CONFIG_CAN_C_CAN_PLATFORM) += c_can_platform.o +obj-$(CONFIG_CAN_C_CAN_PCI) += c_can_pci.o ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c new file mode 100644 index 000000000000..914aecfa09a9 --- /dev/null +++ b/drivers/net/can/c_can/c_can_pci.c @@ -0,0 +1,236 @@ +/* + * PCI bus driver for Bosch C_CAN/D_CAN controller + * + * Copyright (C) 2012 Federico Vaga + * + * Borrowed from c_can_platform.c + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include + +#include "c_can.h" + +enum c_can_pci_reg_align { + C_CAN_REG_ALIGN_16, + C_CAN_REG_ALIGN_32, +}; + +struct c_can_pci_data { + /* Specify if is C_CAN or D_CAN */ + enum c_can_dev_id type; + /* Set the register alignment in the memory */ + enum c_can_pci_reg_align reg_align; + /* Set the frequency if clk is not usable */ + unsigned int freq; +}; + +/* + * 16-bit c_can registers can be arranged differently in the memory + * architecture of different implementations. For example: 16-bit + * registers can be aligned to a 16-bit boundary or 32-bit boundary etc. + * Handle the same by providing a common read/write interface. + */ +static u16 c_can_pci_read_reg_aligned_to_16bit(struct c_can_priv *priv, + enum reg index) +{ + return readw(priv->base + priv->regs[index]); +} + +static void c_can_pci_write_reg_aligned_to_16bit(struct c_can_priv *priv, + enum reg index, u16 val) +{ + writew(val, priv->base + priv->regs[index]); +} + +static u16 c_can_pci_read_reg_aligned_to_32bit(struct c_can_priv *priv, + enum reg index) +{ + return readw(priv->base + 2 * priv->regs[index]); +} + +static void c_can_pci_write_reg_aligned_to_32bit(struct c_can_priv *priv, + enum reg index, u16 val) +{ + writew(val, priv->base + 2 * priv->regs[index]); +} + +static int __devinit c_can_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct c_can_pci_data *c_can_pci_data = (void *)ent->driver_data; + struct c_can_priv *priv; + struct net_device *dev; + void __iomem *addr; + struct clk *clk; + int ret; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "pci_enable_device FAILED\n"); + goto out; + } + + ret = pci_request_regions(pdev, KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "pci_request_regions FAILED\n"); + goto out_disable_device; + } + + pci_set_master(pdev); + pci_enable_msi(pdev); + + addr = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!addr) { + dev_err(&pdev->dev, + "device has no PCI memory resources, " + "failing adapter\n"); + ret = -ENOMEM; + goto out_release_regions; + } + + /* allocate the c_can device */ + dev = alloc_c_can_dev(); + if (!dev) { + ret = -ENOMEM; + goto out_iounmap; + } + + priv = netdev_priv(dev); + pci_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + dev->irq = pdev->irq; + priv->base = addr; + + if (!c_can_pci_data->freq) { + /* get the appropriate clk */ + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "no clock defined\n"); + ret = -ENODEV; + goto out_free_c_can; + } + priv->can.clock.freq = clk_get_rate(clk); + priv->priv = clk; + } else { + priv->can.clock.freq = c_can_pci_data->freq; + priv->priv = NULL; + } + + /* Configure CAN type */ + switch (c_can_pci_data->type) { + case C_CAN_DEVTYPE: + priv->regs = reg_map_c_can; + break; + case D_CAN_DEVTYPE: + priv->regs = reg_map_d_can; + priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; + break; + default: + ret = -EINVAL; + goto out_free_clock; + } + + /* Configure access to registers */ + switch (c_can_pci_data->reg_align) { + case C_CAN_REG_ALIGN_32: + priv->read_reg = c_can_pci_read_reg_aligned_to_32bit; + priv->write_reg = c_can_pci_write_reg_aligned_to_32bit; + break; + case C_CAN_REG_ALIGN_16: + priv->read_reg = c_can_pci_read_reg_aligned_to_16bit; + priv->write_reg = c_can_pci_write_reg_aligned_to_16bit; + break; + default: + ret = -EINVAL; + goto out_free_clock; + } + + ret = register_c_can_dev(dev); + if (ret) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + KBUILD_MODNAME, ret); + goto out_free_clock; + } + + dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", + KBUILD_MODNAME, priv->regs, dev->irq); + + return 0; + +out_free_clock: + if (priv->priv) + clk_put(priv->priv); +out_free_c_can: + pci_set_drvdata(pdev, NULL); + free_c_can_dev(dev); +out_iounmap: + pci_iounmap(pdev, addr); +out_release_regions: + pci_disable_msi(pdev); + pci_clear_master(pdev); + pci_release_regions(pdev); +out_disable_device: + pci_disable_device(pdev); +out: + return ret; +} + +static void __devexit c_can_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct c_can_priv *priv = netdev_priv(dev); + + unregister_c_can_dev(dev); + + if (priv->priv) + clk_put(priv->priv); + + pci_set_drvdata(pdev, NULL); + free_c_can_dev(dev); + + pci_iounmap(pdev, priv->base); + pci_disable_msi(pdev); + pci_clear_master(pdev); + pci_release_regions(pdev); + pci_disable_device(pdev); +} + +static struct c_can_pci_data c_can_sta2x11= { + .type = C_CAN_DEVTYPE, + .reg_align = C_CAN_REG_ALIGN_32, + .freq = 52000000, /* 52 Mhz */ +}; + +#define C_CAN_ID(_vend, _dev, _driverdata) { \ + PCI_DEVICE(_vend, _dev), \ + .driver_data = (unsigned long)&_driverdata, \ +} +static DEFINE_PCI_DEVICE_TABLE(c_can_pci_tbl) = { + C_CAN_ID(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_CAN, + c_can_sta2x11), + {}, +}; +static struct pci_driver c_can_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = c_can_pci_tbl, + .probe = c_can_pci_probe, + .remove = __devexit_p(c_can_pci_remove), +}; + +module_pci_driver(c_can_pci_driver); + +MODULE_AUTHOR("Federico Vaga "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PCI CAN bus driver for Bosch C_CAN/D_CAN controller"); +MODULE_DEVICE_TABLE(pci, c_can_pci_tbl); -- cgit v1.2.3 From 1e0625facab2e871472472b7df87d8fbe6caf75a Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 13 Jun 2012 20:48:21 +0200 Subject: candev: add/update helpers for CAN FD - update sanity checks - add DLC to length conversion helpers - can_dlc2len() - get data length from can_dlc with sanitized can_dlc - can_len2dlc() - map the sanitized data length to an appropriate DLC Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index f03d7a481a80..239e4dd92ca1 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -33,6 +33,39 @@ MODULE_DESCRIPTION(MOD_DESC); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Wolfgang Grandegger "); +/* CAN DLC to real data length conversion helpers */ + +static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7, + 8, 12, 16, 20, 24, 32, 48, 64}; + +/* get data length from can_dlc with sanitized can_dlc */ +u8 can_dlc2len(u8 can_dlc) +{ + return dlc2len[can_dlc & 0x0F]; +} +EXPORT_SYMBOL_GPL(can_dlc2len); + +static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, /* 0 - 8 */ + 9, 9, 9, 9, /* 9 - 12 */ + 10, 10, 10, 10, /* 13 - 16 */ + 11, 11, 11, 11, /* 17 - 20 */ + 12, 12, 12, 12, /* 21 - 24 */ + 13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */ + 14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */ + 15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */ + 15, 15, 15, 15, 15, 15, 15, 15}; /* 57 - 64 */ + +/* map the sanitized data length to an appropriate data length code */ +u8 can_len2dlc(u8 len) +{ + if (unlikely(len > 64)) + return 0xF; + + return len2dlc[len]; +} +EXPORT_SYMBOL_GPL(can_len2dlc); + #ifdef CONFIG_CAN_CALC_BITTIMING #define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ @@ -454,7 +487,7 @@ EXPORT_SYMBOL_GPL(can_bus_off); static void can_setup(struct net_device *dev) { dev->type = ARPHRD_CAN; - dev->mtu = sizeof(struct can_frame); + dev->mtu = CAN_MTU; dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 10; -- cgit v1.2.3 From 41052ef6dfe90e7639103a010f49d13dadc55a28 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Wed, 13 Jun 2012 20:56:59 +0200 Subject: vcan: add CAN FD support - move the length calculation from dlc to real length (using canfd_frame) - allow to switch the driver between CAN and CAN FD (change of MTU) Signed-off-by: Oliver Hartkopp Signed-off-by: Marc Kleine-Budde --- drivers/net/can/vcan.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/vcan.c b/drivers/net/can/vcan.c index ea2d94285936..4f93c0be0053 100644 --- a/drivers/net/can/vcan.c +++ b/drivers/net/can/vcan.c @@ -70,13 +70,12 @@ MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)"); static void vcan_rx(struct sk_buff *skb, struct net_device *dev) { - struct can_frame *cf = (struct can_frame *)skb->data; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct net_device_stats *stats = &dev->stats; stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; + stats->rx_bytes += cfd->len; - skb->protocol = htons(ETH_P_CAN); skb->pkt_type = PACKET_BROADCAST; skb->dev = dev; skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -86,7 +85,7 @@ static void vcan_rx(struct sk_buff *skb, struct net_device *dev) static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) { - struct can_frame *cf = (struct can_frame *)skb->data; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct net_device_stats *stats = &dev->stats; int loop; @@ -94,7 +93,7 @@ static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; stats->tx_packets++; - stats->tx_bytes += cf->can_dlc; + stats->tx_bytes += cfd->len; /* set flag whether this packet has to be looped back */ loop = skb->pkt_type == PACKET_LOOPBACK; @@ -108,7 +107,7 @@ static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) * CAN core already did the echo for us */ stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; + stats->rx_bytes += cfd->len; } kfree_skb(skb); return NETDEV_TX_OK; @@ -133,14 +132,28 @@ static netdev_tx_t vcan_tx(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } +static int vcan_change_mtu(struct net_device *dev, int new_mtu) +{ + /* Do not allow changing the MTU while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + + if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + static const struct net_device_ops vcan_netdev_ops = { .ndo_start_xmit = vcan_tx, + .ndo_change_mtu = vcan_change_mtu, }; static void vcan_setup(struct net_device *dev) { dev->type = ARPHRD_CAN; - dev->mtu = sizeof(struct can_frame); + dev->mtu = CAN_MTU; dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = 0; -- cgit v1.2.3 From 8970b2e4393a34ddf5832f9c1568a88087b0d948 Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:22 +0000 Subject: bnx2x: Add support for external LB This change enables to do self-test with external loopback via ethtool. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 5 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 4 +- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 76 ++++++++++++++++++++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 5 ++ 4 files changed, 81 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 7de824184979..4335f9f51e6f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1817,6 +1817,7 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, #define LOAD_NORMAL 0 #define LOAD_OPEN 1 #define LOAD_DIAG 2 +#define LOAD_LOOPBACK_EXT 3 #define UNLOAD_NORMAL 0 #define UNLOAD_CLOSE 1 #define UNLOAD_RECOVERY 2 @@ -1900,12 +1901,14 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, #define PCICFG_LINK_SPEED_SHIFT 16 -#define BNX2X_NUM_TESTS 7 +#define BNX2X_NUM_TESTS 8 #define BNX2X_PHY_LOOPBACK 0 #define BNX2X_MAC_LOOPBACK 1 +#define BNX2X_EXT_LOOPBACK 2 #define BNX2X_PHY_LOOPBACK_FAILED 1 #define BNX2X_MAC_LOOPBACK_FAILED 2 +#define BNX2X_EXT_LOOPBACK_FAILED 3 #define BNX2X_LOOPBACK_FAILED (BNX2X_MAC_LOOPBACK_FAILED | \ BNX2X_PHY_LOOPBACK_FAILED) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 8098eea9704d..9370f5ffaf4a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2176,6 +2176,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) break; case LOAD_DIAG: + case LOAD_LOOPBACK_EXT: bp->state = BNX2X_STATE_DIAG; break; @@ -2215,7 +2216,8 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) return -EBUSY; } - bnx2x_dcbx_init(bp); + if (bp->state != BNX2X_STATE_DIAG) + bnx2x_dcbx_init(bp); return 0; #ifndef BNX2X_STOP_ON_ERROR diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index bf30e2829285..68fa902511dc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1538,7 +1538,8 @@ static const struct { } bnx2x_tests_str_arr[BNX2X_NUM_TESTS] = { { "register_test (offline)" }, { "memory_test (offline)" }, - { "loopback_test (offline)" }, + { "int_loopback_test (offline)" }, + { "ext_loopback_test (offline)" }, { "nvram_test (online)" }, { "interrupt_test (online)" }, { "link_test (online)" }, @@ -1943,6 +1944,14 @@ static void bnx2x_wait_for_link(struct bnx2x *bp, u8 link_up, u8 is_serdes) if (cnt <= 0 && bnx2x_link_test(bp, is_serdes)) DP(BNX2X_MSG_ETHTOOL, "Timeout waiting for link up\n"); + + cnt = 1400; + while (!bp->link_vars.link_up && cnt--) + msleep(20); + + if (cnt <= 0 && !bp->link_vars.link_up) + DP(BNX2X_MSG_ETHTOOL, + "Timeout waiting for link init\n"); } } @@ -1968,13 +1977,16 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode) u16 len; int rc = -ENODEV; u8 *data; - struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, txdata->txq_index); + struct netdev_queue *txq = netdev_get_tx_queue(bp->dev, + txdata->txq_index); /* check the loopback mode */ switch (loopback_mode) { case BNX2X_PHY_LOOPBACK: - if (bp->link_params.loopback_mode != LOOPBACK_XGXS) + if (bp->link_params.loopback_mode != LOOPBACK_XGXS) { + DP(BNX2X_MSG_ETHTOOL, "PHY loopback not supported\n"); return -EINVAL; + } break; case BNX2X_MAC_LOOPBACK: if (CHIP_IS_E3(bp)) { @@ -1991,6 +2003,13 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode) bnx2x_phy_init(&bp->link_params, &bp->link_vars); break; + case BNX2X_EXT_LOOPBACK: + if (bp->link_params.loopback_mode != LOOPBACK_EXT) { + DP(BNX2X_MSG_ETHTOOL, + "Can't configure external loopback\n"); + return -EINVAL; + } + break; default: DP(BNX2X_MSG_ETHTOOL, "Command parameters not supported\n"); return -EINVAL; @@ -2162,6 +2181,38 @@ static int bnx2x_test_loopback(struct bnx2x *bp) return rc; } +static int bnx2x_test_ext_loopback(struct bnx2x *bp) +{ + int rc; + u8 is_serdes = + (bp->link_vars.link_status & LINK_STATUS_SERDES_LINK) > 0; + + if (BP_NOMCP(bp)) + return -ENODEV; + + if (!netif_running(bp->dev)) + return BNX2X_EXT_LOOPBACK_FAILED; + + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + rc = bnx2x_nic_load(bp, LOAD_LOOPBACK_EXT); + if (rc) { + DP(BNX2X_MSG_ETHTOOL, + "Can't perform self-test, nic_load (for external lb) failed\n"); + return -ENODEV; + } + bnx2x_wait_for_link(bp, 1, is_serdes); + + bnx2x_netif_stop(bp, 1); + + rc = bnx2x_run_loopback(bp, BNX2X_EXT_LOOPBACK); + if (rc) + DP(BNX2X_MSG_ETHTOOL, "EXT loopback failed (res %d)\n", rc); + + bnx2x_netif_start(bp); + + return rc; +} + #define CRC32_RESIDUAL 0xdebb20e3 static int bnx2x_test_nvram(struct bnx2x *bp) @@ -2263,6 +2314,10 @@ static void bnx2x_self_test(struct net_device *dev, etest->flags |= ETH_TEST_FL_FAILED; return; } + DP(BNX2X_MSG_ETHTOOL, + "Self-test command parameters: offline = %d, external_lb = %d\n", + (etest->flags & ETH_TEST_FL_OFFLINE), + (etest->flags & ETH_TEST_FL_EXTERNAL_LB)>>2); memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS); @@ -2300,10 +2355,17 @@ static void bnx2x_self_test(struct net_device *dev, etest->flags |= ETH_TEST_FL_FAILED; } - buf[2] = bnx2x_test_loopback(bp); + buf[2] = bnx2x_test_loopback(bp); /* internal LB */ if (buf[2] != 0) etest->flags |= ETH_TEST_FL_FAILED; + if (etest->flags & ETH_TEST_FL_EXTERNAL_LB) { + buf[3] = bnx2x_test_ext_loopback(bp); /* external LB */ + if (buf[3] != 0) + etest->flags |= ETH_TEST_FL_FAILED; + etest->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; + } + bnx2x_nic_unload(bp, UNLOAD_NORMAL); /* restore input for TX port IF */ @@ -2314,16 +2376,16 @@ static void bnx2x_self_test(struct net_device *dev, bnx2x_wait_for_link(bp, link_up, is_serdes); } if (bnx2x_test_nvram(bp) != 0) { - buf[3] = 1; + buf[4] = 1; etest->flags |= ETH_TEST_FL_FAILED; } if (bnx2x_test_intr(bp) != 0) { - buf[4] = 1; + buf[5] = 1; etest->flags |= ETH_TEST_FL_FAILED; } if (bnx2x_link_test(bp, is_serdes) != 0) { - buf[5] = 1; + buf[6] = 1; etest->flags |= ETH_TEST_FL_FAILED; } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index a622bb7bf21d..cebc557f0b37 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -2124,6 +2124,11 @@ u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode) } } + if (load_mode == LOAD_LOOPBACK_EXT) { + struct link_params *lp = &bp->link_params; + lp->loopback_mode = LOOPBACK_EXT; + } + rc = bnx2x_phy_init(&bp->link_params, &bp->link_vars); bnx2x_release_phy_lock(bp); -- cgit v1.2.3 From cf2c1df62e065bfc15e38daf2d3479a56b320f29 Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:23 +0000 Subject: bnx2x: Return only online tests for MF 1. In multi-function device, show only the online tests in self-test results as only these test are performed (offline tests cannot be performed as they may corrupt the traffic of other functions on the same physical port). Note that multi-function mode cannot change while the driver is up. 2. Check result code in NIC load and act accordingly. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 6 +- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 85 +++++++++++++++------- 2 files changed, 62 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 4335f9f51e6f..8ee4bfc1b0bb 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1900,8 +1900,10 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, #define PCICFG_LINK_SPEED 0xf0000 #define PCICFG_LINK_SPEED_SHIFT 16 - -#define BNX2X_NUM_TESTS 8 +#define BNX2X_NUM_TESTS_SF 7 +#define BNX2X_NUM_TESTS_MF 3 +#define BNX2X_NUM_TESTS(bp) (IS_MF(bp) ? BNX2X_NUM_TESTS_MF : \ + BNX2X_NUM_TESTS_SF) #define BNX2X_PHY_LOOPBACK 0 #define BNX2X_MAC_LOOPBACK 1 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 68fa902511dc..ed2b49a0c5a5 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -826,7 +826,7 @@ static void bnx2x_get_drvinfo(struct net_device *dev, ((phy_fw_ver[0] != '\0') ? " phy " : ""), phy_fw_ver); strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); info->n_stats = BNX2X_NUM_STATS; - info->testinfo_len = BNX2X_NUM_TESTS; + info->testinfo_len = BNX2X_NUM_TESTS(bp); info->eedump_len = bp->common.flash_size; info->regdump_len = bnx2x_get_regs_len(dev); } @@ -1533,17 +1533,14 @@ static int bnx2x_set_pauseparam(struct net_device *dev, return 0; } -static const struct { - char string[ETH_GSTRING_LEN]; -} bnx2x_tests_str_arr[BNX2X_NUM_TESTS] = { - { "register_test (offline)" }, - { "memory_test (offline)" }, - { "int_loopback_test (offline)" }, - { "ext_loopback_test (offline)" }, - { "nvram_test (online)" }, - { "interrupt_test (online)" }, - { "link_test (online)" }, - { "idle check (online)" } +char *bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF] = { + "register_test (offline) ", + "memory_test (offline) ", + "int_loopback_test (offline)", + "ext_loopback_test (offline)", + "nvram_test (online) ", + "interrupt_test (online) ", + "link_test (online) " }; static u32 bnx2x_eee_to_adv(u32 eee_adv) @@ -2308,6 +2305,8 @@ static void bnx2x_self_test(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); u8 is_serdes; + int rc; + if (bp->recovery_state != BNX2X_RECOVERY_DONE) { netdev_err(bp->dev, "Handling parity error recovery. Try again later\n"); @@ -2319,17 +2318,18 @@ static void bnx2x_self_test(struct net_device *dev, (etest->flags & ETH_TEST_FL_OFFLINE), (etest->flags & ETH_TEST_FL_EXTERNAL_LB)>>2); - memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS); + memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS(bp)); - if (!netif_running(dev)) + if (!netif_running(dev)) { + DP(BNX2X_MSG_ETHTOOL, + "Can't perform self-test when interface is down\n"); return; + } - /* offline tests are not supported in MF mode */ - if (IS_MF(bp)) - etest->flags &= ~ETH_TEST_FL_OFFLINE; is_serdes = (bp->link_vars.link_status & LINK_STATUS_SERDES_LINK) > 0; - if (etest->flags & ETH_TEST_FL_OFFLINE) { + /* offline tests are not supported in MF mode */ + if ((etest->flags & ETH_TEST_FL_OFFLINE) && !IS_MF(bp)) { int port = BP_PORT(bp); u32 val; u8 link_up; @@ -2342,7 +2342,14 @@ static void bnx2x_self_test(struct net_device *dev, link_up = bp->link_vars.link_up; bnx2x_nic_unload(bp, UNLOAD_NORMAL); - bnx2x_nic_load(bp, LOAD_DIAG); + rc = bnx2x_nic_load(bp, LOAD_DIAG); + if (rc) { + etest->flags |= ETH_TEST_FL_FAILED; + DP(BNX2X_MSG_ETHTOOL, + "Can't perform self-test, nic_load (for offline) failed\n"); + return; + } + /* wait until link state is restored */ bnx2x_wait_for_link(bp, 1, is_serdes); @@ -2370,22 +2377,36 @@ static void bnx2x_self_test(struct net_device *dev, /* restore input for TX port IF */ REG_WR(bp, NIG_REG_EGRESS_UMP0_IN_EN + port*4, val); - - bnx2x_nic_load(bp, LOAD_NORMAL); + rc = bnx2x_nic_load(bp, LOAD_NORMAL); + if (rc) { + etest->flags |= ETH_TEST_FL_FAILED; + DP(BNX2X_MSG_ETHTOOL, + "Can't perform self-test, nic_load (for online) failed\n"); + return; + } /* wait until link state is restored */ bnx2x_wait_for_link(bp, link_up, is_serdes); } if (bnx2x_test_nvram(bp) != 0) { - buf[4] = 1; + if (!IS_MF(bp)) + buf[4] = 1; + else + buf[0] = 1; etest->flags |= ETH_TEST_FL_FAILED; } if (bnx2x_test_intr(bp) != 0) { - buf[5] = 1; + if (!IS_MF(bp)) + buf[5] = 1; + else + buf[1] = 1; etest->flags |= ETH_TEST_FL_FAILED; } if (bnx2x_link_test(bp, is_serdes) != 0) { - buf[6] = 1; + if (!IS_MF(bp)) + buf[6] = 1; + else + buf[2] = 1; etest->flags |= ETH_TEST_FL_FAILED; } @@ -2430,7 +2451,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset) return num_stats; case ETH_SS_TEST: - return BNX2X_NUM_TESTS; + return BNX2X_NUM_TESTS(bp); default: return -EINVAL; @@ -2440,7 +2461,7 @@ static int bnx2x_get_sset_count(struct net_device *dev, int stringset) static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) { struct bnx2x *bp = netdev_priv(dev); - int i, j, k; + int i, j, k, offset, start; char queue_name[MAX_QUEUE_NAME_LEN+1]; switch (stringset) { @@ -2471,7 +2492,17 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) break; case ETH_SS_TEST: - memcpy(buf, bnx2x_tests_str_arr, sizeof(bnx2x_tests_str_arr)); + /* First 4 tests cannot be done in MF mode */ + if (!IS_MF(bp)) + start = 0; + else + start = 4; + for (i = 0, j = start; j < (start + BNX2X_NUM_TESTS(bp)); + i++, j++) { + offset = sprintf(buf+32*i, "%s", + bnx2x_tests_str_arr[j]); + *(buf+offset) = '\0'; + } break; } } -- cgit v1.2.3 From 5d317c6a9597267643b6ce6593c4d1785eb1b8df Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:24 +0000 Subject: bnx2x: Add support for 4-tupple UDP RSS This change enables to control via ethtool whether to do UDP RSS on 2-tupple (IP source / destination only) or on 4-tupple (include UDP source / destination port). It also enables to read back the RSS configuration. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 16 +-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 8 +- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 136 ++++++++++++++++++++- drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c | 8 ++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h | 6 + 5 files changed, 159 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 9370f5ffaf4a..f4366f79c117 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1666,14 +1666,13 @@ static void bnx2x_set_rx_buf_size(struct bnx2x *bp) static int bnx2x_init_rss_pf(struct bnx2x *bp) { int i; - u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; u8 num_eth_queues = BNX2X_NUM_ETH_QUEUES(bp); /* Prepare the initial contents fo the indirection table if RSS is * enabled */ - for (i = 0; i < sizeof(ind_table); i++) - ind_table[i] = + for (i = 0; i < sizeof(bp->rss_conf_obj.ind_table); i++) + bp->rss_conf_obj.ind_table[i] = bp->fp->cl_id + ethtool_rxfh_indir_default(i, num_eth_queues); @@ -1685,12 +1684,11 @@ static int bnx2x_init_rss_pf(struct bnx2x *bp) * For 57712 and newer on the other hand it's a per-function * configuration. */ - return bnx2x_config_rss_eth(bp, ind_table, - bp->port.pmf || !CHIP_IS_E1x(bp)); + return bnx2x_config_rss_eth(bp, bp->port.pmf || !CHIP_IS_E1x(bp)); } int bnx2x_config_rss_pf(struct bnx2x *bp, struct bnx2x_rss_config_obj *rss_obj, - u8 *ind_table, bool config_hash) + bool config_hash) { struct bnx2x_config_rss_params params = {NULL}; int i; @@ -1713,11 +1711,15 @@ int bnx2x_config_rss_pf(struct bnx2x *bp, struct bnx2x_rss_config_obj *rss_obj, __set_bit(BNX2X_RSS_IPV4_TCP, ¶ms.rss_flags); __set_bit(BNX2X_RSS_IPV6, ¶ms.rss_flags); __set_bit(BNX2X_RSS_IPV6_TCP, ¶ms.rss_flags); + if (rss_obj->udp_rss_v4) + __set_bit(BNX2X_RSS_IPV4_UDP, ¶ms.rss_flags); + if (rss_obj->udp_rss_v6) + __set_bit(BNX2X_RSS_IPV6_UDP, ¶ms.rss_flags); /* Hash bits */ params.rss_result_mask = MULTI_MASK; - memcpy(params.ind_table, ind_table, sizeof(params.ind_table)); + memcpy(params.ind_table, rss_obj->ind_table, sizeof(params.ind_table)); if (config_hash) { /* RSS keys */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 7cd99b75347a..bb479843d59c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -94,7 +94,7 @@ void bnx2x_send_unload_done(struct bnx2x *bp); * @config_hash: re-configure RSS hash keys configuration */ int bnx2x_config_rss_pf(struct bnx2x *bp, struct bnx2x_rss_config_obj *rss_obj, - u8 *ind_table, bool config_hash); + bool config_hash); /** * bnx2x__init_func_obj - init function object @@ -865,11 +865,9 @@ static inline int func_by_vn(struct bnx2x *bp, int vn) return 2 * vn + BP_PORT(bp); } -static inline int bnx2x_config_rss_eth(struct bnx2x *bp, u8 *ind_table, - bool config_hash) +static inline int bnx2x_config_rss_eth(struct bnx2x *bp, bool config_hash) { - return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, ind_table, - config_hash); + return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, config_hash); } /** diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index ed2b49a0c5a5..d096813994b2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2600,6 +2600,41 @@ static int bnx2x_set_phys_id(struct net_device *dev, return 0; } +static int bnx2x_get_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) +{ + + switch (info->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + break; + case UDP_V4_FLOW: + if (bp->rss_conf_obj.udp_rss_v4) + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + else + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case UDP_V6_FLOW: + if (bp->rss_conf_obj.udp_rss_v6) + info->data = RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3; + else + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + case IPV4_FLOW: + case IPV6_FLOW: + info->data = RXH_IP_SRC | RXH_IP_DST; + break; + default: + info->data = 0; + break; + } + + return 0; +} + static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rules __always_unused) { @@ -2609,7 +2644,102 @@ static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, case ETHTOOL_GRXRINGS: info->data = BNX2X_NUM_ETH_QUEUES(bp); return 0; + case ETHTOOL_GRXFH: + return bnx2x_get_rss_flags(bp, info); + default: + DP(BNX2X_MSG_ETHTOOL, "Command parameters not supported\n"); + return -EOPNOTSUPP; + } +} + +static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info) +{ + int udp_rss_requested; + + DP(BNX2X_MSG_ETHTOOL, + "Set rss flags command parameters: flow type = %d, data = %llu\n", + info->flow_type, info->data); + + switch (info->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + /* For TCP only 4-tupple hash is supported */ + if (info->data ^ (RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + DP(BNX2X_MSG_ETHTOOL, + "Command parameters not supported\n"); + return -EINVAL; + } else { + return 0; + } + + case UDP_V4_FLOW: + case UDP_V6_FLOW: + /* For UDP either 2-tupple hash or 4-tupple hash is supported */ + if (info->data == (RXH_IP_SRC | RXH_IP_DST | + RXH_L4_B_0_1 | RXH_L4_B_2_3)) + udp_rss_requested = 1; + else if (info->data == (RXH_IP_SRC | RXH_IP_DST)) + udp_rss_requested = 0; + else + return -EINVAL; + if ((info->flow_type == UDP_V4_FLOW) && + (bp->rss_conf_obj.udp_rss_v4 != udp_rss_requested)) { + bp->rss_conf_obj.udp_rss_v4 = udp_rss_requested; + DP(BNX2X_MSG_ETHTOOL, + "rss re-configured, UDP 4-tupple %s\n", + udp_rss_requested ? "enabled" : "disabled"); + return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); + } else if ((info->flow_type == UDP_V6_FLOW) && + (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) { + bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested; + return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0); + DP(BNX2X_MSG_ETHTOOL, + "rss re-configured, UDP 4-tupple %s\n", + udp_rss_requested ? "enabled" : "disabled"); + } else { + return 0; + } + case IPV4_FLOW: + case IPV6_FLOW: + /* For IP only 2-tupple hash is supported */ + if (info->data ^ (RXH_IP_SRC | RXH_IP_DST)) { + DP(BNX2X_MSG_ETHTOOL, + "Command parameters not supported\n"); + return -EINVAL; + } else { + return 0; + } + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case SCTP_V6_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IP_USER_FLOW: + case ETHER_FLOW: + /* RSS is not supported for these protocols */ + if (info->data) { + DP(BNX2X_MSG_ETHTOOL, + "Command parameters not supported\n"); + return -EINVAL; + } else { + return 0; + } + default: + return -EINVAL; + } +} + +static int bnx2x_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) +{ + struct bnx2x *bp = netdev_priv(dev); + switch (info->cmd) { + case ETHTOOL_SRXFH: + return bnx2x_set_rss_flags(bp, info); default: DP(BNX2X_MSG_ETHTOOL, "Command parameters not supported\n"); return -EOPNOTSUPP; @@ -2649,7 +2779,6 @@ static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir) { struct bnx2x *bp = netdev_priv(dev); size_t i; - u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE] = {0}; for (i = 0; i < T_ETH_INDIRECTION_TABLE_SIZE; i++) { /* @@ -2661,10 +2790,10 @@ static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir) * align the received table to the Client ID of the leading RSS * queue */ - ind_table[i] = indir[i] + bp->fp->cl_id; + bp->rss_conf_obj.ind_table[i] = indir[i] + bp->fp->cl_id; } - return bnx2x_config_rss_eth(bp, ind_table, false); + return bnx2x_config_rss_eth(bp, false); } static const struct ethtool_ops bnx2x_ethtool_ops = { @@ -2694,6 +2823,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .set_phys_id = bnx2x_set_phys_id, .get_ethtool_stats = bnx2x_get_ethtool_stats, .get_rxnfc = bnx2x_get_rxnfc, + .set_rxnfc = bnx2x_set_rxnfc, .get_rxfh_indir_size = bnx2x_get_rxfh_indir_size, .get_rxfh_indir = bnx2x_get_rxfh_indir, .set_rxfh_indir = bnx2x_set_rxfh_indir, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 6c14b4a4e82c..734fd87cd990 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -4107,6 +4107,10 @@ static int bnx2x_setup_rss(struct bnx2x *bp, data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_IPV4_TCP_CAPABILITY; + if (test_bit(BNX2X_RSS_IPV4_UDP, &p->rss_flags)) + data->capabilities |= + ETH_RSS_UPDATE_RAMROD_DATA_IPV4_UDP_CAPABILITY; + if (test_bit(BNX2X_RSS_IPV6, &p->rss_flags)) data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_IPV6_CAPABILITY; @@ -4115,6 +4119,10 @@ static int bnx2x_setup_rss(struct bnx2x *bp, data->capabilities |= ETH_RSS_UPDATE_RAMROD_DATA_IPV6_TCP_CAPABILITY; + if (test_bit(BNX2X_RSS_IPV6_UDP, &p->rss_flags)) + data->capabilities |= + ETH_RSS_UPDATE_RAMROD_DATA_IPV6_UDP_CAPABILITY; + /* Hashing mask */ data->rss_result_mask = p->rss_result_mask; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h index efd80bdd0dfe..76818ef08f9b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h @@ -694,8 +694,10 @@ enum { BNX2X_RSS_IPV4, BNX2X_RSS_IPV4_TCP, + BNX2X_RSS_IPV4_UDP, BNX2X_RSS_IPV6, BNX2X_RSS_IPV6_TCP, + BNX2X_RSS_IPV6_UDP, }; struct bnx2x_config_rss_params { @@ -729,6 +731,10 @@ struct bnx2x_rss_config_obj { /* Last configured indirection table */ u8 ind_table[T_ETH_INDIRECTION_TABLE_SIZE]; + /* flags for enabling 4-tupple hash on UDP */ + u8 udp_rss_v4; + u8 udp_rss_v6; + int (*config_rss)(struct bnx2x *bp, struct bnx2x_config_rss_params *p); }; -- cgit v1.2.3 From a052997ea32164b2466daff8db5f783131184dae Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:25 +0000 Subject: bnx2x: Allow more than 64 L2 CIDs With increased number of RSS queues, each multiplied by the number of traffic- classes, we may have up to 64*3=192 CIDs. The current driver scheme with regard to context allocation supports only 64 CIDs. The new scheme enables scatter- gatehr list of pages for the context. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 10 ++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 67 ++++++++++++++++++------ 2 files changed, 57 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 8ee4bfc1b0bb..c0c539fd2490 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -978,8 +978,8 @@ union cdu_context { }; /* CDU host DB constants */ -#define CDU_ILT_PAGE_SZ_HW 3 -#define CDU_ILT_PAGE_SZ (8192 << CDU_ILT_PAGE_SZ_HW) /* 64K */ +#define CDU_ILT_PAGE_SZ_HW 2 +#define CDU_ILT_PAGE_SZ (8192 << CDU_ILT_PAGE_SZ_HW) /* 32K */ #define ILT_PAGE_CIDS (CDU_ILT_PAGE_SZ / sizeof(union cdu_context)) #ifdef BCM_CNIC @@ -1420,7 +1420,11 @@ struct bnx2x { dma_addr_t fw_stats_data_mapping; int fw_stats_data_sz; - struct hw_context context; + /* For max 196 cids (64*3 + non-eth), 32KB ILT page size and 1KB + * context size we need 8 ILT entries. + */ +#define ILT_MAX_L2_LINES 8 + struct hw_context context[ILT_MAX_L2_LINES]; struct bnx2x_ilt *ilt; #define BP_ILT(bp) ((bp)->ilt) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index cebc557f0b37..9b216e64c4fc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -7068,12 +7068,10 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) cdu_ilt_start = ilt->clients[ILT_CLIENT_CDU].start; for (i = 0; i < L2_ILT_LINES(bp); i++) { - ilt->lines[cdu_ilt_start + i].page = - bp->context.vcxt + (ILT_PAGE_CIDS * i); + ilt->lines[cdu_ilt_start + i].page = bp->context[i].vcxt; ilt->lines[cdu_ilt_start + i].page_mapping = - bp->context.cxt_mapping + (CDU_ILT_PAGE_SZ * i); - /* cdu ilt pages are allocated manually so there's no need to - set the size */ + bp->context[i].cxt_mapping; + ilt->lines[cdu_ilt_start + i].size = bp->context[i].size; } bnx2x_ilt_init_op(bp, INITOP_SET); @@ -7340,6 +7338,8 @@ static int bnx2x_init_hw_func(struct bnx2x *bp) void bnx2x_free_mem(struct bnx2x *bp) { + int i; + /* fastpath */ bnx2x_free_fp_mem(bp); /* end of fastpath */ @@ -7353,9 +7353,9 @@ void bnx2x_free_mem(struct bnx2x *bp) BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping, sizeof(struct bnx2x_slowpath)); - BNX2X_PCI_FREE(bp->context.vcxt, bp->context.cxt_mapping, - bp->context.size); - + for (i = 0; i < L2_ILT_LINES(bp); i++) + BNX2X_PCI_FREE(bp->context[i].vcxt, bp->context[i].cxt_mapping, + bp->context[i].size); bnx2x_ilt_mem_op(bp, ILT_MEMOP_FREE); BNX2X_FREE(bp->ilt->lines); @@ -7441,6 +7441,8 @@ alloc_mem_err: int bnx2x_alloc_mem(struct bnx2x *bp) { + int i, allocated, context_size; + #ifdef BCM_CNIC if (!CHIP_IS_E1x(bp)) /* size = the status block + ramrod buffers */ @@ -7470,11 +7472,29 @@ int bnx2x_alloc_mem(struct bnx2x *bp) if (bnx2x_alloc_fw_stats_mem(bp)) goto alloc_mem_err; - bp->context.size = sizeof(union cdu_context) * BNX2X_L2_CID_COUNT(bp); - - BNX2X_PCI_ALLOC(bp->context.vcxt, &bp->context.cxt_mapping, - bp->context.size); + /* Allocate memory for CDU context: + * This memory is allocated separately and not in the generic ILT + * functions because CDU differs in few aspects: + * 1. There are multiple entities allocating memory for context - + * 'regular' driver, CNIC and SRIOV driver. Each separately controls + * its own ILT lines. + * 2. Since CDU page-size is not a single 4KB page (which is the case + * for the other ILT clients), to be efficient we want to support + * allocation of sub-page-size in the last entry. + * 3. Context pointers are used by the driver to pass to FW / update + * the context (for the other ILT clients the pointers are used just to + * free the memory during unload). + */ + context_size = sizeof(union cdu_context) * BNX2X_L2_CID_COUNT(bp); + for (i = 0, allocated = 0; allocated < context_size; i++) { + bp->context[i].size = min(CDU_ILT_PAGE_SZ, + (context_size - allocated)); + BNX2X_PCI_ALLOC(bp->context[i].vcxt, + &bp->context[i].cxt_mapping, + bp->context[i].size); + allocated += bp->context[i].size; + } BNX2X_ALLOC(bp->ilt->lines, sizeof(struct ilt_line) * ILT_MAX_LINES); if (bnx2x_ilt_mem_op(bp, ILT_MEMOP_ALLOC)) @@ -7748,6 +7768,8 @@ static void bnx2x_pf_q_prep_init(struct bnx2x *bp, { u8 cos; + int cxt_index, cxt_offset; + /* FCoE Queue uses Default SB, thus has no HC capabilities */ if (!IS_FCOE_FP(fp)) { __set_bit(BNX2X_Q_FLG_HC, &init_params->rx.flags); @@ -7784,9 +7806,13 @@ static void bnx2x_pf_q_prep_init(struct bnx2x *bp, fp->index, init_params->max_cos); /* set the context pointers queue object */ - for (cos = FIRST_TX_COS_INDEX; cos < init_params->max_cos; cos++) + for (cos = FIRST_TX_COS_INDEX; cos < init_params->max_cos; cos++) { + cxt_index = fp->txdata[cos].cid / ILT_PAGE_CIDS; + cxt_offset = fp->txdata[cos].cid - (cxt_index * + ILT_PAGE_CIDS); init_params->cxts[cos] = - &bp->context.vcxt[fp->txdata[cos].cid].eth; + &bp->context[cxt_index].vcxt[cxt_offset].eth; + } } int bnx2x_setup_tx_only(struct bnx2x *bp, struct bnx2x_fastpath *fp, @@ -12202,6 +12228,7 @@ static int bnx2x_set_iscsi_eth_mac_addr(struct bnx2x *bp) static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count) { struct eth_spe *spe; + int cxt_index, cxt_offset; #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) @@ -12224,10 +12251,16 @@ static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count) * ramrod */ if (type == ETH_CONNECTION_TYPE) { - if (cmd == RAMROD_CMD_ID_ETH_CLIENT_SETUP) - bnx2x_set_ctx_validation(bp, &bp->context. - vcxt[BNX2X_ISCSI_ETH_CID].eth, + if (cmd == RAMROD_CMD_ID_ETH_CLIENT_SETUP) { + cxt_index = BNX2X_ISCSI_ETH_CID / + ILT_PAGE_CIDS; + cxt_offset = BNX2X_ISCSI_ETH_CID - + (cxt_index * ILT_PAGE_CIDS); + bnx2x_set_ctx_validation(bp, + &bp->context[cxt_index]. + vcxt[cxt_offset].eth, BNX2X_ISCSI_ETH_CID); + } } /* -- cgit v1.2.3 From 65565884fba67d9254f32d239b0fb6c38fae88aa Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:26 +0000 Subject: bnx2x: Make the transmission queues adjacent In the current scheme the transmission queues of traffic-class 0 were 0-15, the transmission queues of traffic-class 1 were 16-31 and so on. If the number of RSS queues was smaller than 16, there were gaps in transmission queues numbering, as well as in CIDs numbering. This is both a waste (especially when 16 is increased to 64), and may causes problems with flushing queues when reducing the number of RSS queues (using ethtool -L). The new scheme eliminates the gaps. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 66 +++++++------ drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 105 +++++++++++++-------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 14 +-- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 42 +++++---- drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c | 4 +- 6 files changed, 139 insertions(+), 94 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index c0c539fd2490..d06064b0d063 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -276,29 +276,32 @@ enum { #define FIRST_TX_ONLY_COS_INDEX 1 #define FIRST_TX_COS_INDEX 0 -/* defines for decodeing the fastpath index and the cos index out of the - * transmission queue index - */ #define MAX_TXQS_PER_COS FP_SB_MAX_E1x -#define TXQ_TO_FP(txq_index) ((txq_index) % MAX_TXQS_PER_COS) -#define TXQ_TO_COS(txq_index) ((txq_index) / MAX_TXQS_PER_COS) - /* rules for calculating the cids of tx-only connections */ -#define CID_TO_FP(cid) ((cid) % MAX_TXQS_PER_COS) -#define CID_COS_TO_TX_ONLY_CID(cid, cos) (cid + cos * MAX_TXQS_PER_COS) +#define CID_TO_FP(cid, bp) ((cid) % BNX2X_NUM_NON_CNIC_QUEUES(bp)) +#define CID_COS_TO_TX_ONLY_CID(cid, cos, bp) \ + (cid + cos * BNX2X_NUM_NON_CNIC_QUEUES(bp)) /* fp index inside class of service range */ -#define FP_COS_TO_TXQ(fp, cos) ((fp)->index + cos * MAX_TXQS_PER_COS) - -/* - * 0..15 eth cos0 - * 16..31 eth cos1 if applicable - * 32..47 eth cos2 If applicable - * fcoe queue follows eth queues (16, 32, 48 depending on cos) +#define FP_COS_TO_TXQ(fp, cos, bp) \ + ((fp)->index + cos * BNX2X_NUM_NON_CNIC_QUEUES(bp)) + +/* Indexes for transmission queues array: + * txdata for RSS i CoS j is at location i + (j * num of RSS) + * txdata for FCoE (if exist) is at location max cos * num of RSS + * txdata for FWD (if exist) is one location after FCoE + * txdata for OOO (if exist) is one location after FWD */ -#define MAX_ETH_TXQ_IDX(bp) (MAX_TXQS_PER_COS * (bp)->max_cos) -#define FCOE_TXQ_IDX(bp) (MAX_ETH_TXQ_IDX(bp)) +enum { + FCOE_TXQ_IDX_OFFSET, + FWD_TXQ_IDX_OFFSET, + OOO_TXQ_IDX_OFFSET, +}; +#define MAX_ETH_TXQ_IDX(bp) (BNX2X_NUM_NON_CNIC_QUEUES(bp) * (bp)->max_cos) +#ifdef BCM_CNIC +#define FCOE_TXQ_IDX(bp) (MAX_ETH_TXQ_IDX(bp) + FCOE_TXQ_IDX_OFFSET) +#endif /* fast path */ /* @@ -481,6 +484,8 @@ struct bnx2x_fp_txdata { __le16 *tx_cons_sb; int txq_index; + struct bnx2x_fastpath *parent_fp; + int tx_ring_size; }; enum bnx2x_tpa_mode_t { @@ -507,7 +512,7 @@ struct bnx2x_fastpath { enum bnx2x_tpa_mode_t mode; u8 max_cos; /* actual number of active tx coses */ - struct bnx2x_fp_txdata txdata[BNX2X_MULTI_TX_COS]; + struct bnx2x_fp_txdata *txdata_ptr[BNX2X_MULTI_TX_COS]; struct sw_rx_bd *rx_buf_ring; /* BDs mappings ring */ struct sw_rx_page *rx_page_ring; /* SGE pages mappings ring */ @@ -579,19 +584,22 @@ struct bnx2x_fastpath { /* Use 2500 as a mini-jumbo MTU for FCoE */ #define BNX2X_FCOE_MINI_JUMBO_MTU 2500 -/* FCoE L2 `fastpath' entry is right after the eth entries */ -#define FCOE_IDX BNX2X_NUM_ETH_QUEUES(bp) -#define bnx2x_fcoe_fp(bp) (&bp->fp[FCOE_IDX]) -#define bnx2x_fcoe(bp, var) (bnx2x_fcoe_fp(bp)->var) -#define bnx2x_fcoe_tx(bp, var) (bnx2x_fcoe_fp(bp)-> \ - txdata[FIRST_TX_COS_INDEX].var) +#define FCOE_IDX_OFFSET 0 + +#define FCOE_IDX(bp) (BNX2X_NUM_NON_CNIC_QUEUES(bp) + \ + FCOE_IDX_OFFSET) +#define bnx2x_fcoe_fp(bp) (&bp->fp[FCOE_IDX(bp)]) +#define bnx2x_fcoe(bp, var) (bnx2x_fcoe_fp(bp)->var) +#define bnx2x_fcoe_tx(bp, var) (bnx2x_fcoe_fp(bp)-> \ + txdata_ptr[FIRST_TX_COS_INDEX] \ + ->var) #define IS_ETH_FP(fp) (fp->index < \ BNX2X_NUM_ETH_QUEUES(fp->bp)) #ifdef BCM_CNIC -#define IS_FCOE_FP(fp) (fp->index == FCOE_IDX) -#define IS_FCOE_IDX(idx) ((idx) == FCOE_IDX) +#define IS_FCOE_FP(fp) (fp->index == FCOE_IDX(fp->bp)) +#define IS_FCOE_IDX(idx) ((idx) == FCOE_IDX(bp)) #else #define IS_FCOE_FP(fp) false #define IS_FCOE_IDX(idx) false @@ -1187,6 +1195,8 @@ struct bnx2x { * are grouped together in the beginning of the structure */ struct bnx2x_fastpath *fp; + struct bnx2x_fp_txdata *bnx2x_txq; + int bnx2x_txq_size; void __iomem *regview; void __iomem *doorbells; u16 db_size; @@ -1389,6 +1399,7 @@ struct bnx2x { u8 igu_dsb_id; u8 igu_base_sb; u8 igu_sb_cnt; + dma_addr_t def_status_blk_mapping; struct bnx2x_slowpath *slowpath; @@ -1443,7 +1454,6 @@ struct bnx2x { NON_ETH_CONTEXT_USE + CNIC_PRESENT) #define L2_ILT_LINES(bp) (DIV_ROUND_UP(BNX2X_L2_CID_COUNT(bp),\ ILT_PAGE_CIDS)) -#define BNX2X_DB_SIZE(bp) (BNX2X_L2_CID_COUNT(bp) * (1 << BNX2X_DB_SHIFT)) int qm_cid_count; @@ -1602,6 +1612,8 @@ struct bnx2x { extern int num_queues; #define BNX2X_NUM_QUEUES(bp) (bp->num_queues) #define BNX2X_NUM_ETH_QUEUES(bp) (BNX2X_NUM_QUEUES(bp) - NON_ETH_CONTEXT_USE) +#define BNX2X_NUM_NON_CNIC_QUEUES(bp) (BNX2X_NUM_QUEUES(bp) - \ + NON_ETH_CONTEXT_USE) #define BNX2X_NUM_RX_QUEUES(bp) BNX2X_NUM_QUEUES(bp) #define is_multi(bp) (BNX2X_NUM_QUEUES(bp) > 1) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index f4366f79c117..d99f20ace9df 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -40,12 +40,15 @@ * Makes sure the contents of the bp->fp[to].napi is kept * intact. This is done by first copying the napi struct from * the target to the source, and then mem copying the entire - * source onto the target + * source onto the target. Update txdata pointers and related + * content. */ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) { struct bnx2x_fastpath *from_fp = &bp->fp[from]; struct bnx2x_fastpath *to_fp = &bp->fp[to]; + int old_max_eth_txqs, new_max_eth_txqs; + int old_txdata_index = 0, new_txdata_index = 0; /* Copy the NAPI object as it has been already initialized */ from_fp->napi = to_fp->napi; @@ -53,6 +56,24 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) /* Move bnx2x_fastpath contents */ memcpy(to_fp, from_fp, sizeof(*to_fp)); to_fp->index = to; + + /* Update txdata pointers in fp and move txdata content accordingly: + * Each fp consumes 'max_cos' txdata structures, so the index should be + * decremented by max_cos x delta. + */ + + old_max_eth_txqs = BNX2X_NUM_ETH_QUEUES(bp) * (bp)->max_cos; + new_max_eth_txqs = (BNX2X_NUM_ETH_QUEUES(bp) - from + to) * + (bp)->max_cos; + if (from == FCOE_IDX(bp)) { + old_txdata_index = old_max_eth_txqs + FCOE_TXQ_IDX_OFFSET; + new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET; + } + + memcpy(&bp->bnx2x_txq[old_txdata_index], + &bp->bnx2x_txq[new_txdata_index], + sizeof(struct bnx2x_fp_txdata)); + to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index]; } int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */ @@ -888,7 +909,7 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) prefetch(fp->rx_cons_sb); for_each_cos_in_tx_queue(fp, cos) - prefetch(fp->txdata[cos].tx_cons_sb); + prefetch(fp->txdata_ptr[cos]->tx_cons_sb); prefetch(&fp->sb_running_index[SM_RX_ID]); napi_schedule(&bnx2x_fp(bp, fp->index, napi)); @@ -1205,7 +1226,7 @@ static void bnx2x_free_tx_skbs(struct bnx2x *bp) for_each_tx_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; for_each_cos_in_tx_queue(fp, cos) { - struct bnx2x_fp_txdata *txdata = &fp->txdata[cos]; + struct bnx2x_fp_txdata *txdata = fp->txdata_ptr[cos]; unsigned pkts_compl = 0, bytes_compl = 0; u16 sw_prod = txdata->tx_pkt_prod; @@ -1217,7 +1238,8 @@ static void bnx2x_free_tx_skbs(struct bnx2x *bp) sw_cons++; } netdev_tx_reset_queue( - netdev_get_tx_queue(bp->dev, txdata->txq_index)); + netdev_get_tx_queue(bp->dev, + txdata->txq_index)); } } } @@ -1579,6 +1601,8 @@ void bnx2x_set_num_queues(struct bnx2x *bp) #endif /* Add special queues */ bp->num_queues += NON_ETH_CONTEXT_USE; + + BNX2X_DEV_INFO("set number of queues to %d\n", bp->num_queues); } /** @@ -1607,8 +1631,8 @@ static int bnx2x_set_real_num_queues(struct bnx2x *bp) { int rc, tx, rx; - tx = MAX_TXQS_PER_COS * bp->max_cos; - rx = BNX2X_NUM_ETH_QUEUES(bp); + tx = BNX2X_NUM_ETH_QUEUES(bp) * bp->max_cos; + rx = BNX2X_NUM_QUEUES(bp) - NON_ETH_CONTEXT_USE; /* account for fcoe queue */ #ifdef BCM_CNIC @@ -1853,6 +1877,7 @@ bool bnx2x_test_firmware_version(struct bnx2x *bp, bool is_err) static void bnx2x_bz_fp(struct bnx2x *bp, int index) { struct bnx2x_fastpath *fp = &bp->fp[index]; + int cos; struct napi_struct orig_napi = fp->napi; /* bzero bnx2x_fastpath contents */ if (bp->stats_init) @@ -1902,6 +1927,16 @@ static void bnx2x_bz_fp(struct bnx2x *bp, int index) /* Special queues support only one CoS */ fp->max_cos = 1; + /* Init txdata pointers */ +#ifdef BCM_CNIC + if (IS_FCOE_FP(fp)) + fp->txdata_ptr[0] = &bp->bnx2x_txq[FCOE_TXQ_IDX(bp)]; +#endif + if (IS_ETH_FP(fp)) + for_each_cos_in_tx_queue(fp, cos) + fp->txdata_ptr[cos] = &bp->bnx2x_txq[cos * + BNX2X_NUM_ETH_QUEUES(bp) + index]; + /* * set the tpa flag for each queue. The tpa flag determines the queue * minimal size so it must be set prior to queue memory allocation @@ -1951,11 +1986,13 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) /* * Zero fastpath structures preserving invariants like napi, which are * allocated only once, fp index, max_cos, bp pointer. - * Also set fp->disable_tpa. + * Also set fp->disable_tpa and txdata_ptr. */ DP(NETIF_MSG_IFUP, "num queues: %d", bp->num_queues); for_each_queue(bp, i) bnx2x_bz_fp(bp, i); + memset(bp->bnx2x_txq, 0, bp->bnx2x_txq_size * + sizeof(struct bnx2x_fp_txdata)); /* Set the receive queues buffer size */ @@ -2302,6 +2339,7 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) /* Stop Tx */ bnx2x_tx_disable(bp); + netdev_reset_tc(bp->dev); #ifdef BCM_CNIC bnx2x_cnic_notify(bp, CNIC_CTL_STOP_CMD); @@ -2460,8 +2498,8 @@ int bnx2x_poll(struct napi_struct *napi, int budget) #endif for_each_cos_in_tx_queue(fp, cos) - if (bnx2x_tx_queue_has_work(&fp->txdata[cos])) - bnx2x_tx_int(bp, &fp->txdata[cos]); + if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) + bnx2x_tx_int(bp, fp->txdata_ptr[cos]); if (bnx2x_has_rx_work(fp)) { @@ -2838,7 +2876,6 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); - struct bnx2x_fastpath *fp; struct netdev_queue *txq; struct bnx2x_fp_txdata *txdata; struct sw_tx_bd *tx_buf; @@ -2848,7 +2885,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) struct eth_tx_parse_bd_e2 *pbd_e2 = NULL; u32 pbd_e2_parsing_data = 0; u16 pkt_prod, bd_prod; - int nbd, txq_index, fp_index, txdata_index; + int nbd, txq_index; dma_addr_t mapping; u32 xmit_type = bnx2x_xmit_type(bp, skb); int i; @@ -2867,31 +2904,12 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) BUG_ON(txq_index >= MAX_ETH_TXQ_IDX(bp) + FCOE_PRESENT); - /* decode the fastpath index and the cos index from the txq */ - fp_index = TXQ_TO_FP(txq_index); - txdata_index = TXQ_TO_COS(txq_index); - -#ifdef BCM_CNIC - /* - * Override the above for the FCoE queue: - * - FCoE fp entry is right after the ETH entries. - * - FCoE L2 queue uses bp->txdata[0] only. - */ - if (unlikely(!NO_FCOE(bp) && (txq_index == - bnx2x_fcoe_tx(bp, txq_index)))) { - fp_index = FCOE_IDX; - txdata_index = 0; - } -#endif + txdata = &bp->bnx2x_txq[txq_index]; /* enable this debug print to view the transmission queue being used DP(NETIF_MSG_TX_QUEUED, "indices: txq %d, fp %d, txdata %d\n", txq_index, fp_index, txdata_index); */ - /* locate the fastpath and the txdata */ - fp = &bp->fp[fp_index]; - txdata = &fp->txdata[txdata_index]; - /* enable this debug print to view the tranmission details DP(NETIF_MSG_TX_QUEUED, "transmitting packet cid %d fp index %d txdata_index %d tx_data ptr %p fp pointer %p\n", @@ -2899,7 +2917,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(bnx2x_tx_avail(bp, txdata) < (skb_shinfo(skb)->nr_frags + 3))) { - fp->eth_q_stats.driver_xoff++; + txdata->parent_fp->eth_q_stats.driver_xoff++; netif_tx_stop_queue(txq); BNX2X_ERR("BUG! Tx ring full when queue awake!\n"); return NETDEV_TX_BUSY; @@ -3181,7 +3199,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) * fp->bd_tx_cons */ smp_mb(); - fp->eth_q_stats.driver_xoff++; + txdata->parent_fp->eth_q_stats.driver_xoff++; if (bnx2x_tx_avail(bp, txdata) >= MAX_SKB_FRAGS + 4) netif_tx_wake_queue(txq); } @@ -3247,7 +3265,7 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) /* configure traffic class to transmission queue mapping */ for (cos = 0; cos < bp->max_cos; cos++) { count = BNX2X_NUM_ETH_QUEUES(bp); - offset = cos * MAX_TXQS_PER_COS; + offset = cos * BNX2X_NUM_NON_CNIC_QUEUES(bp); netdev_set_tc_queue(dev, cos, count, offset); DP(BNX2X_MSG_SP | NETIF_MSG_IFUP, "mapping tc %d to offset %d count %d\n", @@ -3346,7 +3364,7 @@ static void bnx2x_free_fp_mem_at(struct bnx2x *bp, int fp_index) if (!skip_tx_queue(bp, fp_index)) { /* fastpath tx rings: tx_buf tx_desc */ for_each_cos_in_tx_queue(fp, cos) { - struct bnx2x_fp_txdata *txdata = &fp->txdata[cos]; + struct bnx2x_fp_txdata *txdata = fp->txdata_ptr[cos]; DP(NETIF_MSG_IFDOWN, "freeing tx memory of fp %d cos %d cid %d\n", @@ -3503,7 +3521,7 @@ static int bnx2x_alloc_fp_mem_at(struct bnx2x *bp, int index) if (!skip_tx_queue(bp, index)) { /* fastpath tx rings: tx_buf tx_desc */ for_each_cos_in_tx_queue(fp, cos) { - struct bnx2x_fp_txdata *txdata = &fp->txdata[cos]; + struct bnx2x_fp_txdata *txdata = fp->txdata_ptr[cos]; DP(NETIF_MSG_IFUP, "allocating tx memory of fp %d cos %d\n", @@ -3586,7 +3604,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) #ifdef BCM_CNIC if (!NO_FCOE(bp)) /* FCoE */ - if (bnx2x_alloc_fp_mem_at(bp, FCOE_IDX)) + if (bnx2x_alloc_fp_mem_at(bp, FCOE_IDX(bp))) /* we will fail load process instead of mark * NO_FCOE_FLAG */ @@ -3611,7 +3629,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) */ /* move FCoE fp even NO_FCOE_FLAG is on */ - bnx2x_move_fp(bp, FCOE_IDX, FCOE_IDX - delta); + bnx2x_move_fp(bp, FCOE_IDX(bp), FCOE_IDX(bp) - delta); #endif bp->num_queues -= delta; BNX2X_ERR("Adjusted num of queues from %d to %d\n", @@ -3624,6 +3642,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) void bnx2x_free_mem_bp(struct bnx2x *bp) { kfree(bp->fp); + kfree(bp->bnx2x_txq); kfree(bp->msix_table); kfree(bp->ilt); } @@ -3648,6 +3667,16 @@ int __devinit bnx2x_alloc_mem_bp(struct bnx2x *bp) goto alloc_err; bp->fp = fp; + /* Allocate memory for the transmission queues array */ + bp->bnx2x_txq_size = BNX2X_MAX_RSS_COUNT(bp) * BNX2X_MULTI_TX_COS; +#ifdef BCM_CNIC + bp->bnx2x_txq_size++; +#endif + bp->bnx2x_txq = kcalloc(bp->bnx2x_txq_size, + sizeof(struct bnx2x_fp_txdata), GFP_KERNEL); + if (!bp->bnx2x_txq) + goto alloc_err; + /* msix table */ tbl = kcalloc(msix_table_size, sizeof(*tbl), GFP_KERNEL); if (!tbl) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index bb479843d59c..112ffccb3121 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -728,7 +728,7 @@ static inline bool bnx2x_has_tx_work(struct bnx2x_fastpath *fp) { u8 cos; for_each_cos_in_tx_queue(fp, cos) - if (bnx2x_tx_queue_has_work(&fp->txdata[cos])) + if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) return true; return false; } @@ -1066,12 +1066,14 @@ static inline u32 bnx2x_rx_ustorm_prods_offset(struct bnx2x_fastpath *fp) } static inline void bnx2x_init_txdata(struct bnx2x *bp, - struct bnx2x_fp_txdata *txdata, u32 cid, int txq_index, - __le16 *tx_cons_sb) + struct bnx2x_fp_txdata *txdata, u32 cid, + int txq_index, __le16 *tx_cons_sb, + struct bnx2x_fastpath *fp) { txdata->cid = cid; txdata->txq_index = txq_index; txdata->tx_cons_sb = tx_cons_sb; + txdata->parent_fp = fp; DP(NETIF_MSG_IFUP, "created tx data cid %d, txq %d\n", txdata->cid, txdata->txq_index); @@ -1114,9 +1116,9 @@ static inline void bnx2x_init_fcoe_fp(struct bnx2x *bp) bnx2x_fcoe(bp, fw_sb_id) = DEF_SB_ID; bnx2x_fcoe(bp, igu_sb_id) = bp->igu_dsb_id; bnx2x_fcoe(bp, rx_cons_sb) = BNX2X_FCOE_L2_RX_INDEX; - - bnx2x_init_txdata(bp, &bnx2x_fcoe(bp, txdata[0]), - fp->cid, FCOE_TXQ_IDX(bp), BNX2X_FCOE_L2_TX_INDEX); + bnx2x_init_txdata(bp, bnx2x_fcoe(bp, txdata_ptr[0]), + fp->cid, FCOE_TXQ_IDX(bp), BNX2X_FCOE_L2_TX_INDEX, + fp); DP(NETIF_MSG_IFUP, "created fcoe tx data (fp index %d)\n", fp->index); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index d096813994b2..b46822356a1c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1959,7 +1959,7 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode) unsigned char *packet; struct bnx2x_fastpath *fp_rx = &bp->fp[0]; struct bnx2x_fastpath *fp_tx = &bp->fp[0]; - struct bnx2x_fp_txdata *txdata = &fp_tx->txdata[0]; + struct bnx2x_fp_txdata *txdata = fp_tx->txdata_ptr[0]; u16 tx_start_idx, tx_idx; u16 rx_start_idx, rx_idx; u16 pkt_prod, bd_prod; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 9b216e64c4fc..e1c011e188aa 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -758,7 +758,7 @@ void bnx2x_panic_dump(struct bnx2x *bp) /* Tx */ for_each_cos_in_tx_queue(fp, cos) { - txdata = fp->txdata[cos]; + txdata = *fp->txdata_ptr[cos]; BNX2X_ERR("fp%d: tx_pkt_prod(0x%x) tx_pkt_cons(0x%x) tx_bd_prod(0x%x) tx_bd_cons(0x%x) *tx_cons_sb(0x%x)\n", i, txdata.tx_pkt_prod, txdata.tx_pkt_cons, txdata.tx_bd_prod, @@ -876,7 +876,7 @@ void bnx2x_panic_dump(struct bnx2x *bp) for_each_tx_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; for_each_cos_in_tx_queue(fp, cos) { - struct bnx2x_fp_txdata *txdata = &fp->txdata[cos]; + struct bnx2x_fp_txdata *txdata = fp->txdata_ptr[cos]; start = TX_BD(le16_to_cpu(*txdata->tx_cons_sb) - 10); end = TX_BD(le16_to_cpu(*txdata->tx_cons_sb) + 245); @@ -1710,7 +1710,7 @@ irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) /* Handle Rx or Tx according to SB id */ prefetch(fp->rx_cons_sb); for_each_cos_in_tx_queue(fp, cos) - prefetch(fp->txdata[cos].tx_cons_sb); + prefetch(fp->txdata_ptr[cos]->tx_cons_sb); prefetch(&fp->sb_running_index[SM_RX_ID]); napi_schedule(&bnx2x_fp(bp, fp->index, napi)); status &= ~mask; @@ -2921,7 +2921,7 @@ static void bnx2x_pf_tx_q_prep(struct bnx2x *bp, struct bnx2x_fastpath *fp, struct bnx2x_txq_setup_params *txq_init, u8 cos) { - txq_init->dscr_map = fp->txdata[cos].tx_desc_mapping; + txq_init->dscr_map = fp->txdata_ptr[cos]->tx_desc_mapping; txq_init->sb_cq_index = HC_INDEX_ETH_FIRST_TX_CQ_CONS + cos; txq_init->traffic_type = LLFC_TRAFFIC_TYPE_NW; txq_init->fw_sb_id = fp->fw_sb_id; @@ -3068,11 +3068,11 @@ static void bnx2x_drv_info_fcoe_stat(struct bnx2x *bp) /* insert FCoE stats from ramrod response */ if (!NO_FCOE(bp)) { struct tstorm_per_queue_stats *fcoe_q_tstorm_stats = - &bp->fw_stats_data->queue_stats[FCOE_IDX]. + &bp->fw_stats_data->queue_stats[FCOE_IDX(bp)]. tstorm_queue_statistics; struct xstorm_per_queue_stats *fcoe_q_xstorm_stats = - &bp->fw_stats_data->queue_stats[FCOE_IDX]. + &bp->fw_stats_data->queue_stats[FCOE_IDX(bp)]. xstorm_queue_statistics; struct fcoe_statistics_params *fw_fcoe_stat = @@ -4741,7 +4741,7 @@ static void bnx2x_after_function_update(struct bnx2x *bp) #ifdef BCM_CNIC if (!NO_FCOE(bp)) { - fp = &bp->fp[FCOE_IDX]; + fp = &bp->fp[FCOE_IDX(bp)]; queue_params.q_obj = &fp->q_obj; /* clear pending completion bit */ @@ -4778,7 +4778,7 @@ static struct bnx2x_queue_sp_obj *bnx2x_cid_to_q_obj( return &bnx2x_fcoe(bp, q_obj); else #endif - return &bnx2x_fp(bp, CID_TO_FP(cid), q_obj); + return &bnx2x_fp(bp, CID_TO_FP(cid, bp), q_obj); } static void bnx2x_eq_int(struct bnx2x *bp) @@ -5660,11 +5660,11 @@ static void bnx2x_init_eth_fp(struct bnx2x *bp, int fp_idx) /* init tx data */ for_each_cos_in_tx_queue(fp, cos) { - bnx2x_init_txdata(bp, &fp->txdata[cos], - CID_COS_TO_TX_ONLY_CID(fp->cid, cos), - FP_COS_TO_TXQ(fp, cos), - BNX2X_TX_SB_INDEX_BASE + cos); - cids[cos] = fp->txdata[cos].cid; + bnx2x_init_txdata(bp, fp->txdata_ptr[cos], + CID_COS_TO_TX_ONLY_CID(fp->cid, cos, bp), + FP_COS_TO_TXQ(fp, cos, bp), + BNX2X_TX_SB_INDEX_BASE + cos, fp); + cids[cos] = fp->txdata_ptr[cos]->cid; } bnx2x_init_queue_obj(bp, &fp->q_obj, fp->cl_id, cids, fp->max_cos, @@ -5719,7 +5719,7 @@ static void bnx2x_init_tx_rings(struct bnx2x *bp) for_each_tx_queue(bp, i) for_each_cos_in_tx_queue(&bp->fp[i], cos) - bnx2x_init_tx_ring_one(&bp->fp[i].txdata[cos]); + bnx2x_init_tx_ring_one(bp->fp[i].txdata_ptr[cos]); } void bnx2x_nic_init(struct bnx2x *bp, u32 load_code) @@ -7807,8 +7807,8 @@ static void bnx2x_pf_q_prep_init(struct bnx2x *bp, /* set the context pointers queue object */ for (cos = FIRST_TX_COS_INDEX; cos < init_params->max_cos; cos++) { - cxt_index = fp->txdata[cos].cid / ILT_PAGE_CIDS; - cxt_offset = fp->txdata[cos].cid - (cxt_index * + cxt_index = fp->txdata_ptr[cos]->cid / ILT_PAGE_CIDS; + cxt_offset = fp->txdata_ptr[cos]->cid - (cxt_index * ILT_PAGE_CIDS); init_params->cxts[cos] = &bp->context[cxt_index].vcxt[cxt_offset].eth; @@ -7961,7 +7961,7 @@ static int bnx2x_stop_queue(struct bnx2x *bp, int index) tx_index++){ /* ascertain this is a normal queue*/ - txdata = &fp->txdata[tx_index]; + txdata = fp->txdata_ptr[tx_index]; DP(NETIF_MSG_IFDOWN, "stopping tx-only queue %d\n", txdata->txq_index); @@ -8328,7 +8328,7 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode) struct bnx2x_fastpath *fp = &bp->fp[i]; for_each_cos_in_tx_queue(fp, cos) - rc = bnx2x_clean_tx_queue(bp, &fp->txdata[cos]); + rc = bnx2x_clean_tx_queue(bp, fp->txdata_ptr[cos]); #ifdef BNX2X_STOP_ON_ERROR if (rc) return; @@ -11769,7 +11769,7 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, struct bnx2x *bp; int pcie_width, pcie_speed; int rc, max_non_def_sbs; - int rx_count, tx_count, rss_count; + int rx_count, tx_count, rss_count, doorbell_size; /* * An estimated maximum supported CoS number according to the chip * version. @@ -11863,8 +11863,10 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, * Map doorbels here as we need the real value of bp->max_cos which * is initialized in bnx2x_init_bp(). */ + doorbell_size = (rss_count * max_cos_est + NON_ETH_CONTEXT_USE + + CNIC_PRESENT) * (1 << BNX2X_DB_SHIFT); bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2), - min_t(u64, BNX2X_DB_SIZE(bp), + min_t(u64, doorbell_size, pci_resource_len(pdev, 2))); if (!bp->doorbells) { dev_err(&bp->pdev->dev, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 0e8bdcb9c748..776b52163b11 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -1432,7 +1432,7 @@ static void bnx2x_prep_fw_stats_req(struct bnx2x *bp) query[first_queue_query_index + i]; cur_query_entry->kind = STATS_TYPE_QUEUE; - cur_query_entry->index = bnx2x_stats_id(&bp->fp[FCOE_IDX]); + cur_query_entry->index = bnx2x_stats_id(&bp->fp[FCOE_IDX(bp)]); cur_query_entry->funcID = cpu_to_le16(BP_FUNC(bp)); cur_query_entry->address.hi = cpu_to_le32(U64_HI(cur_data_offset)); @@ -1573,7 +1573,7 @@ void bnx2x_afex_collect_stats(struct bnx2x *bp, void *void_afex_stats, struct afex_stats *afex_stats = (struct afex_stats *)void_afex_stats; struct bnx2x_eth_stats *estats = &bp->eth_stats; struct per_queue_stats *fcoe_q_stats = - &bp->fw_stats_data->queue_stats[FCOE_IDX]; + &bp->fw_stats_data->queue_stats[FCOE_IDX(bp)]; struct tstorm_per_queue_stats *fcoe_q_tstorm_stats = &fcoe_q_stats->tstorm_queue_statistics; -- cgit v1.2.3 From 37ae41a965fbb810b6a98df7df8ab46fefcc15eb Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:27 +0000 Subject: bnx2x: Move the CNIC L2 CIDs to be right after the RSS CIDs Currently the CNIC-related L2 CIDs (for sending control FCoE / iSCSI packets) were at fixed position, according to the maximal number of RSS queues multiplied by the number of traffic-classes. This change makes the CIDs dynamic, as they are defined to be right after the highest RSS CID. This decreases the memory allocated for the context. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 19 +++++----- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 1 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 15 +++++--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 47 +++++++++++++++++------- 4 files changed, 52 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index d06064b0d063..234517f132a8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -248,13 +248,12 @@ enum { BNX2X_MAX_CNIC_ETH_CL_ID_IDX, }; -#define BNX2X_CNIC_START_ETH_CID 48 -enum { +#define BNX2X_CNIC_START_ETH_CID(bp) (BNX2X_NUM_NON_CNIC_QUEUES(bp) *\ + (bp)->max_cos) /* iSCSI L2 */ - BNX2X_ISCSI_ETH_CID = BNX2X_CNIC_START_ETH_CID, +#define BNX2X_ISCSI_ETH_CID(bp) (BNX2X_CNIC_START_ETH_CID(bp)) /* FCoE L2 */ - BNX2X_FCOE_ETH_CID, -}; +#define BNX2X_FCOE_ETH_CID(bp) (BNX2X_CNIC_START_ETH_CID(bp) + 1) /** Additional rings budgeting */ #ifdef BCM_CNIC @@ -276,8 +275,6 @@ enum { #define FIRST_TX_ONLY_COS_INDEX 1 #define FIRST_TX_COS_INDEX 0 -#define MAX_TXQS_PER_COS FP_SB_MAX_E1x - /* rules for calculating the cids of tx-only connections */ #define CID_TO_FP(cid, bp) ((cid) % BNX2X_NUM_NON_CNIC_QUEUES(bp)) #define CID_COS_TO_TX_ONLY_CID(cid, cos, bp) \ @@ -1448,10 +1445,12 @@ struct bnx2x { /* * Maximum CID count that might be required by the bnx2x: - * Max Tss * Max_Tx_Multi_Cos + CNIC L2 Clients (FCoE and iSCSI related) + * Max RSS * Max_Tx_Multi_Cos + FCoE + iSCSI */ -#define BNX2X_L2_CID_COUNT(bp) (MAX_TXQS_PER_COS * BNX2X_MULTI_TX_COS +\ - NON_ETH_CONTEXT_USE + CNIC_PRESENT) +#define BNX2X_L2_CID_COUNT(bp) (BNX2X_NUM_ETH_QUEUES(bp) * BNX2X_MULTI_TX_COS \ + + NON_ETH_CONTEXT_USE + CNIC_PRESENT) +#define BNX2X_L2_MAX_CID(bp) (BNX2X_MAX_RSS_COUNT(bp) * BNX2X_MULTI_TX_COS \ + + NON_ETH_CONTEXT_USE + CNIC_PRESENT) #define L2_ILT_LINES(bp) (DIV_ROUND_UP(BNX2X_L2_CID_COUNT(bp),\ ILT_PAGE_CIDS)) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index d99f20ace9df..cdd9ca42556e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2235,6 +2235,7 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) /* re-read iscsi info */ bnx2x_get_iscsi_info(bp); bnx2x_setup_cnic_irq_info(bp); + bnx2x_setup_cnic_info(bp); if (bp->state == BNX2X_STATE_OPEN) bnx2x_cnic_notify(bp, CNIC_CTL_START_CMD); #endif diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 112ffccb3121..de79cdf1299b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -244,6 +244,14 @@ int bnx2x_cnic_notify(struct bnx2x *bp, int cmd); * @bp: driver handle */ void bnx2x_setup_cnic_irq_info(struct bnx2x *bp); + +/** + * bnx2x_setup_cnic_info - provides cnic with updated info + * + * @bp: driver handle + */ +void bnx2x_setup_cnic_info(struct bnx2x *bp); + #endif /** @@ -1107,12 +1115,7 @@ static inline void bnx2x_init_fcoe_fp(struct bnx2x *bp) bnx2x_fcoe(bp, rx_queue) = BNX2X_NUM_ETH_QUEUES(bp); bnx2x_fcoe(bp, cl_id) = bnx2x_cnic_eth_cl_id(bp, BNX2X_FCOE_ETH_CL_ID_IDX); - /** Current BNX2X_FCOE_ETH_CID deffinition implies not more than - * 16 ETH clients per function when CNIC is enabled! - * - * Fix it ASAP!!! - */ - bnx2x_fcoe(bp, cid) = BNX2X_FCOE_ETH_CID; + bnx2x_fcoe(bp, cid) = BNX2X_FCOE_ETH_CID(bp); bnx2x_fcoe(bp, fw_sb_id) = DEF_SB_ID; bnx2x_fcoe(bp, igu_sb_id) = bp->igu_dsb_id; bnx2x_fcoe(bp, rx_cons_sb) = BNX2X_FCOE_L2_RX_INDEX; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index e1c011e188aa..c9302d9a796c 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -4628,7 +4628,7 @@ static void bnx2x_handle_classification_eqe(struct bnx2x *bp, case BNX2X_FILTER_MAC_PENDING: DP(BNX2X_MSG_SP, "Got SETUP_MAC completions\n"); #ifdef BCM_CNIC - if (cid == BNX2X_ISCSI_ETH_CID) + if (cid == BNX2X_ISCSI_ETH_CID(bp)) vlan_mac_obj = &bp->iscsi_l2_mac_obj; else #endif @@ -4774,7 +4774,7 @@ static struct bnx2x_queue_sp_obj *bnx2x_cid_to_q_obj( { DP(BNX2X_MSG_SP, "retrieving fp from cid %d\n", cid); #ifdef BCM_CNIC - if (cid == BNX2X_FCOE_ETH_CID) + if (cid == BNX2X_FCOE_ETH_CID(bp)) return &bnx2x_fcoe(bp, q_obj); else #endif @@ -11724,7 +11724,7 @@ void bnx2x__init_func_obj(struct bnx2x *bp) /* must be called after sriov-enable */ static int bnx2x_set_qm_cid_count(struct bnx2x *bp) { - int cid_count = BNX2X_L2_CID_COUNT(bp); + int cid_count = BNX2X_L2_MAX_CID(bp); #ifdef BCM_CNIC cid_count += CNIC_CID_MAX; @@ -11829,9 +11829,9 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, /* * Maximum number of netdev Tx queues: - * Maximum TSS queues * Maximum supported number of CoS + FCoE L2 + * Maximum TSS queues * Maximum supported number of CoS + FCoE L2 */ - tx_count = MAX_TXQS_PER_COS * max_cos_est + FCOE_PRESENT; + tx_count = rss_count * max_cos_est + FCOE_PRESENT; /* dev zeroed in init_etherdev */ dev = alloc_etherdev_mqs(sizeof(*bp), tx_count, rx_count); @@ -11863,11 +11863,15 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, * Map doorbels here as we need the real value of bp->max_cos which * is initialized in bnx2x_init_bp(). */ - doorbell_size = (rss_count * max_cos_est + NON_ETH_CONTEXT_USE + - CNIC_PRESENT) * (1 << BNX2X_DB_SHIFT); + doorbell_size = BNX2X_L2_MAX_CID(bp) * (1 << BNX2X_DB_SHIFT); + if (doorbell_size > pci_resource_len(pdev, 2)) { + dev_err(&bp->pdev->dev, + "Cannot map doorbells, bar size too small, aborting\n"); + rc = -ENOMEM; + goto init_one_exit; + } bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2), - min_t(u64, doorbell_size, - pci_resource_len(pdev, 2))); + doorbell_size); if (!bp->doorbells) { dev_err(&bp->pdev->dev, "Cannot map doorbell space, aborting\n"); @@ -12254,14 +12258,14 @@ static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count) */ if (type == ETH_CONNECTION_TYPE) { if (cmd == RAMROD_CMD_ID_ETH_CLIENT_SETUP) { - cxt_index = BNX2X_ISCSI_ETH_CID / + cxt_index = BNX2X_ISCSI_ETH_CID(bp) / ILT_PAGE_CIDS; - cxt_offset = BNX2X_ISCSI_ETH_CID - + cxt_offset = BNX2X_ISCSI_ETH_CID(bp) - (cxt_index * ILT_PAGE_CIDS); bnx2x_set_ctx_validation(bp, &bp->context[cxt_index]. vcxt[cxt_offset].eth, - BNX2X_ISCSI_ETH_CID); + BNX2X_ISCSI_ETH_CID(bp)); } } @@ -12615,6 +12619,21 @@ void bnx2x_setup_cnic_irq_info(struct bnx2x *bp) cp->num_irq = 2; } +void bnx2x_setup_cnic_info(struct bnx2x *bp) +{ + struct cnic_eth_dev *cp = &bp->cnic_eth_dev; + + + cp->ctx_tbl_offset = FUNC_ILT_BASE(BP_FUNC(bp)) + + bnx2x_cid_ilt_lines(bp); + cp->starting_cid = bnx2x_cid_ilt_lines(bp) * ILT_PAGE_CIDS; + cp->fcoe_init_cid = BNX2X_FCOE_ETH_CID(bp); + cp->iscsi_l2_cid = BNX2X_ISCSI_ETH_CID(bp); + + if (NO_ISCSI_OOO(bp)) + cp->drv_state |= CNIC_DRV_STATE_NO_ISCSI_OOO; +} + static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops, void *data) { @@ -12693,10 +12712,10 @@ struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev) cp->drv_ctl = bnx2x_drv_ctl; cp->drv_register_cnic = bnx2x_register_cnic; cp->drv_unregister_cnic = bnx2x_unregister_cnic; - cp->fcoe_init_cid = BNX2X_FCOE_ETH_CID; + cp->fcoe_init_cid = BNX2X_FCOE_ETH_CID(bp); cp->iscsi_l2_client_id = bnx2x_cnic_eth_cl_id(bp, BNX2X_ISCSI_ETH_CL_ID_IDX); - cp->iscsi_l2_cid = BNX2X_ISCSI_ETH_CID; + cp->iscsi_l2_cid = BNX2X_ISCSI_ETH_CID(bp); if (NO_ISCSI_OOO(bp)) cp->drv_state |= CNIC_DRV_STATE_NO_ISCSI_OOO; -- cgit v1.2.3 From 15192a8cf8a8d16e0ff38a144c8a4630c94f9fd6 Mon Sep 17 00:00:00 2001 From: Barak Witkowski Date: Tue, 19 Jun 2012 07:48:28 +0000 Subject: bnx2x: Split the FP structure This patch moves some fields out of the FP structure to different structures, in order to minimize size of contigiuous memory allocated. Signed-off-by: Barak Witkowski Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 41 +++++---- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 97 ++++++++++++++++------ drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 8 +- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 4 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 37 +++++---- drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c | 55 +++++++----- 6 files changed, 155 insertions(+), 87 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 234517f132a8..25d46c10dbd4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -549,34 +549,23 @@ struct bnx2x_fastpath { rx_calls; /* TPA related */ - struct bnx2x_agg_info tpa_info[ETH_MAX_AGGREGATION_QUEUES_E1H_E2]; + struct bnx2x_agg_info *tpa_info; u8 disable_tpa; #ifdef BNX2X_STOP_ON_ERROR u64 tpa_queue_used; #endif - - struct tstorm_per_queue_stats old_tclient; - struct ustorm_per_queue_stats old_uclient; - struct xstorm_per_queue_stats old_xclient; - struct bnx2x_eth_q_stats eth_q_stats; - struct bnx2x_eth_q_stats_old eth_q_stats_old; - /* The size is calculated using the following: sizeof name field from netdev structure + 4 ('-Xx-' string) + 4 (for the digits and to make it DWORD aligned) */ #define FP_NAME_SIZE (sizeof(((struct net_device *)0)->name) + 8) char name[FP_NAME_SIZE]; - - /* MACs object */ - struct bnx2x_vlan_mac_obj mac_obj; - - /* Queue State object */ - struct bnx2x_queue_sp_obj q_obj; - }; -#define bnx2x_fp(bp, nr, var) (bp->fp[nr].var) +#define bnx2x_fp(bp, nr, var) ((bp)->fp[(nr)].var) +#define bnx2x_sp_obj(bp, fp) ((bp)->sp_objs[(fp)->index]) +#define bnx2x_fp_stats(bp, fp) (&((bp)->fp_stats[(fp)->index])) +#define bnx2x_fp_qstats(bp, fp) (&((bp)->fp_stats[(fp)->index].eth_q_stats)) /* Use 2500 as a mini-jumbo MTU for FCoE */ #define BNX2X_FCOE_MINI_JUMBO_MTU 2500 @@ -587,6 +576,8 @@ struct bnx2x_fastpath { FCOE_IDX_OFFSET) #define bnx2x_fcoe_fp(bp) (&bp->fp[FCOE_IDX(bp)]) #define bnx2x_fcoe(bp, var) (bnx2x_fcoe_fp(bp)->var) +#define bnx2x_fcoe_inner_sp_obj(bp) (&bp->sp_objs[FCOE_IDX(bp)]) +#define bnx2x_fcoe_sp_obj(bp, var) (bnx2x_fcoe_inner_sp_obj(bp)->var) #define bnx2x_fcoe_tx(bp, var) (bnx2x_fcoe_fp(bp)-> \ txdata_ptr[FIRST_TX_COS_INDEX] \ ->var) @@ -1187,11 +1178,29 @@ struct bnx2x_prev_path_list { struct list_head list; }; +struct bnx2x_sp_objs { + /* MACs object */ + struct bnx2x_vlan_mac_obj mac_obj; + + /* Queue State object */ + struct bnx2x_queue_sp_obj q_obj; +}; + +struct bnx2x_fp_stats { + struct tstorm_per_queue_stats old_tclient; + struct ustorm_per_queue_stats old_uclient; + struct xstorm_per_queue_stats old_xclient; + struct bnx2x_eth_q_stats eth_q_stats; + struct bnx2x_eth_q_stats_old eth_q_stats_old; +}; + struct bnx2x { /* Fields used in the tx and intr/napi performance paths * are grouped together in the beginning of the structure */ struct bnx2x_fastpath *fp; + struct bnx2x_sp_objs *sp_objs; + struct bnx2x_fp_stats *fp_stats; struct bnx2x_fp_txdata *bnx2x_txq; int bnx2x_txq_size; void __iomem *regview; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index cdd9ca42556e..413b665646b7 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -47,6 +47,10 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) { struct bnx2x_fastpath *from_fp = &bp->fp[from]; struct bnx2x_fastpath *to_fp = &bp->fp[to]; + struct bnx2x_sp_objs *from_sp_objs = &bp->sp_objs[from]; + struct bnx2x_sp_objs *to_sp_objs = &bp->sp_objs[to]; + struct bnx2x_fp_stats *from_fp_stats = &bp->fp_stats[from]; + struct bnx2x_fp_stats *to_fp_stats = &bp->fp_stats[to]; int old_max_eth_txqs, new_max_eth_txqs; int old_txdata_index = 0, new_txdata_index = 0; @@ -57,6 +61,12 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to) memcpy(to_fp, from_fp, sizeof(*to_fp)); to_fp->index = to; + /* move sp_objs contents as well, as their indices match fp ones */ + memcpy(to_sp_objs, from_sp_objs, sizeof(*to_sp_objs)); + + /* move fp_stats contents as well, as their indices match fp ones */ + memcpy(to_fp_stats, from_fp_stats, sizeof(*to_fp_stats)); + /* Update txdata pointers in fp and move txdata content accordingly: * Each fp consumes 'max_cos' txdata structures, so the index should be * decremented by max_cos x delta. @@ -500,7 +510,7 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp, where we are and drop the whole packet */ err = bnx2x_alloc_rx_sge(bp, fp, sge_idx); if (unlikely(err)) { - fp->eth_q_stats.rx_skb_alloc_failed++; + bnx2x_fp_qstats(bp, fp)->rx_skb_alloc_failed++; return err; } @@ -605,7 +615,7 @@ drop: /* drop the packet and keep the buffer in the bin */ DP(NETIF_MSG_RX_STATUS, "Failed to allocate or map a new skb - dropping packet!\n"); - fp->eth_q_stats.rx_skb_alloc_failed++; + bnx2x_fp_stats(bp, fp)->eth_q_stats.rx_skb_alloc_failed++; } static int bnx2x_alloc_rx_data(struct bnx2x *bp, @@ -638,8 +648,10 @@ static int bnx2x_alloc_rx_data(struct bnx2x *bp, return 0; } -static void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe, - struct bnx2x_fastpath *fp) +static +void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe, + struct bnx2x_fastpath *fp, + struct bnx2x_eth_q_stats *qstats) { /* Do nothing if no IP/L4 csum validation was done */ @@ -653,7 +665,7 @@ static void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe, if (cqe->fast_path_cqe.type_error_flags & (ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG | ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG)) - fp->eth_q_stats.hw_csum_err++; + qstats->hw_csum_err++; else skb->ip_summed = CHECKSUM_UNNECESSARY; } @@ -797,7 +809,7 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) DP(NETIF_MSG_RX_ERR | NETIF_MSG_RX_STATUS, "ERROR flags %x rx packet %u\n", cqe_fp_flags, sw_comp_cons); - fp->eth_q_stats.rx_err_discard_pkt++; + bnx2x_fp_qstats(bp, fp)->rx_err_discard_pkt++; goto reuse_rx; } @@ -810,7 +822,7 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) if (skb == NULL) { DP(NETIF_MSG_RX_ERR | NETIF_MSG_RX_STATUS, "ERROR packet dropped because of alloc failure\n"); - fp->eth_q_stats.rx_skb_alloc_failed++; + bnx2x_fp_qstats(bp, fp)->rx_skb_alloc_failed++; goto reuse_rx; } memcpy(skb->data, data + pad, len); @@ -824,14 +836,15 @@ int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) skb = build_skb(data, 0); if (unlikely(!skb)) { kfree(data); - fp->eth_q_stats.rx_skb_alloc_failed++; + bnx2x_fp_qstats(bp, fp)-> + rx_skb_alloc_failed++; goto next_rx; } skb_reserve(skb, pad); } else { DP(NETIF_MSG_RX_ERR | NETIF_MSG_RX_STATUS, "ERROR packet dropped because of alloc failure\n"); - fp->eth_q_stats.rx_skb_alloc_failed++; + bnx2x_fp_qstats(bp, fp)->rx_skb_alloc_failed++; reuse_rx: bnx2x_reuse_rx_data(fp, bd_cons, bd_prod); goto next_rx; @@ -847,8 +860,8 @@ reuse_rx: skb_checksum_none_assert(skb); if (bp->dev->features & NETIF_F_RXCSUM) - bnx2x_csum_validate(skb, cqe, fp); - + bnx2x_csum_validate(skb, cqe, fp, + bnx2x_fp_qstats(bp, fp)); skb_record_rx_queue(skb, fp->rx_queue); @@ -1780,7 +1793,7 @@ static void bnx2x_squeeze_objects(struct bnx2x *bp) int rc; unsigned long ramrod_flags = 0, vlan_mac_flags = 0; struct bnx2x_mcast_ramrod_params rparam = {NULL}; - struct bnx2x_vlan_mac_obj *mac_obj = &bp->fp->mac_obj; + struct bnx2x_vlan_mac_obj *mac_obj = &bp->sp_objs->mac_obj; /***************** Cleanup MACs' object first *************************/ @@ -1791,7 +1804,7 @@ static void bnx2x_squeeze_objects(struct bnx2x *bp) /* Clean ETH primary MAC */ __set_bit(BNX2X_ETH_MAC, &vlan_mac_flags); - rc = mac_obj->delete_all(bp, &bp->fp->mac_obj, &vlan_mac_flags, + rc = mac_obj->delete_all(bp, &bp->sp_objs->mac_obj, &vlan_mac_flags, &ramrod_flags); if (rc != 0) BNX2X_ERR("Failed to clean ETH MACs: %d\n", rc); @@ -1877,12 +1890,16 @@ bool bnx2x_test_firmware_version(struct bnx2x *bp, bool is_err) static void bnx2x_bz_fp(struct bnx2x *bp, int index) { struct bnx2x_fastpath *fp = &bp->fp[index]; + struct bnx2x_fp_stats *fp_stats = &bp->fp_stats[index]; + int cos; struct napi_struct orig_napi = fp->napi; + struct bnx2x_agg_info *orig_tpa_info = fp->tpa_info; /* bzero bnx2x_fastpath contents */ - if (bp->stats_init) + if (bp->stats_init) { + memset(fp->tpa_info, 0, sizeof(*fp->tpa_info)); memset(fp, 0, sizeof(*fp)); - else { + } else { /* Keep Queue statistics */ struct bnx2x_eth_q_stats *tmp_eth_q_stats; struct bnx2x_eth_q_stats_old *tmp_eth_q_stats_old; @@ -1890,26 +1907,27 @@ static void bnx2x_bz_fp(struct bnx2x *bp, int index) tmp_eth_q_stats = kzalloc(sizeof(struct bnx2x_eth_q_stats), GFP_KERNEL); if (tmp_eth_q_stats) - memcpy(tmp_eth_q_stats, &fp->eth_q_stats, + memcpy(tmp_eth_q_stats, &fp_stats->eth_q_stats, sizeof(struct bnx2x_eth_q_stats)); tmp_eth_q_stats_old = kzalloc(sizeof(struct bnx2x_eth_q_stats_old), GFP_KERNEL); if (tmp_eth_q_stats_old) - memcpy(tmp_eth_q_stats_old, &fp->eth_q_stats_old, + memcpy(tmp_eth_q_stats_old, &fp_stats->eth_q_stats_old, sizeof(struct bnx2x_eth_q_stats_old)); + memset(fp->tpa_info, 0, sizeof(*fp->tpa_info)); memset(fp, 0, sizeof(*fp)); if (tmp_eth_q_stats) { - memcpy(&fp->eth_q_stats, tmp_eth_q_stats, - sizeof(struct bnx2x_eth_q_stats)); + memcpy(&fp_stats->eth_q_stats, tmp_eth_q_stats, + sizeof(struct bnx2x_eth_q_stats)); kfree(tmp_eth_q_stats); } if (tmp_eth_q_stats_old) { - memcpy(&fp->eth_q_stats_old, tmp_eth_q_stats_old, + memcpy(&fp_stats->eth_q_stats_old, tmp_eth_q_stats_old, sizeof(struct bnx2x_eth_q_stats_old)); kfree(tmp_eth_q_stats_old); } @@ -1918,7 +1936,7 @@ static void bnx2x_bz_fp(struct bnx2x *bp, int index) /* Restore the NAPI object as it has been already initialized */ fp->napi = orig_napi; - + fp->tpa_info = orig_tpa_info; fp->bp = bp; fp->index = index; if (IS_ETH_FP(fp)) @@ -2918,7 +2936,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(bnx2x_tx_avail(bp, txdata) < (skb_shinfo(skb)->nr_frags + 3))) { - txdata->parent_fp->eth_q_stats.driver_xoff++; + bnx2x_fp_qstats(bp, txdata->parent_fp)->driver_xoff++; netif_tx_stop_queue(txq); BNX2X_ERR("BUG! Tx ring full when queue awake!\n"); return NETDEV_TX_BUSY; @@ -3200,7 +3218,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) * fp->bd_tx_cons */ smp_mb(); - txdata->parent_fp->eth_q_stats.driver_xoff++; + bnx2x_fp_qstats(bp, txdata->parent_fp)->driver_xoff++; if (bnx2x_tx_avail(bp, txdata) >= MAX_SKB_FRAGS + 4) netif_tx_wake_queue(txq); } @@ -3437,7 +3455,7 @@ static int bnx2x_alloc_rx_bds(struct bnx2x_fastpath *fp, cqe_ring_prod); fp->rx_pkt = fp->rx_calls = 0; - fp->eth_q_stats.rx_skb_alloc_failed += failure_cnt; + bnx2x_fp_stats(bp, fp)->eth_q_stats.rx_skb_alloc_failed += failure_cnt; return i - failure_cnt; } @@ -3642,7 +3660,10 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp) void bnx2x_free_mem_bp(struct bnx2x *bp) { + kfree(bp->fp->tpa_info); kfree(bp->fp); + kfree(bp->sp_objs); + kfree(bp->fp_stats); kfree(bp->bnx2x_txq); kfree(bp->msix_table); kfree(bp->ilt); @@ -3654,6 +3675,8 @@ int __devinit bnx2x_alloc_mem_bp(struct bnx2x *bp) struct msix_entry *tbl; struct bnx2x_ilt *ilt; int msix_table_size = 0; + int fp_array_size; + int i; /* * The biggest MSI-X table we might need is as a maximum number of fast @@ -3662,12 +3685,34 @@ int __devinit bnx2x_alloc_mem_bp(struct bnx2x *bp) msix_table_size = bp->igu_sb_cnt + 1; /* fp array: RSS plus CNIC related L2 queues */ - fp = kcalloc(BNX2X_MAX_RSS_COUNT(bp) + NON_ETH_CONTEXT_USE, - sizeof(*fp), GFP_KERNEL); + fp_array_size = BNX2X_MAX_RSS_COUNT(bp) + NON_ETH_CONTEXT_USE; + BNX2X_DEV_INFO("fp_array_size %d", fp_array_size); + + fp = kcalloc(fp_array_size, sizeof(*fp), GFP_KERNEL); if (!fp) goto alloc_err; + for (i = 0; i < fp_array_size; i++) { + fp[i].tpa_info = + kcalloc(ETH_MAX_AGGREGATION_QUEUES_E1H_E2, + sizeof(struct bnx2x_agg_info), GFP_KERNEL); + if (!(fp[i].tpa_info)) + goto alloc_err; + } + bp->fp = fp; + /* allocate sp objs */ + bp->sp_objs = kcalloc(fp_array_size, sizeof(struct bnx2x_sp_objs), + GFP_KERNEL); + if (!bp->sp_objs) + goto alloc_err; + + /* allocate fp_stats */ + bp->fp_stats = kcalloc(fp_array_size, sizeof(struct bnx2x_fp_stats), + GFP_KERNEL); + if (!bp->fp_stats) + goto alloc_err; + /* Allocate memory for the transmission queues array */ bp->bnx2x_txq_size = BNX2X_MAX_RSS_COUNT(bp) * BNX2X_MULTI_TX_COS; #ifdef BCM_CNIC diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index de79cdf1299b..11afe5d8819b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -981,8 +981,8 @@ static inline void bnx2x_init_vlan_mac_fp_objs(struct bnx2x_fastpath *fp, struct bnx2x *bp = fp->bp; /* Configure classification DBs */ - bnx2x_init_mac_obj(bp, &fp->mac_obj, fp->cl_id, fp->cid, - BP_FUNC(bp), bnx2x_sp(bp, mac_rdata), + bnx2x_init_mac_obj(bp, &bnx2x_sp_obj(bp, fp).mac_obj, fp->cl_id, + fp->cid, BP_FUNC(bp), bnx2x_sp(bp, mac_rdata), bnx2x_sp_mapping(bp, mac_rdata), BNX2X_FILTER_MAC_PENDING, &bp->sp_state, obj_type, @@ -1138,8 +1138,8 @@ static inline void bnx2x_init_fcoe_fp(struct bnx2x *bp) /* No multi-CoS for FCoE L2 client */ BUG_ON(fp->max_cos != 1); - bnx2x_init_queue_obj(bp, &fp->q_obj, fp->cl_id, &fp->cid, 1, - BP_FUNC(bp), bnx2x_sp(bp, q_rdata), + bnx2x_init_queue_obj(bp, &bnx2x_sp_obj(bp, fp).q_obj, fp->cl_id, + &fp->cid, 1, BP_FUNC(bp), bnx2x_sp(bp, q_rdata), bnx2x_sp_mapping(bp, q_rdata), q_type); DP(NETIF_MSG_IFUP, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index b46822356a1c..af84ebdeed7a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2292,7 +2292,7 @@ static int bnx2x_test_intr(struct bnx2x *bp) return -ENODEV; } - params.q_obj = &bp->fp->q_obj; + params.q_obj = &bp->sp_objs->q_obj; params.cmd = BNX2X_Q_CMD_EMPTY; __set_bit(RAMROD_COMP_WAIT, ¶ms.ramrod_flags); @@ -2516,7 +2516,7 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, if (is_multi(bp)) { for_each_eth_queue(bp, i) { - hw_stats = (u32 *)&bp->fp[i].eth_q_stats; + hw_stats = (u32 *)&bp->fp_stats[i].eth_q_stats; for (j = 0; j < BNX2X_NUM_Q_STATS; j++) { if (bnx2x_q_stats_arr[j].size == 0) { /* skip this counter */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index c9302d9a796c..dce705636bc6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -1583,7 +1583,7 @@ void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe) int cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data); int command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data); enum bnx2x_queue_cmd drv_cmd = BNX2X_Q_CMD_MAX; - struct bnx2x_queue_sp_obj *q_obj = &fp->q_obj; + struct bnx2x_queue_sp_obj *q_obj = &bnx2x_sp_obj(bp, fp).q_obj; DP(BNX2X_MSG_SP, "fp %d cid %d got ramrod #%d state is %x type is %d\n", @@ -3035,9 +3035,9 @@ static void bnx2x_drv_info_ether_stat(struct bnx2x *bp) memcpy(ether_stat->version, DRV_MODULE_VERSION, ETH_STAT_INFO_VERSION_LEN - 1); - bp->fp[0].mac_obj.get_n_elements(bp, &bp->fp[0].mac_obj, - DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED, - ether_stat->mac_local); + bp->sp_objs[0].mac_obj.get_n_elements(bp, &bp->sp_objs[0].mac_obj, + DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED, + ether_stat->mac_local); ether_stat->mtu_size = bp->dev->mtu; @@ -4632,7 +4632,7 @@ static void bnx2x_handle_classification_eqe(struct bnx2x *bp, vlan_mac_obj = &bp->iscsi_l2_mac_obj; else #endif - vlan_mac_obj = &bp->fp[cid].mac_obj; + vlan_mac_obj = &bp->sp_objs[cid].mac_obj; break; case BNX2X_FILTER_MCAST_PENDING: @@ -4730,7 +4730,7 @@ static void bnx2x_after_function_update(struct bnx2x *bp) for_each_eth_queue(bp, q) { /* Set the appropriate Queue object */ fp = &bp->fp[q]; - queue_params.q_obj = &fp->q_obj; + queue_params.q_obj = &bnx2x_sp_obj(bp, fp).q_obj; /* send the ramrod */ rc = bnx2x_queue_state_change(bp, &queue_params); @@ -4742,7 +4742,7 @@ static void bnx2x_after_function_update(struct bnx2x *bp) #ifdef BCM_CNIC if (!NO_FCOE(bp)) { fp = &bp->fp[FCOE_IDX(bp)]; - queue_params.q_obj = &fp->q_obj; + queue_params.q_obj = &bnx2x_sp_obj(bp, fp).q_obj; /* clear pending completion bit */ __clear_bit(RAMROD_COMP_WAIT, &queue_params.ramrod_flags); @@ -4775,10 +4775,10 @@ static struct bnx2x_queue_sp_obj *bnx2x_cid_to_q_obj( DP(BNX2X_MSG_SP, "retrieving fp from cid %d\n", cid); #ifdef BCM_CNIC if (cid == BNX2X_FCOE_ETH_CID(bp)) - return &bnx2x_fcoe(bp, q_obj); + return &bnx2x_fcoe_sp_obj(bp, q_obj); else #endif - return &bnx2x_fp(bp, CID_TO_FP(cid, bp), q_obj); + return &bp->sp_objs[CID_TO_FP(cid, bp)].q_obj; } static void bnx2x_eq_int(struct bnx2x *bp) @@ -5667,8 +5667,8 @@ static void bnx2x_init_eth_fp(struct bnx2x *bp, int fp_idx) cids[cos] = fp->txdata_ptr[cos]->cid; } - bnx2x_init_queue_obj(bp, &fp->q_obj, fp->cl_id, cids, fp->max_cos, - BP_FUNC(bp), bnx2x_sp(bp, q_rdata), + bnx2x_init_queue_obj(bp, &bnx2x_sp_obj(bp, fp).q_obj, fp->cl_id, cids, + fp->max_cos, BP_FUNC(bp), bnx2x_sp(bp, q_rdata), bnx2x_sp_mapping(bp, q_rdata), q_type); /** @@ -7596,8 +7596,8 @@ int bnx2x_set_eth_mac(struct bnx2x *bp, bool set) __set_bit(RAMROD_COMP_WAIT, &ramrod_flags); /* Eth MAC is set on RSS leading client (fp[0]) */ - return bnx2x_set_mac_one(bp, bp->dev->dev_addr, &bp->fp->mac_obj, set, - BNX2X_ETH_MAC, &ramrod_flags); + return bnx2x_set_mac_one(bp, bp->dev->dev_addr, &bp->sp_objs->mac_obj, + set, BNX2X_ETH_MAC, &ramrod_flags); } int bnx2x_setup_leading(struct bnx2x *bp) @@ -7877,7 +7877,7 @@ int bnx2x_setup_queue(struct bnx2x *bp, struct bnx2x_fastpath *fp, bnx2x_ack_sb(bp, fp->igu_sb_id, USTORM_ID, 0, IGU_INT_ENABLE, 0); - q_params.q_obj = &fp->q_obj; + q_params.q_obj = &bnx2x_sp_obj(bp, fp).q_obj; /* We want to wait for completion in this context */ __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags); @@ -7950,7 +7950,7 @@ static int bnx2x_stop_queue(struct bnx2x *bp, int index) DP(NETIF_MSG_IFDOWN, "stopping queue %d cid %d\n", index, fp->cid); - q_params.q_obj = &fp->q_obj; + q_params.q_obj = &bnx2x_sp_obj(bp, fp).q_obj; /* We want to wait for completion in this context */ __set_bit(RAMROD_COMP_WAIT, &q_params.ramrod_flags); @@ -8339,12 +8339,13 @@ void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode) usleep_range(1000, 1000); /* Clean all ETH MACs */ - rc = bnx2x_del_all_macs(bp, &bp->fp[0].mac_obj, BNX2X_ETH_MAC, false); + rc = bnx2x_del_all_macs(bp, &bp->sp_objs[0].mac_obj, BNX2X_ETH_MAC, + false); if (rc < 0) BNX2X_ERR("Failed to delete all ETH macs: %d\n", rc); /* Clean up UC list */ - rc = bnx2x_del_all_macs(bp, &bp->fp[0].mac_obj, BNX2X_UC_LIST_MAC, + rc = bnx2x_del_all_macs(bp, &bp->sp_objs[0].mac_obj, BNX2X_UC_LIST_MAC, true); if (rc < 0) BNX2X_ERR("Failed to schedule DEL commands for UC MACs list: %d\n", @@ -11049,7 +11050,7 @@ static int bnx2x_set_uc_list(struct bnx2x *bp) int rc; struct net_device *dev = bp->dev; struct netdev_hw_addr *ha; - struct bnx2x_vlan_mac_obj *mac_obj = &bp->fp->mac_obj; + struct bnx2x_vlan_mac_obj *mac_obj = &bp->sp_objs->mac_obj; unsigned long ramrod_flags = 0; /* First schedule a cleanup up of old configuration */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c index 776b52163b11..514a528f6ddf 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_stats.c @@ -859,17 +859,22 @@ static int bnx2x_storm_stats_update(struct bnx2x *bp) struct tstorm_per_queue_stats *tclient = &bp->fw_stats_data->queue_stats[i]. tstorm_queue_statistics; - struct tstorm_per_queue_stats *old_tclient = &fp->old_tclient; + struct tstorm_per_queue_stats *old_tclient = + &bnx2x_fp_stats(bp, fp)->old_tclient; struct ustorm_per_queue_stats *uclient = &bp->fw_stats_data->queue_stats[i]. ustorm_queue_statistics; - struct ustorm_per_queue_stats *old_uclient = &fp->old_uclient; + struct ustorm_per_queue_stats *old_uclient = + &bnx2x_fp_stats(bp, fp)->old_uclient; struct xstorm_per_queue_stats *xclient = &bp->fw_stats_data->queue_stats[i]. xstorm_queue_statistics; - struct xstorm_per_queue_stats *old_xclient = &fp->old_xclient; - struct bnx2x_eth_q_stats *qstats = &fp->eth_q_stats; - struct bnx2x_eth_q_stats_old *qstats_old = &fp->eth_q_stats_old; + struct xstorm_per_queue_stats *old_xclient = + &bnx2x_fp_stats(bp, fp)->old_xclient; + struct bnx2x_eth_q_stats *qstats = + &bnx2x_fp_stats(bp, fp)->eth_q_stats; + struct bnx2x_eth_q_stats_old *qstats_old = + &bnx2x_fp_stats(bp, fp)->eth_q_stats_old; u32 diff; @@ -1052,8 +1057,11 @@ static void bnx2x_net_stats_update(struct bnx2x *bp) nstats->tx_bytes = bnx2x_hilo(&estats->total_bytes_transmitted_hi); tmp = estats->mac_discard; - for_each_rx_queue(bp, i) - tmp += le32_to_cpu(bp->fp[i].old_tclient.checksum_discard); + for_each_rx_queue(bp, i) { + struct tstorm_per_queue_stats *old_tclient = + &bp->fp_stats[i].old_tclient; + tmp += le32_to_cpu(old_tclient->checksum_discard); + } nstats->rx_dropped = tmp + bp->net_stats_old.rx_dropped; nstats->tx_dropped = 0; @@ -1103,9 +1111,9 @@ static void bnx2x_drv_stats_update(struct bnx2x *bp) int i; for_each_queue(bp, i) { - struct bnx2x_eth_q_stats *qstats = &bp->fp[i].eth_q_stats; + struct bnx2x_eth_q_stats *qstats = &bp->fp_stats[i].eth_q_stats; struct bnx2x_eth_q_stats_old *qstats_old = - &bp->fp[i].eth_q_stats_old; + &bp->fp_stats[i].eth_q_stats_old; UPDATE_ESTAT_QSTAT(driver_xoff); UPDATE_ESTAT_QSTAT(rx_err_discard_pkt); @@ -1483,15 +1491,19 @@ void bnx2x_stats_init(struct bnx2x *bp) /* function stats */ for_each_queue(bp, i) { - struct bnx2x_fastpath *fp = &bp->fp[i]; - - memset(&fp->old_tclient, 0, sizeof(fp->old_tclient)); - memset(&fp->old_uclient, 0, sizeof(fp->old_uclient)); - memset(&fp->old_xclient, 0, sizeof(fp->old_xclient)); + struct bnx2x_fp_stats *fp_stats = &bp->fp_stats[i]; + + memset(&fp_stats->old_tclient, 0, + sizeof(fp_stats->old_tclient)); + memset(&fp_stats->old_uclient, 0, + sizeof(fp_stats->old_uclient)); + memset(&fp_stats->old_xclient, 0, + sizeof(fp_stats->old_xclient)); if (bp->stats_init) { - memset(&fp->eth_q_stats, 0, sizeof(fp->eth_q_stats)); - memset(&fp->eth_q_stats_old, 0, - sizeof(fp->eth_q_stats_old)); + memset(&fp_stats->eth_q_stats, 0, + sizeof(fp_stats->eth_q_stats)); + memset(&fp_stats->eth_q_stats_old, 0, + sizeof(fp_stats->eth_q_stats_old)); } } @@ -1533,8 +1545,10 @@ void bnx2x_save_statistics(struct bnx2x *bp) /* save queue statistics */ for_each_eth_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; - struct bnx2x_eth_q_stats *qstats = &fp->eth_q_stats; - struct bnx2x_eth_q_stats_old *qstats_old = &fp->eth_q_stats_old; + struct bnx2x_eth_q_stats *qstats = + &bnx2x_fp_stats(bp, fp)->eth_q_stats; + struct bnx2x_eth_q_stats_old *qstats_old = + &bnx2x_fp_stats(bp, fp)->eth_q_stats_old; UPDATE_QSTAT_OLD(total_unicast_bytes_received_hi); UPDATE_QSTAT_OLD(total_unicast_bytes_received_lo); @@ -1590,8 +1604,7 @@ void bnx2x_afex_collect_stats(struct bnx2x *bp, void *void_afex_stats, memset(afex_stats, 0, sizeof(struct afex_stats)); for_each_eth_queue(bp, i) { - struct bnx2x_fastpath *fp = &bp->fp[i]; - struct bnx2x_eth_q_stats *qstats = &fp->eth_q_stats; + struct bnx2x_eth_q_stats *qstats = &bp->fp_stats[i].eth_q_stats; ADD_64(afex_stats->rx_unicast_bytes_hi, qstats->total_unicast_bytes_received_hi, -- cgit v1.2.3 From 60aa0509056616afe9d66cf0fcd290589395032c Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:29 +0000 Subject: bnx2x: Allow up to 63 RSS queues This patch removed the limitation in the code for 16 RSS queues. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index dce705636bc6..1ebcaa1183df 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -11813,13 +11813,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, max_non_def_sbs = bnx2x_get_num_non_def_sbs(pdev); - /* !!! FIXME !!! - * Do not allow the maximum SB count to grow above 16 - * since Special CIDs starts from 16*BNX2X_MULTI_TX_COS=48. - * We will use the FP_SB_MAX_E1x macro for this matter. - */ - max_non_def_sbs = min_t(int, FP_SB_MAX_E1x, max_non_def_sbs); - WARN_ON(!max_non_def_sbs); /* Maximum number of RSS queues: one IGU SB goes to CNIC */ @@ -11841,9 +11834,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, bp = netdev_priv(dev); - BNX2X_DEV_INFO("Allocated netdev with %d tx and %d rx queues\n", - tx_count, rx_count); - bp->igu_sb_cnt = max_non_def_sbs; bp->msg_enable = debug; pci_set_drvdata(pdev, dev); @@ -11856,6 +11846,9 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, BNX2X_DEV_INFO("max_non_def_sbs %d\n", max_non_def_sbs); + BNX2X_DEV_INFO("Allocated netdev with %d tx and %d rx queues\n", + tx_count, rx_count); + rc = bnx2x_init_bp(bp); if (rc) goto init_one_exit; -- cgit v1.2.3 From 0e8d2ec5c6ccf74dbe40216bb62d82c2f73fe836 Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:30 +0000 Subject: bnx2x: Add support for ethtool -L Add support for ethtool -L/-l for setting and getting the number of RSS queues. The 'combined' field is used as we don't support separate IRQ for Rx and Tx. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 4 ++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 11 ++- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 80 ++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 15 ++-- 5 files changed, 100 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 25d46c10dbd4..fcc3b26c1c60 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1393,6 +1393,7 @@ struct bnx2x { #define BNX2X_MAX_COS 3 #define BNX2X_MAX_TX_COS 2 int num_queues; + int num_napi_queues; int disable_tpa; u32 rx_mode; @@ -1680,6 +1681,9 @@ struct bnx2x_func_init_params { continue; \ else +#define for_each_napi_rx_queue(bp, var) \ + for ((var) = 0; (var) < bp->num_napi_queues; (var)++) + /* Skip OOO FP */ #define for_each_tx_queue(bp, var) \ for ((var) = 0; (var) < BNX2X_NUM_QUEUES(bp); (var)++) \ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 413b665646b7..829befbc20a5 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1360,7 +1360,7 @@ void bnx2x_free_irq(struct bnx2x *bp) free_irq(bp->dev->irq, bp->dev); } -int __devinit bnx2x_enable_msix(struct bnx2x *bp) +int bnx2x_enable_msix(struct bnx2x *bp) { int msix_vec = 0, i, rc, req_cnt; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 11afe5d8819b..12dbbc4ff9bc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -29,6 +29,7 @@ extern int load_count[2][3]; /* per-path: 0-common, 1-port0, 2-port1 */ extern int num_queues; +extern int int_mode; /************************ Macros ********************************/ #define BNX2X_PCI_FREE(x, y, size) \ @@ -495,7 +496,7 @@ void bnx2x_netif_start(struct bnx2x *bp); * fills msix_table, requests vectors, updates num_queues * according to number of available vectors. */ -int __devinit bnx2x_enable_msix(struct bnx2x *bp); +int bnx2x_enable_msix(struct bnx2x *bp); /** * bnx2x_enable_msi - request msi mode from OS, updated internals accordingly @@ -788,8 +789,10 @@ static inline void bnx2x_add_all_napi(struct bnx2x *bp) { int i; + bp->num_napi_queues = bp->num_queues; + /* Add NAPI objects */ - for_each_rx_queue(bp, i) + for_each_napi_rx_queue(bp, i) netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, BNX2X_NAPI_WEIGHT); } @@ -798,10 +801,12 @@ static inline void bnx2x_del_all_napi(struct bnx2x *bp) { int i; - for_each_rx_queue(bp, i) + for_each_napi_rx_queue(bp, i) netif_napi_del(&bnx2x_fp(bp, i, napi)); } +void bnx2x_set_int_mode(struct bnx2x *bp); + static inline void bnx2x_disable_msi(struct bnx2x *bp) { if (bp->flags & USING_MSIX_FLAG) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index af84ebdeed7a..70c0881ce5a0 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2796,6 +2796,84 @@ static int bnx2x_set_rxfh_indir(struct net_device *dev, const u32 *indir) return bnx2x_config_rss_eth(bp, false); } +/** + * bnx2x_get_channels - gets the number of RSS queues. + * + * @dev: net device + * @channels: returns the number of max / current queues + */ +static void bnx2x_get_channels(struct net_device *dev, + struct ethtool_channels *channels) +{ + struct bnx2x *bp = netdev_priv(dev); + + channels->max_combined = BNX2X_MAX_RSS_COUNT(bp); + channels->combined_count = BNX2X_NUM_ETH_QUEUES(bp); +} + +/** + * bnx2x_change_num_queues - change the number of RSS queues. + * + * @bp: bnx2x private structure + * + * Re-configure interrupt mode to get the new number of MSI-X + * vectors and re-add NAPI objects. + */ +static void bnx2x_change_num_queues(struct bnx2x *bp, int num_rss) +{ + bnx2x_del_all_napi(bp); + bnx2x_disable_msi(bp); + BNX2X_NUM_QUEUES(bp) = num_rss + NON_ETH_CONTEXT_USE; + bnx2x_set_int_mode(bp); + bnx2x_add_all_napi(bp); +} + +/** + * bnx2x_set_channels - sets the number of RSS queues. + * + * @dev: net device + * @channels: includes the number of queues requested + */ +static int bnx2x_set_channels(struct net_device *dev, + struct ethtool_channels *channels) +{ + struct bnx2x *bp = netdev_priv(dev); + + + DP(BNX2X_MSG_ETHTOOL, + "set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n", + channels->rx_count, channels->tx_count, channels->other_count, + channels->combined_count); + + /* We don't support separate rx / tx channels. + * We don't allow setting 'other' channels. + */ + if (channels->rx_count || channels->tx_count || channels->other_count + || (channels->combined_count == 0) || + (channels->combined_count > BNX2X_MAX_RSS_COUNT(bp))) { + DP(BNX2X_MSG_ETHTOOL, "command parameters not supported\n"); + return -EINVAL; + } + + /* Check if there was a change in the active parameters */ + if (channels->combined_count == BNX2X_NUM_ETH_QUEUES(bp)) { + DP(BNX2X_MSG_ETHTOOL, "No change in active parameters\n"); + return 0; + } + + /* Set the requested number of queues in bp context. + * Note that the actual number of queues created during load may be + * less than requested if memory is low. + */ + if (unlikely(!netif_running(dev))) { + bnx2x_change_num_queues(bp, channels->combined_count); + return 0; + } + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + bnx2x_change_num_queues(bp, channels->combined_count); + return bnx2x_nic_load(bp, LOAD_NORMAL); +} + static const struct ethtool_ops bnx2x_ethtool_ops = { .get_settings = bnx2x_get_settings, .set_settings = bnx2x_set_settings, @@ -2827,6 +2905,8 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .get_rxfh_indir_size = bnx2x_get_rxfh_indir_size, .get_rxfh_indir = bnx2x_get_rxfh_indir, .set_rxfh_indir = bnx2x_set_rxfh_indir, + .get_channels = bnx2x_get_channels, + .set_channels = bnx2x_set_channels, .get_eee = bnx2x_get_eee, .set_eee = bnx2x_set_eee, }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 1ebcaa1183df..08c8d7d5e893 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -104,7 +104,7 @@ MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature"); #define INT_MODE_INTx 1 #define INT_MODE_MSI 2 -static int int_mode; +int int_mode; module_param(int_mode, int, 0); MODULE_PARM_DESC(int_mode, " Force interrupt mode other than MSI-X " "(1 INT#x; 2 MSI)"); @@ -7612,7 +7612,7 @@ int bnx2x_setup_leading(struct bnx2x *bp) * * In case of MSI-X it will also try to enable MSI-X. */ -static void __devinit bnx2x_set_int_mode(struct bnx2x *bp) +void bnx2x_set_int_mode(struct bnx2x *bp) { switch (int_mode) { case INT_MODE_MSI: @@ -7623,11 +7623,6 @@ static void __devinit bnx2x_set_int_mode(struct bnx2x *bp) BNX2X_DEV_INFO("set number of queues to 1\n"); break; default: - /* Set number of queues for MSI-X mode */ - bnx2x_set_num_queues(bp); - - BNX2X_DEV_INFO("set number of queues to %d\n", bp->num_queues); - /* if we can't use MSI-X we only need one fp, * so try to enable MSI-X with the requested number of fp's * and fallback to MSI or legacy INTx with one fp @@ -11883,8 +11878,12 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, #endif + + /* Set bp->num_queues for MSI-X mode*/ + bnx2x_set_num_queues(bp); + /* Configure interrupt mode: try to enable MSI-X/MSI if - * needed, set bp->num_queues appropriately. + * needed. */ bnx2x_set_int_mode(bp); -- cgit v1.2.3 From 9876879fce3081cd9f3f2b9e995954d423bcd863 Mon Sep 17 00:00:00 2001 From: Barak Witkowski Date: Tue, 19 Jun 2012 07:48:31 +0000 Subject: bnx2x: Support DCBX for all functions In multi-function device, allow configuring dcbx admin params from all drivers on a single physical port. Signed-off-by: Barak Witkowski Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 1 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 6 +++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c | 30 +++++++++++++++++------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h | 1 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 2 ++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h | 1 + 7 files changed, 31 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index fcc3b26c1c60..fa061cf60fee 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1318,6 +1318,7 @@ struct bnx2x { #define NO_FCOE_FLAG (1 << 15) #define BC_SUPPORTS_PFC_STATS (1 << 17) #define USING_SINGLE_MSIX_FLAG (1 << 20) +#define BC_SUPPORTS_DCBX_MSG_NON_PMF (1 << 21) #define NO_ISCSI(bp) ((bp)->flags & NO_ISCSI_FLAG) #define NO_ISCSI_OOO(bp) ((bp)->flags & NO_ISCSI_OOO_FLAG) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 829befbc20a5..00951b3aa62b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -2274,8 +2274,10 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) return -EBUSY; } - if (bp->state != BNX2X_STATE_DIAG) - bnx2x_dcbx_init(bp); + /* If PMF - send ADMIN DCBX msg to MFW to initiate DCBX FSM */ + if (bp->port.pmf && (bp->state != BNX2X_STATE_DIAG)) + bnx2x_dcbx_init(bp, false); + return 0; #ifndef BNX2X_STOP_ON_ERROR diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 12dbbc4ff9bc..daa894bd772a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -418,7 +418,7 @@ void bnx2x_ilt_set_info(struct bnx2x *bp); * * @bp: driver handle */ -void bnx2x_dcbx_init(struct bnx2x *bp); +void bnx2x_dcbx_init(struct bnx2x *bp, bool update_shmem); /** * bnx2x_set_power_state - set power state to the requested value. diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c index 4f9244bd7530..8a73374e52a7 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c @@ -972,23 +972,26 @@ void bnx2x_dcbx_init_params(struct bnx2x *bp) bp->dcbx_config_params.admin_default_priority = 0; } -void bnx2x_dcbx_init(struct bnx2x *bp) +void bnx2x_dcbx_init(struct bnx2x *bp, bool update_shmem) { u32 dcbx_lldp_params_offset = SHMEM_LLDP_DCBX_PARAMS_NONE; + /* only PMF can send ADMIN msg to MFW in old MFW versions */ + if ((!bp->port.pmf) && (!(bp->flags & BC_SUPPORTS_DCBX_MSG_NON_PMF))) + return; + if (bp->dcbx_enabled <= 0) return; /* validate: * chip of good for dcbx version, * dcb is wanted - * the function is pmf * shmem2 contains DCBX support fields */ DP(BNX2X_MSG_DCB, "dcb_state %d bp->port.pmf %d\n", bp->dcb_state, bp->port.pmf); - if (bp->dcb_state == BNX2X_DCB_STATE_ON && bp->port.pmf && + if (bp->dcb_state == BNX2X_DCB_STATE_ON && SHMEM2_HAS(bp, dcbx_lldp_params_offset)) { dcbx_lldp_params_offset = SHMEM2_RD(bp, dcbx_lldp_params_offset); @@ -999,12 +1002,23 @@ void bnx2x_dcbx_init(struct bnx2x *bp) bnx2x_update_drv_flags(bp, 1 << DRV_FLAGS_DCB_CONFIGURED, 0); if (SHMEM_LLDP_DCBX_PARAMS_NONE != dcbx_lldp_params_offset) { - bnx2x_dcbx_admin_mib_updated_params(bp, - dcbx_lldp_params_offset); + /* need HW lock to avoid scenario of two drivers + * writing in parallel to shmem + */ + bnx2x_acquire_hw_lock(bp, + HW_LOCK_RESOURCE_DCBX_ADMIN_MIB); + if (update_shmem) + bnx2x_dcbx_admin_mib_updated_params(bp, + dcbx_lldp_params_offset); /* Let HW start negotiation */ bnx2x_fw_command(bp, DRV_MSG_CODE_DCBX_ADMIN_PMF_MSG, 0); + /* release HW lock only after MFW acks that it finished + * reading values from shmem + */ + bnx2x_release_hw_lock(bp, + HW_LOCK_RESOURCE_DCBX_ADMIN_MIB); } } } @@ -2063,10 +2077,8 @@ static u8 bnx2x_dcbnl_set_all(struct net_device *netdev) "Handling parity error recovery. Try again later\n"); return 1; } - if (netif_running(bp->dev)) { - bnx2x_nic_unload(bp, UNLOAD_NORMAL); - rc = bnx2x_nic_load(bp, LOAD_NORMAL); - } + if (netif_running(bp->dev)) + bnx2x_dcbx_init(bp, true); DP(BNX2X_MSG_DCB, "set_dcbx_params done (%d)\n", rc); if (rc) return 1; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index c61aa37298a3..6b776309e0a1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1253,6 +1253,7 @@ struct drv_func_mb { #define DRV_MSG_CODE_DCBX_ADMIN_PMF_MSG 0xb0000000 #define DRV_MSG_CODE_DCBX_PMF_DRV_OK 0xb2000000 + #define REQ_BC_VER_4_DCBX_ADMIN_MSG_NON_PMF 0x00070401 #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 08c8d7d5e893..8ddc78e0d945 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9732,6 +9732,8 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) bp->flags |= (val >= REQ_BC_VER_4_PFC_STATS_SUPPORTED) ? BC_SUPPORTS_PFC_STATS : 0; + bp->flags |= (val >= REQ_BC_VER_4_DCBX_ADMIN_MSG_NON_PMF) ? + BC_SUPPORTS_DCBX_MSG_NON_PMF : 0; boot_mode = SHMEM_RD(bp, dev_info.port_feature_config[BP_PORT(bp)].mba_config) & PORT_FEATURE_MBA_BOOT_AGENT_TYPE_MASK; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index bfef98f666c9..a78e35683b03 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -5913,6 +5913,7 @@ #define MISC_REGISTERS_SPIO_OUTPUT_LOW 0 #define MISC_REGISTERS_SPIO_SET_POS 8 #define HW_LOCK_MAX_RESOURCE_VALUE 31 +#define HW_LOCK_RESOURCE_DCBX_ADMIN_MIB 13 #define HW_LOCK_RESOURCE_DRV_FLAGS 10 #define HW_LOCK_RESOURCE_GPIO 1 #define HW_LOCK_RESOURCE_MDIO 0 -- cgit v1.2.3 From 364f5b3a151942ad24521155355cc7d1d252fd55 Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 19 Jun 2012 07:48:32 +0000 Subject: bnx2x: Change date and version to 1.72.51-0 This change updates the date and version of the bnx2x driver. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index fa061cf60fee..7211cb07426e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -23,8 +23,8 @@ * (you will need to reboot afterwards) */ /* #define BNX2X_STOP_ON_ERROR */ -#define DRV_MODULE_VERSION "1.72.50-0" -#define DRV_MODULE_RELDATE "2012/04/23" +#define DRV_MODULE_VERSION "1.72.51-0" +#define DRV_MODULE_RELDATE "2012/06/18" #define BNX2X_BC_VER 0x040200 #if defined(CONFIG_DCB) -- cgit v1.2.3 From 0402788a6cda4e204a805e83eaaff64fef9e4418 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:03 +0000 Subject: team: make team_mode struct const Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 55 +++++++++++++++++++++---------- drivers/net/team/team_mode_activebackup.c | 2 +- drivers/net/team/team_mode_loadbalance.c | 2 +- drivers/net/team/team_mode_roundrobin.c | 2 +- 4 files changed, 41 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index c61ae35a53ce..bdf87a971382 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -371,13 +371,18 @@ static int team_option_set(struct team *team, static LIST_HEAD(mode_list); static DEFINE_SPINLOCK(mode_list_lock); -static struct team_mode *__find_mode(const char *kind) +struct team_mode_item { + struct list_head list; + const struct team_mode *mode; +}; + +static struct team_mode_item *__find_mode(const char *kind) { - struct team_mode *mode; + struct team_mode_item *mitem; - list_for_each_entry(mode, &mode_list, list) { - if (strcmp(mode->kind, kind) == 0) - return mode; + list_for_each_entry(mitem, &mode_list, list) { + if (strcmp(mitem->mode->kind, kind) == 0) + return mitem; } return NULL; } @@ -392,49 +397,65 @@ static bool is_good_mode_name(const char *name) return true; } -int team_mode_register(struct team_mode *mode) +int team_mode_register(const struct team_mode *mode) { int err = 0; + struct team_mode_item *mitem; if (!is_good_mode_name(mode->kind) || mode->priv_size > TEAM_MODE_PRIV_SIZE) return -EINVAL; + + mitem = kmalloc(sizeof(*mitem), GFP_KERNEL); + if (!mitem) + return -ENOMEM; + spin_lock(&mode_list_lock); if (__find_mode(mode->kind)) { err = -EEXIST; + kfree(mitem); goto unlock; } - list_add_tail(&mode->list, &mode_list); + mitem->mode = mode; + list_add_tail(&mitem->list, &mode_list); unlock: spin_unlock(&mode_list_lock); return err; } EXPORT_SYMBOL(team_mode_register); -int team_mode_unregister(struct team_mode *mode) +void team_mode_unregister(const struct team_mode *mode) { + struct team_mode_item *mitem; + spin_lock(&mode_list_lock); - list_del_init(&mode->list); + mitem = __find_mode(mode->kind); + if (mitem) { + list_del_init(&mitem->list); + kfree(mitem); + } spin_unlock(&mode_list_lock); - return 0; } EXPORT_SYMBOL(team_mode_unregister); -static struct team_mode *team_mode_get(const char *kind) +static const struct team_mode *team_mode_get(const char *kind) { - struct team_mode *mode; + struct team_mode_item *mitem; + const struct team_mode *mode = NULL; spin_lock(&mode_list_lock); - mode = __find_mode(kind); - if (!mode) { + mitem = __find_mode(kind); + if (!mitem) { spin_unlock(&mode_list_lock); request_module("team-mode-%s", kind); spin_lock(&mode_list_lock); - mode = __find_mode(kind); + mitem = __find_mode(kind); } - if (mode) + if (mitem) { + mode = mitem->mode; if (!try_module_get(mode->owner)) mode = NULL; + } spin_unlock(&mode_list_lock); return mode; @@ -523,7 +544,7 @@ static int __team_change_mode(struct team *team, static int team_change_mode(struct team *team, const char *kind) { - struct team_mode *new_mode; + const struct team_mode *new_mode; struct net_device *dev = team->dev; int err; diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index fd6bd03aaa89..acd925f070b5 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -108,7 +108,7 @@ static const struct team_mode_ops ab_mode_ops = { .port_leave = ab_port_leave, }; -static struct team_mode ab_mode = { +static const struct team_mode ab_mode = { .kind = "activebackup", .owner = THIS_MODULE, .priv_size = sizeof(struct ab_priv), diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 86e8183c8e3d..6452428a1d6c 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -148,7 +148,7 @@ static const struct team_mode_ops lb_mode_ops = { .transmit = lb_transmit, }; -static struct team_mode lb_mode = { +static const struct team_mode lb_mode = { .kind = "loadbalance", .owner = THIS_MODULE, .priv_size = sizeof(struct lb_priv), diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index 6abfbdc96be5..daafca2b2da4 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -81,7 +81,7 @@ static const struct team_mode_ops rr_mode_ops = { .port_change_mac = rr_port_change_mac, }; -static struct team_mode rr_mode = { +static const struct team_mode rr_mode = { .kind = "roundrobin", .owner = THIS_MODULE, .priv_size = sizeof(struct rr_priv), -- cgit v1.2.3 From d299cd51166c156c77250f4cdd6363b74fd2f77e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:04 +0000 Subject: team: for nomode use dummy struct team_mode That leaves team->mode and all its values valid so no checks would be needed (for example in team_mode_option_get()). Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index bdf87a971382..343f4ffaf8f0 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -479,6 +479,20 @@ rx_handler_result_t team_dummy_receive(struct team *team, return RX_HANDLER_ANOTHER; } +static const struct team_mode __team_no_mode = { + .kind = "*NOMODE*", +}; + +static bool team_is_mode_set(struct team *team) +{ + return team->mode != &__team_no_mode; +} + +static void team_set_no_mode(struct team *team) +{ + team->mode = &__team_no_mode; +} + static void team_adjust_ops(struct team *team) { /* @@ -487,13 +501,13 @@ static void team_adjust_ops(struct team *team) */ if (list_empty(&team->port_list) || - !team->mode || !team->mode->ops->transmit) + !team_is_mode_set(team) || !team->mode->ops->transmit) team->ops.transmit = team_dummy_transmit; else team->ops.transmit = team->mode->ops->transmit; if (list_empty(&team->port_list) || - !team->mode || !team->mode->ops->receive) + !team_is_mode_set(team) || !team->mode->ops->receive) team->ops.receive = team_dummy_receive; else team->ops.receive = team->mode->ops->receive; @@ -508,7 +522,7 @@ static int __team_change_mode(struct team *team, const struct team_mode *new_mode) { /* Check if mode was previously set and do cleanup if so */ - if (team->mode) { + if (team_is_mode_set(team)) { void (*exit_op)(struct team *team) = team->ops.exit; /* Clear ops area so no callback is called any longer */ @@ -518,7 +532,7 @@ static int __team_change_mode(struct team *team, if (exit_op) exit_op(team); team_mode_put(team->mode); - team->mode = NULL; + team_set_no_mode(team); /* zero private data area */ memset(&team->mode_priv, 0, sizeof(struct team) - offsetof(struct team, mode_priv)); @@ -553,7 +567,7 @@ static int team_change_mode(struct team *team, const char *kind) return -EBUSY; } - if (team->mode && strcmp(team->mode->kind, kind) == 0) { + if (team_is_mode_set(team) && strcmp(team->mode->kind, kind) == 0) { netdev_err(dev, "Unable to change to the same mode the team is in\n"); return -EINVAL; } @@ -912,11 +926,9 @@ static int team_port_del(struct team *team, struct net_device *port_dev) * Net device ops *****************/ -static const char team_no_mode_kind[] = "*NOMODE*"; - static int team_mode_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->data.str_val = team->mode ? team->mode->kind : team_no_mode_kind; + ctx->data.str_val = team->mode->kind; return 0; } @@ -1014,6 +1026,7 @@ static int team_init(struct net_device *dev) team->dev = dev; mutex_init(&team->lock); + team_set_no_mode(team); team->pcpu_stats = alloc_percpu(struct team_pcpu_stats); if (!team->pcpu_stats) -- cgit v1.2.3 From 5149ee58385bdfef260fb07a89a8ff0913be6b25 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:05 +0000 Subject: team: add mode priv to port Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 343f4ffaf8f0..dea2d8afa2f4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -793,7 +793,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev) return -EBUSY; } - port = kzalloc(sizeof(struct team_port), GFP_KERNEL); + port = kzalloc(sizeof(struct team_port) + team->mode->port_priv_size, + GFP_KERNEL); if (!port) return -ENOMEM; -- cgit v1.2.3 From 596e2024c42159fedf71b3d17fba78eed9b5bf10 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:06 +0000 Subject: team: lb: push hash counting into separate function Also squash hash into one byte Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team_mode_loadbalance.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 6452428a1d6c..a475b1304f9a 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -27,18 +27,27 @@ static struct lb_priv *lb_priv(struct team *team) return (struct lb_priv *) &team->mode_priv; } -static bool lb_transmit(struct team *team, struct sk_buff *skb) +static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv, + struct sk_buff *skb) { struct sk_filter *fp; + uint32_t lhash; + unsigned char *c; + + fp = rcu_dereference(lb_priv->fp); + if (unlikely(!fp)) + return 0; + lhash = SK_RUN_FILTER(fp, skb); + c = (char *) &lhash; + return c[0] ^ c[1] ^ c[2] ^ c[3]; +} + +static bool lb_transmit(struct team *team, struct sk_buff *skb) +{ struct team_port *port; - unsigned int hash; int port_index; - fp = rcu_dereference(lb_priv(team)->fp); - if (unlikely(!fp)) - goto drop; - hash = SK_RUN_FILTER(fp, skb); - port_index = hash % team->en_port_count; + port_index = lb_get_skb_hash(lb_priv(team), skb) % team->en_port_count; port = team_get_port_by_index_rcu(team, port_index); if (unlikely(!port)) goto drop; -- cgit v1.2.3 From f82b959d26557fe4ce283d3b27050d4b8268ef1e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:07 +0000 Subject: team: allow read/write-only options Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index dea2d8afa2f4..f50b8ca8dc94 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -346,6 +346,8 @@ static int team_option_get(struct team *team, struct team_option_inst *opt_inst, struct team_gsetter_ctx *ctx) { + if (!opt_inst->option->getter) + return -EOPNOTSUPP; return opt_inst->option->getter(team, ctx); } @@ -355,6 +357,8 @@ static int team_option_set(struct team *team, { int err; + if (!opt_inst->option->setter) + return -EOPNOTSUPP; err = opt_inst->option->setter(team, ctx); if (err) return err; -- cgit v1.2.3 From b13033262d2496e271444d5a09226a2be5ceb989 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:08 +0000 Subject: team: introduce array options Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 75 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index f50b8ca8dc94..32cb290fb800 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -90,6 +90,7 @@ struct team_option_inst { /* One for each option instance */ struct list_head list; struct team_option *option; struct team_port *port; /* != NULL if per-port */ + u32 array_index; bool changed; bool removed; }; @@ -106,22 +107,6 @@ static struct team_option *__team_find_option(struct team *team, return NULL; } -static int __team_option_inst_add(struct team *team, struct team_option *option, - struct team_port *port) -{ - struct team_option_inst *opt_inst; - - opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); - if (!opt_inst) - return -ENOMEM; - opt_inst->option = option; - opt_inst->port = port; - opt_inst->changed = true; - opt_inst->removed = false; - list_add_tail(&opt_inst->list, &team->option_inst_list); - return 0; -} - static void __team_option_inst_del(struct team_option_inst *opt_inst) { list_del(&opt_inst->list); @@ -139,14 +124,42 @@ static void __team_option_inst_del_option(struct team *team, } } +static int __team_option_inst_add(struct team *team, struct team_option *option, + struct team_port *port) +{ + struct team_option_inst *opt_inst; + unsigned int array_size; + unsigned int i; + + array_size = option->array_size; + if (!array_size) + array_size = 1; /* No array but still need one instance */ + + for (i = 0; i < array_size; i++) { + opt_inst = kmalloc(sizeof(*opt_inst), GFP_KERNEL); + if (!opt_inst) + return -ENOMEM; + opt_inst->option = option; + opt_inst->port = port; + opt_inst->array_index = i; + opt_inst->changed = true; + opt_inst->removed = false; + list_add_tail(&opt_inst->list, &team->option_inst_list); + } + return 0; +} + static int __team_option_inst_add_option(struct team *team, struct team_option *option) { struct team_port *port; int err; - if (!option->per_port) - return __team_option_inst_add(team, option, 0); + if (!option->per_port) { + err = __team_option_inst_add(team, option, 0); + if (err) + goto inst_del_option; + } list_for_each_entry(port, &team->port_list, list) { err = __team_option_inst_add(team, option, port); @@ -1567,6 +1580,11 @@ static int team_nl_fill_options_get(struct sk_buff *skb, opt_inst->port->dev->ifindex)) goto nla_put_failure; ctx.port = opt_inst->port; + if (opt_inst->option->array_size && + nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, + opt_inst->array_index)) + goto nla_put_failure; + ctx.array_index = opt_inst->array_index; switch (option->type) { case TEAM_OPTION_TYPE_U32: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) @@ -1668,10 +1686,12 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) { struct nlattr *opt_attrs[TEAM_ATTR_OPTION_MAX + 1]; - struct nlattr *attr_port_ifindex; + struct nlattr *attr; struct nlattr *attr_data; enum team_option_type opt_type; int opt_port_ifindex = 0; /* != 0 for per-port options */ + u32 opt_array_index = 0; + bool opt_is_array = false; struct team_option_inst *opt_inst; char *opt_name; bool opt_found = false; @@ -1713,9 +1733,15 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) } opt_name = nla_data(opt_attrs[TEAM_ATTR_OPTION_NAME]); - attr_port_ifindex = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; - if (attr_port_ifindex) - opt_port_ifindex = nla_get_u32(attr_port_ifindex); + attr = opt_attrs[TEAM_ATTR_OPTION_PORT_IFINDEX]; + if (attr) + opt_port_ifindex = nla_get_u32(attr); + + attr = opt_attrs[TEAM_ATTR_OPTION_ARRAY_INDEX]; + if (attr) { + opt_is_array = true; + opt_array_index = nla_get_u32(attr); + } list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct team_option *option = opt_inst->option; @@ -1726,10 +1752,13 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) opt_inst->port->dev->ifindex : 0; if (option->type != opt_type || strcmp(option->name, opt_name) || - tmp_ifindex != opt_port_ifindex) + tmp_ifindex != opt_port_ifindex || + (option->array_size && !opt_is_array) || + opt_inst->array_index != opt_array_index) continue; opt_found = true; ctx.port = opt_inst->port; + ctx.array_index = opt_inst->array_index; switch (opt_type) { case TEAM_OPTION_TYPE_U32: ctx.data.u32_val = nla_get_u32(attr_data); -- cgit v1.2.3 From 0d572e45f7f8ae3a27c82c5f0e352abf1dcb67f4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:09 +0000 Subject: team: comments: s/net\/drivers\/team/drivers\/net\/team/ Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 2 +- drivers/net/team/team_mode_activebackup.c | 2 +- drivers/net/team/team_mode_roundrobin.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 32cb290fb800..7ec53f81c1c4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1,5 +1,5 @@ /* - * net/drivers/team/team.c - Network team device driver + * drivers/net/team/team.c - Network team device driver * Copyright (c) 2011 Jiri Pirko * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index acd925f070b5..bcc7d6d2579a 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -1,5 +1,5 @@ /* - * net/drivers/team/team_mode_activebackup.c - Active-backup mode for team + * drivers/net/team/team_mode_activebackup.c - Active-backup mode for team * Copyright (c) 2011 Jiri Pirko * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c index daafca2b2da4..52dd0ec9cd1f 100644 --- a/drivers/net/team/team_mode_roundrobin.c +++ b/drivers/net/team/team_mode_roundrobin.c @@ -1,5 +1,5 @@ /* - * net/drivers/team/team_mode_roundrobin.c - Round-robin mode for team + * drivers/net/team/team_mode_roundrobin.c - Round-robin mode for team * Copyright (c) 2011 Jiri Pirko * * This program is free software; you can redistribute it and/or modify -- cgit v1.2.3 From 85d59a87248de90e3266e10dce99477b60f524c0 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:10 +0000 Subject: team: push array_index and port into separate structure Introduce struct team_option_inst_info and push option instance info there. It can be then easily passed to gsetter context and used for feature async option changes. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 68 +++++++++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 7ec53f81c1c4..cff8e253df72 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -89,8 +89,7 @@ static void team_refresh_port_linkup(struct team_port *port) struct team_option_inst { /* One for each option instance */ struct list_head list; struct team_option *option; - struct team_port *port; /* != NULL if per-port */ - u32 array_index; + struct team_option_inst_info info; bool changed; bool removed; }; @@ -130,6 +129,7 @@ static int __team_option_inst_add(struct team *team, struct team_option *option, struct team_option_inst *opt_inst; unsigned int array_size; unsigned int i; + int err; array_size = option->array_size; if (!array_size) @@ -140,11 +140,17 @@ static int __team_option_inst_add(struct team *team, struct team_option *option, if (!opt_inst) return -ENOMEM; opt_inst->option = option; - opt_inst->port = port; - opt_inst->array_index = i; + opt_inst->info.port = port; + opt_inst->info.array_index = i; opt_inst->changed = true; opt_inst->removed = false; list_add_tail(&opt_inst->list, &team->option_inst_list); + if (option->init) { + err = option->init(team, &opt_inst->info); + if (err) + return err; + } + } return 0; } @@ -193,7 +199,7 @@ static void __team_option_inst_del_port(struct team *team, list_for_each_entry_safe(opt_inst, tmp, &team->option_inst_list, list) { if (opt_inst->option->per_port && - opt_inst->port == port) + opt_inst->info.port == port) __team_option_inst_del(opt_inst); } } @@ -224,7 +230,7 @@ static void __team_option_inst_mark_removed_port(struct team *team, struct team_option_inst *opt_inst; list_for_each_entry(opt_inst, &team->option_inst_list, list) { - if (opt_inst->port == port) { + if (opt_inst->info.port == port) { opt_inst->changed = true; opt_inst->removed = true; } @@ -958,39 +964,47 @@ static int team_mode_option_set(struct team *team, struct team_gsetter_ctx *ctx) static int team_port_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->data.bool_val = team_port_enabled(ctx->port); + struct team_port *port = ctx->info->port; + + ctx->data.bool_val = team_port_enabled(port); return 0; } static int team_port_en_option_set(struct team *team, struct team_gsetter_ctx *ctx) { + struct team_port *port = ctx->info->port; + if (ctx->data.bool_val) - team_port_enable(team, ctx->port); + team_port_enable(team, port); else - team_port_disable(team, ctx->port); + team_port_disable(team, port); return 0; } static int team_user_linkup_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->data.bool_val = ctx->port->user.linkup; + struct team_port *port = ctx->info->port; + + ctx->data.bool_val = port->user.linkup; return 0; } static int team_user_linkup_option_set(struct team *team, struct team_gsetter_ctx *ctx) { - ctx->port->user.linkup = ctx->data.bool_val; - team_refresh_port_linkup(ctx->port); + struct team_port *port = ctx->info->port; + + port->user.linkup = ctx->data.bool_val; + team_refresh_port_linkup(port); return 0; } static int team_user_linkup_en_option_get(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_port *port = ctx->port; + struct team_port *port = ctx->info->port; ctx->data.bool_val = port->user.linkup_enabled; return 0; @@ -999,10 +1013,10 @@ static int team_user_linkup_en_option_get(struct team *team, static int team_user_linkup_en_option_set(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_port *port = ctx->port; + struct team_port *port = ctx->info->port; port->user.linkup_enabled = ctx->data.bool_val; - team_refresh_port_linkup(ctx->port); + team_refresh_port_linkup(port); return 0; } @@ -1557,6 +1571,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb, list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct nlattr *option_item; struct team_option *option = opt_inst->option; + struct team_option_inst_info *opt_inst_info; struct team_gsetter_ctx ctx; /* Include only changed options if fill all mode is not on */ @@ -1575,16 +1590,18 @@ static int team_nl_fill_options_get(struct sk_buff *skb, if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) goto nla_put_failure; - if (opt_inst->port && + + opt_inst_info = &opt_inst->info; + if (opt_inst_info->port && nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, - opt_inst->port->dev->ifindex)) + opt_inst_info->port->dev->ifindex)) goto nla_put_failure; - ctx.port = opt_inst->port; if (opt_inst->option->array_size && nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, - opt_inst->array_index)) + opt_inst_info->array_index)) goto nla_put_failure; - ctx.array_index = opt_inst->array_index; + ctx.info = opt_inst_info; + switch (option->type) { case TEAM_OPTION_TYPE_U32: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) @@ -1746,19 +1763,20 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct team_option *option = opt_inst->option; struct team_gsetter_ctx ctx; + struct team_option_inst_info *opt_inst_info; int tmp_ifindex; - tmp_ifindex = opt_inst->port ? - opt_inst->port->dev->ifindex : 0; + opt_inst_info = &opt_inst->info; + tmp_ifindex = opt_inst_info->port ? + opt_inst_info->port->dev->ifindex : 0; if (option->type != opt_type || strcmp(option->name, opt_name) || tmp_ifindex != opt_port_ifindex || (option->array_size && !opt_is_array) || - opt_inst->array_index != opt_array_index) + opt_inst_info->array_index != opt_array_index) continue; opt_found = true; - ctx.port = opt_inst->port; - ctx.array_index = opt_inst->array_index; + ctx.info = opt_inst_info; switch (opt_type) { case TEAM_OPTION_TYPE_U32: ctx.data.u32_val = nla_get_u32(attr_data); -- cgit v1.2.3 From 0f1aad2b7f01d88782fbf4ab08b13a7d92b9b6b2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:11 +0000 Subject: team: allow async option changes This patch adds two exported functions. One allows to mark option instance as changed and the second processes change check and does transfer of changed options to userspace. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index cff8e253df72..7988ba099b94 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -82,6 +82,7 @@ static void team_refresh_port_linkup(struct team_port *port) port->state.linkup; } + /******************* * Options handling *******************/ @@ -387,6 +388,22 @@ static int team_option_set(struct team *team, return err; } +void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info) +{ + struct team_option_inst *opt_inst; + + opt_inst = container_of(opt_inst_info, struct team_option_inst, info); + opt_inst->changed = true; +} +EXPORT_SYMBOL(team_option_inst_set_change); + +void team_options_change_check(struct team *team) +{ + __team_options_change_check(team); +} +EXPORT_SYMBOL(team_options_change_check); + + /**************** * Mode handling ****************/ @@ -2051,6 +2068,7 @@ static void team_port_change_check(struct team_port *port, bool linkup) mutex_unlock(&team->lock); } + /************************************ * Net device notifier event handler ************************************/ -- cgit v1.2.3 From 75db986a6b93dd93e543387720b88f350ce38d30 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:12 +0000 Subject: team: fix error path in team_nl_fill_options_get() genlmsg_cancel() needs to be called in case nest fails Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 7988ba099b94..82ded1820caf 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1583,7 +1583,7 @@ static int team_nl_fill_options_get(struct sk_buff *skb, goto nla_put_failure; option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION); if (!option_list) - return -EMSGSIZE; + goto nla_put_failure; list_for_each_entry(opt_inst, &team->option_inst_list, list) { struct nlattr *option_item; -- cgit v1.2.3 From 3221c64603d9843d8a28dfd8e678e46625d5d807 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:13 +0000 Subject: team: fix error path in team_nl_fill_port_list_get() genlmsg_cancel() needs to be called in case nest fails Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 82ded1820caf..e977f440c378 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1849,7 +1849,7 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb, goto nla_put_failure; port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT); if (!port_list) - return -EMSGSIZE; + goto nla_put_failure; list_for_each_entry(port, &team->port_list, list) { struct nlattr *port_item; -- cgit v1.2.3 From 01048d9a293cbe9aac00fd014b9754bbc7eb136f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:14 +0000 Subject: team: allow to specify one option instance to be send to userspace No need to walk through option instance list and look for ->changed == true when called knows exactly what one option instance changed. Also use lists to pass option instances needed to be present in netlink message. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 210 ++++++++++++++++++++++++++++-------------------- 1 file changed, 124 insertions(+), 86 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index e977f440c378..da72f41d3914 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -89,6 +89,7 @@ static void team_refresh_port_linkup(struct team_port *port) struct team_option_inst { /* One for each option instance */ struct list_head list; + struct list_head tmp_list; struct team_option *option; struct team_option_inst_info info; bool changed; @@ -319,6 +320,8 @@ static void __team_options_unregister(struct team *team, } static void __team_options_change_check(struct team *team); +static void __team_option_inst_change(struct team *team, + struct team_option_inst *opt_inst); int team_options_register(struct team *team, const struct team_option *option, @@ -383,8 +386,7 @@ static int team_option_set(struct team *team, if (err) return err; - opt_inst->changed = true; - __team_options_change_check(team); + __team_option_inst_change(team, opt_inst); return err; } @@ -1565,9 +1567,95 @@ err_fill: return err; } +static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team, + struct team_option_inst *opt_inst) +{ + struct nlattr *option_item; + struct team_option *option = opt_inst->option; + struct team_option_inst_info *opt_inst_info; + struct team_gsetter_ctx ctx; + int err; + + option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); + if (!option_item) + goto nla_put_failure; + if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) + goto nla_put_failure; + if (opt_inst->changed) { + if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) + goto nla_put_failure; + opt_inst->changed = false; + } + if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) + goto nla_put_failure; + + opt_inst_info = &opt_inst->info; + if (opt_inst_info->port && + nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, + opt_inst_info->port->dev->ifindex)) + goto nla_put_failure; + if (opt_inst->option->array_size && + nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, + opt_inst_info->array_index)) + goto nla_put_failure; + ctx.info = opt_inst_info; + + switch (option->type) { + case TEAM_OPTION_TYPE_U32: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) + goto nla_put_failure; + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; + if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val)) + goto nla_put_failure; + break; + case TEAM_OPTION_TYPE_STRING: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) + goto nla_put_failure; + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; + if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, + ctx.data.str_val)) + goto nla_put_failure; + break; + case TEAM_OPTION_TYPE_BINARY: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) + goto nla_put_failure; + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; + if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len, + ctx.data.bin_val.ptr)) + goto nla_put_failure; + break; + case TEAM_OPTION_TYPE_BOOL: + if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) + goto nla_put_failure; + err = team_option_get(team, opt_inst, &ctx); + if (err) + goto errout; + if (ctx.data.bool_val && + nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) + goto nla_put_failure; + break; + default: + BUG(); + } + nla_nest_end(skb, option_item); + return 0; + +nla_put_failure: + err = -EMSGSIZE; +errout: + return err; +} + static int team_nl_fill_options_get(struct sk_buff *skb, u32 pid, u32 seq, int flags, - struct team *team, bool fillall) + struct team *team, + struct list_head *sel_opt_inst_list) { struct nlattr *option_list; void *hdr; @@ -1585,85 +1673,10 @@ static int team_nl_fill_options_get(struct sk_buff *skb, if (!option_list) goto nla_put_failure; - list_for_each_entry(opt_inst, &team->option_inst_list, list) { - struct nlattr *option_item; - struct team_option *option = opt_inst->option; - struct team_option_inst_info *opt_inst_info; - struct team_gsetter_ctx ctx; - - /* Include only changed options if fill all mode is not on */ - if (!fillall && !opt_inst->changed) - continue; - option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); - if (!option_item) - goto nla_put_failure; - if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) - goto nla_put_failure; - if (opt_inst->changed) { - if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) - goto nla_put_failure; - opt_inst->changed = false; - } - if (opt_inst->removed && - nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) - goto nla_put_failure; - - opt_inst_info = &opt_inst->info; - if (opt_inst_info->port && - nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, - opt_inst_info->port->dev->ifindex)) - goto nla_put_failure; - if (opt_inst->option->array_size && - nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, - opt_inst_info->array_index)) - goto nla_put_failure; - ctx.info = opt_inst_info; - - switch (option->type) { - case TEAM_OPTION_TYPE_U32: - if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; - if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, - ctx.data.u32_val)) - goto nla_put_failure; - break; - case TEAM_OPTION_TYPE_STRING: - if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; - if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, - ctx.data.str_val)) - goto nla_put_failure; - break; - case TEAM_OPTION_TYPE_BINARY: - if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; - if (nla_put(skb, TEAM_ATTR_OPTION_DATA, - ctx.data.bin_val.len, ctx.data.bin_val.ptr)) - goto nla_put_failure; - break; - case TEAM_OPTION_TYPE_BOOL: - if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; - if (ctx.data.bool_val && - nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) - goto nla_put_failure; - break; - default: - BUG(); - } - nla_nest_end(skb, option_item); + list_for_each_entry(opt_inst, sel_opt_inst_list, tmp_list) { + err = team_nl_fill_one_option_get(skb, team, opt_inst); + if (err) + goto errout; } nla_nest_end(skb, option_list); @@ -1680,9 +1693,14 @@ static int team_nl_fill_options_get_all(struct sk_buff *skb, struct genl_info *info, int flags, struct team *team) { + struct team_option_inst *opt_inst; + LIST_HEAD(sel_opt_inst_list); + + list_for_each_entry(opt_inst, &team->option_inst_list, list) + list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); return team_nl_fill_options_get(skb, info->snd_pid, info->snd_seq, NLM_F_ACK, - team, true); + team, &sel_opt_inst_list); } static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) @@ -1941,7 +1959,8 @@ static struct genl_multicast_group team_change_event_mcgrp = { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, }; -static int team_nl_send_event_options_get(struct team *team) +static int team_nl_send_event_options_get(struct team *team, + struct list_head *sel_opt_inst_list) { struct sk_buff *skb; int err; @@ -1951,7 +1970,7 @@ static int team_nl_send_event_options_get(struct team *team) if (!skb) return -ENOMEM; - err = team_nl_fill_options_get(skb, 0, 0, 0, team, false); + err = team_nl_fill_options_get(skb, 0, 0, 0, team, sel_opt_inst_list); if (err < 0) goto err_fill; @@ -2021,12 +2040,31 @@ static void team_nl_fini(void) static void __team_options_change_check(struct team *team) { int err; + struct team_option_inst *opt_inst; + LIST_HEAD(sel_opt_inst_list); - err = team_nl_send_event_options_get(team); + list_for_each_entry(opt_inst, &team->option_inst_list, list) { + if (opt_inst->changed) + list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); + } + err = team_nl_send_event_options_get(team, &sel_opt_inst_list); if (err) netdev_warn(team->dev, "Failed to send options change via netlink\n"); } +static void __team_option_inst_change(struct team *team, + struct team_option_inst *sel_opt_inst) +{ + int err; + LIST_HEAD(sel_opt_inst_list); + + sel_opt_inst->changed = true; + list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list); + err = team_nl_send_event_options_get(team, &sel_opt_inst_list); + if (err) + netdev_warn(team->dev, "Failed to send option change via netlink\n"); +} + /* rtnl lock is held */ static void __team_port_change_check(struct team_port *port, bool linkup) { -- cgit v1.2.3 From f88725ffb069fef0ea3d47381d33c928f4c48cba Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:15 +0000 Subject: team: pass NULL to __team_option_inst_add() instead of 0 Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index da72f41d3914..eb18ac93095a 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -164,7 +164,7 @@ static int __team_option_inst_add_option(struct team *team, int err; if (!option->per_port) { - err = __team_option_inst_add(team, option, 0); + err = __team_option_inst_add(team, option, NULL); if (err) goto inst_del_option; } -- cgit v1.2.3 From 4bccfd17e1f77593e99d5321c48c704a0a43ab68 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:16 +0000 Subject: team: add port_[enabled/disabled] mode callbacks Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index eb18ac93095a..bc76f946e071 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -714,6 +714,8 @@ static void team_port_enable(struct team *team, port->index = team->en_port_count++; hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); + if (team->ops.port_enabled) + team->ops.port_enabled(team, port); } static void __reconstruct_port_hlist(struct team *team, int rm_index) @@ -737,6 +739,8 @@ static void team_port_disable(struct team *team, if (!team_port_enabled(port)) return; + if (team->ops.port_disabled) + team->ops.port_disabled(team, port); hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, rm_index); team->en_port_count--; -- cgit v1.2.3 From ab8250d70063f77929fc404c02390a1f64d66416 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:17 +0000 Subject: team: lb: introduce infrastructure for userspace driven tx loadbalancing Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team_mode_loadbalance.c | 517 ++++++++++++++++++++++++++++++- 1 file changed, 500 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index a475b1304f9a..33e30edeb202 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -17,18 +17,163 @@ #include #include +struct lb_priv; + +typedef struct team_port *lb_select_tx_port_func_t(struct team *, + struct lb_priv *, + struct sk_buff *, + unsigned char); + +#define LB_TX_HASHTABLE_SIZE 256 /* hash is a char */ + +struct lb_stats { + u64 tx_bytes; +}; + +struct lb_pcpu_stats { + struct lb_stats hash_stats[LB_TX_HASHTABLE_SIZE]; + struct u64_stats_sync syncp; +}; + +struct lb_stats_info { + struct lb_stats stats; + struct lb_stats last_stats; + struct team_option_inst_info *opt_inst_info; +}; + +struct lb_port_mapping { + struct team_port __rcu *port; + struct team_option_inst_info *opt_inst_info; +}; + +struct lb_priv_ex { + struct team *team; + struct lb_port_mapping tx_hash_to_port_mapping[LB_TX_HASHTABLE_SIZE]; + struct sock_fprog *orig_fprog; + struct { + unsigned int refresh_interval; /* in tenths of second */ + struct delayed_work refresh_dw; + struct lb_stats_info info[LB_TX_HASHTABLE_SIZE]; + } stats; +}; + struct lb_priv { struct sk_filter __rcu *fp; - struct sock_fprog *orig_fprog; + lb_select_tx_port_func_t __rcu *select_tx_port_func; + struct lb_pcpu_stats __percpu *pcpu_stats; + struct lb_priv_ex *ex; /* priv extension */ }; -static struct lb_priv *lb_priv(struct team *team) +static struct lb_priv *get_lb_priv(struct team *team) { return (struct lb_priv *) &team->mode_priv; } -static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv, - struct sk_buff *skb) +struct lb_port_priv { + struct lb_stats __percpu *pcpu_stats; + struct lb_stats_info stats_info; +}; + +static struct lb_port_priv *get_lb_port_priv(struct team_port *port) +{ + return (struct lb_port_priv *) &port->mode_priv; +} + +#define LB_HTPM_PORT_BY_HASH(lp_priv, hash) \ + (lb_priv)->ex->tx_hash_to_port_mapping[hash].port + +#define LB_HTPM_OPT_INST_INFO_BY_HASH(lp_priv, hash) \ + (lb_priv)->ex->tx_hash_to_port_mapping[hash].opt_inst_info + +static void lb_tx_hash_to_port_mapping_null_port(struct team *team, + struct team_port *port) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + bool changed = false; + int i; + + for (i = 0; i < LB_TX_HASHTABLE_SIZE; i++) { + struct lb_port_mapping *pm; + + pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; + if (pm->port == port) { + rcu_assign_pointer(pm->port, NULL); + team_option_inst_set_change(pm->opt_inst_info); + changed = true; + } + } + if (changed) + team_options_change_check(team); +} + +/* Basic tx selection based solely by hash */ +static struct team_port *lb_hash_select_tx_port(struct team *team, + struct lb_priv *lb_priv, + struct sk_buff *skb, + unsigned char hash) +{ + int port_index; + + port_index = hash % team->en_port_count; + return team_get_port_by_index_rcu(team, port_index); +} + +/* Hash to port mapping select tx port */ +static struct team_port *lb_htpm_select_tx_port(struct team *team, + struct lb_priv *lb_priv, + struct sk_buff *skb, + unsigned char hash) +{ + return rcu_dereference(LB_HTPM_PORT_BY_HASH(lb_priv, hash)); +} + +struct lb_select_tx_port { + char *name; + lb_select_tx_port_func_t *func; +}; + +static const struct lb_select_tx_port lb_select_tx_port_list[] = { + { + .name = "hash", + .func = lb_hash_select_tx_port, + }, + { + .name = "hash_to_port_mapping", + .func = lb_htpm_select_tx_port, + }, +}; +#define LB_SELECT_TX_PORT_LIST_COUNT ARRAY_SIZE(lb_select_tx_port_list) + +static char *lb_select_tx_port_get_name(lb_select_tx_port_func_t *func) +{ + int i; + + for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) { + const struct lb_select_tx_port *item; + + item = &lb_select_tx_port_list[i]; + if (item->func == func) + return item->name; + } + return NULL; +} + +static lb_select_tx_port_func_t *lb_select_tx_port_get_func(const char *name) +{ + int i; + + for (i = 0; i < LB_SELECT_TX_PORT_LIST_COUNT; i++) { + const struct lb_select_tx_port *item; + + item = &lb_select_tx_port_list[i]; + if (!strcmp(item->name, name)) + return item->func; + } + return NULL; +} + +static unsigned int lb_get_skb_hash(struct lb_priv *lb_priv, + struct sk_buff *skb) { struct sk_filter *fp; uint32_t lhash; @@ -42,18 +187,40 @@ static unsigned char lb_get_skb_hash(struct lb_priv *lb_priv, return c[0] ^ c[1] ^ c[2] ^ c[3]; } +static void lb_update_tx_stats(unsigned int tx_bytes, struct lb_priv *lb_priv, + struct lb_port_priv *lb_port_priv, + unsigned char hash) +{ + struct lb_pcpu_stats *pcpu_stats; + struct lb_stats *port_stats; + struct lb_stats *hash_stats; + + pcpu_stats = this_cpu_ptr(lb_priv->pcpu_stats); + port_stats = this_cpu_ptr(lb_port_priv->pcpu_stats); + hash_stats = &pcpu_stats->hash_stats[hash]; + u64_stats_update_begin(&pcpu_stats->syncp); + port_stats->tx_bytes += tx_bytes; + hash_stats->tx_bytes += tx_bytes; + u64_stats_update_end(&pcpu_stats->syncp); +} + static bool lb_transmit(struct team *team, struct sk_buff *skb) { + struct lb_priv *lb_priv = get_lb_priv(team); + lb_select_tx_port_func_t *select_tx_port_func; struct team_port *port; - int port_index; + unsigned char hash; + unsigned int tx_bytes = skb->len; - port_index = lb_get_skb_hash(lb_priv(team), skb) % team->en_port_count; - port = team_get_port_by_index_rcu(team, port_index); + hash = lb_get_skb_hash(lb_priv, skb); + select_tx_port_func = rcu_dereference(lb_priv->select_tx_port_func); + port = select_tx_port_func(team, lb_priv, skb, hash); if (unlikely(!port)) goto drop; skb->dev = port->dev; if (dev_queue_xmit(skb)) return false; + lb_update_tx_stats(tx_bytes, lb_priv, get_lb_port_priv(port), hash); return true; drop: @@ -63,14 +230,16 @@ drop: static int lb_bpf_func_get(struct team *team, struct team_gsetter_ctx *ctx) { - if (!lb_priv(team)->orig_fprog) { + struct lb_priv *lb_priv = get_lb_priv(team); + + if (!lb_priv->ex->orig_fprog) { ctx->data.bin_val.len = 0; ctx->data.bin_val.ptr = NULL; return 0; } - ctx->data.bin_val.len = lb_priv(team)->orig_fprog->len * + ctx->data.bin_val.len = lb_priv->ex->orig_fprog->len * sizeof(struct sock_filter); - ctx->data.bin_val.ptr = lb_priv(team)->orig_fprog->filter; + ctx->data.bin_val.ptr = lb_priv->ex->orig_fprog->filter; return 0; } @@ -103,6 +272,7 @@ static void __fprog_destroy(struct sock_fprog *fprog) static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) { + struct lb_priv *lb_priv = get_lb_priv(team); struct sk_filter *fp = NULL; struct sock_fprog *fprog = NULL; int err; @@ -119,14 +289,232 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) } } - if (lb_priv(team)->orig_fprog) { + if (lb_priv->ex->orig_fprog) { /* Clear old filter data */ - __fprog_destroy(lb_priv(team)->orig_fprog); - sk_unattached_filter_destroy(lb_priv(team)->fp); + __fprog_destroy(lb_priv->ex->orig_fprog); + sk_unattached_filter_destroy(lb_priv->fp); } - rcu_assign_pointer(lb_priv(team)->fp, fp); - lb_priv(team)->orig_fprog = fprog; + rcu_assign_pointer(lb_priv->fp, fp); + lb_priv->ex->orig_fprog = fprog; + return 0; +} + +static int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + char *name; + + name = lb_select_tx_port_get_name(lb_priv->select_tx_port_func); + BUG_ON(!name); + ctx->data.str_val = name; + return 0; +} + +static int lb_tx_method_set(struct team *team, struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + lb_select_tx_port_func_t *func; + + func = lb_select_tx_port_get_func(ctx->data.str_val); + if (!func) + return -EINVAL; + rcu_assign_pointer(lb_priv->select_tx_port_func, func); + return 0; +} + +static int lb_tx_hash_to_port_mapping_init(struct team *team, + struct team_option_inst_info *info) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + unsigned char hash = info->array_index; + + LB_HTPM_OPT_INST_INFO_BY_HASH(lb_priv, hash) = info; + return 0; +} + +static int lb_tx_hash_to_port_mapping_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + struct team_port *port; + unsigned char hash = ctx->info->array_index; + + port = LB_HTPM_PORT_BY_HASH(lb_priv, hash); + ctx->data.u32_val = port ? port->dev->ifindex : 0; + return 0; +} + +static int lb_tx_hash_to_port_mapping_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + struct team_port *port; + unsigned char hash = ctx->info->array_index; + + list_for_each_entry(port, &team->port_list, list) { + if (ctx->data.u32_val == port->dev->ifindex) { + rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash), + port); + return 0; + } + } + return -ENODEV; +} + +static int lb_hash_stats_init(struct team *team, + struct team_option_inst_info *info) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + unsigned char hash = info->array_index; + + lb_priv->ex->stats.info[hash].opt_inst_info = info; + return 0; +} + +static int lb_hash_stats_get(struct team *team, struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + unsigned char hash = ctx->info->array_index; + + ctx->data.bin_val.ptr = &lb_priv->ex->stats.info[hash].stats; + ctx->data.bin_val.len = sizeof(struct lb_stats); + return 0; +} + +static int lb_port_stats_init(struct team *team, + struct team_option_inst_info *info) +{ + struct team_port *port = info->port; + struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); + + lb_port_priv->stats_info.opt_inst_info = info; + return 0; +} + +static int lb_port_stats_get(struct team *team, struct team_gsetter_ctx *ctx) +{ + struct team_port *port = ctx->info->port; + struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); + + ctx->data.bin_val.ptr = &lb_port_priv->stats_info.stats; + ctx->data.bin_val.len = sizeof(struct lb_stats); + return 0; +} + +static void __lb_stats_info_refresh_prepare(struct lb_stats_info *s_info) +{ + memcpy(&s_info->last_stats, &s_info->stats, sizeof(struct lb_stats)); + memset(&s_info->stats, 0, sizeof(struct lb_stats)); +} + +static bool __lb_stats_info_refresh_check(struct lb_stats_info *s_info, + struct team *team) +{ + if (memcmp(&s_info->last_stats, &s_info->stats, + sizeof(struct lb_stats))) { + team_option_inst_set_change(s_info->opt_inst_info); + return true; + } + return false; +} + +static void __lb_one_cpu_stats_add(struct lb_stats *acc_stats, + struct lb_stats *cpu_stats, + struct u64_stats_sync *syncp) +{ + unsigned int start; + struct lb_stats tmp; + + do { + start = u64_stats_fetch_begin_bh(syncp); + tmp.tx_bytes = cpu_stats->tx_bytes; + } while (u64_stats_fetch_retry_bh(syncp, start)); + acc_stats->tx_bytes += tmp.tx_bytes; +} + +static void lb_stats_refresh(struct work_struct *work) +{ + struct team *team; + struct lb_priv *lb_priv; + struct lb_priv_ex *lb_priv_ex; + struct lb_pcpu_stats *pcpu_stats; + struct lb_stats *stats; + struct lb_stats_info *s_info; + struct team_port *port; + bool changed = false; + int i; + int j; + + lb_priv_ex = container_of(work, struct lb_priv_ex, + stats.refresh_dw.work); + + team = lb_priv_ex->team; + lb_priv = get_lb_priv(team); + + if (!mutex_trylock(&team->lock)) { + schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, 0); + return; + } + + for (j = 0; j < LB_TX_HASHTABLE_SIZE; j++) { + s_info = &lb_priv->ex->stats.info[j]; + __lb_stats_info_refresh_prepare(s_info); + for_each_possible_cpu(i) { + pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); + stats = &pcpu_stats->hash_stats[j]; + __lb_one_cpu_stats_add(&s_info->stats, stats, + &pcpu_stats->syncp); + } + changed |= __lb_stats_info_refresh_check(s_info, team); + } + + list_for_each_entry(port, &team->port_list, list) { + struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); + + s_info = &lb_port_priv->stats_info; + __lb_stats_info_refresh_prepare(s_info); + for_each_possible_cpu(i) { + pcpu_stats = per_cpu_ptr(lb_priv->pcpu_stats, i); + stats = per_cpu_ptr(lb_port_priv->pcpu_stats, i); + __lb_one_cpu_stats_add(&s_info->stats, stats, + &pcpu_stats->syncp); + } + changed |= __lb_stats_info_refresh_check(s_info, team); + } + + if (changed) + team_options_change_check(team); + + schedule_delayed_work(&lb_priv_ex->stats.refresh_dw, + (lb_priv_ex->stats.refresh_interval * HZ) / 10); + + mutex_unlock(&team->lock); +} + +static int lb_stats_refresh_interval_get(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + + ctx->data.u32_val = lb_priv->ex->stats.refresh_interval; + return 0; +} + +static int lb_stats_refresh_interval_set(struct team *team, + struct team_gsetter_ctx *ctx) +{ + struct lb_priv *lb_priv = get_lb_priv(team); + unsigned int interval; + + interval = ctx->data.u32_val; + if (lb_priv->ex->stats.refresh_interval == interval) + return 0; + lb_priv->ex->stats.refresh_interval = interval; + if (interval) + schedule_delayed_work(&lb_priv->ex->stats.refresh_dw, 0); + else + cancel_delayed_work(&lb_priv->ex->stats.refresh_dw); return 0; } @@ -137,23 +525,117 @@ static const struct team_option lb_options[] = { .getter = lb_bpf_func_get, .setter = lb_bpf_func_set, }, + { + .name = "lb_tx_method", + .type = TEAM_OPTION_TYPE_STRING, + .getter = lb_tx_method_get, + .setter = lb_tx_method_set, + }, + { + .name = "lb_tx_hash_to_port_mapping", + .array_size = LB_TX_HASHTABLE_SIZE, + .type = TEAM_OPTION_TYPE_U32, + .init = lb_tx_hash_to_port_mapping_init, + .getter = lb_tx_hash_to_port_mapping_get, + .setter = lb_tx_hash_to_port_mapping_set, + }, + { + .name = "lb_hash_stats", + .array_size = LB_TX_HASHTABLE_SIZE, + .type = TEAM_OPTION_TYPE_BINARY, + .init = lb_hash_stats_init, + .getter = lb_hash_stats_get, + }, + { + .name = "lb_port_stats", + .per_port = true, + .type = TEAM_OPTION_TYPE_BINARY, + .init = lb_port_stats_init, + .getter = lb_port_stats_get, + }, + { + .name = "lb_stats_refresh_interval", + .type = TEAM_OPTION_TYPE_U32, + .getter = lb_stats_refresh_interval_get, + .setter = lb_stats_refresh_interval_set, + }, }; static int lb_init(struct team *team) { - return team_options_register(team, lb_options, - ARRAY_SIZE(lb_options)); + struct lb_priv *lb_priv = get_lb_priv(team); + lb_select_tx_port_func_t *func; + int err; + + /* set default tx port selector */ + func = lb_select_tx_port_get_func("hash"); + BUG_ON(!func); + rcu_assign_pointer(lb_priv->select_tx_port_func, func); + + lb_priv->ex = kzalloc(sizeof(*lb_priv->ex), GFP_KERNEL); + if (!lb_priv->ex) + return -ENOMEM; + lb_priv->ex->team = team; + + lb_priv->pcpu_stats = alloc_percpu(struct lb_pcpu_stats); + if (!lb_priv->pcpu_stats) { + err = -ENOMEM; + goto err_alloc_pcpu_stats; + } + + INIT_DELAYED_WORK(&lb_priv->ex->stats.refresh_dw, lb_stats_refresh); + + err = team_options_register(team, lb_options, ARRAY_SIZE(lb_options)); + if (err) + goto err_options_register; + return 0; + +err_options_register: + free_percpu(lb_priv->pcpu_stats); +err_alloc_pcpu_stats: + kfree(lb_priv->ex); + return err; } static void lb_exit(struct team *team) { + struct lb_priv *lb_priv = get_lb_priv(team); + team_options_unregister(team, lb_options, ARRAY_SIZE(lb_options)); + cancel_delayed_work_sync(&lb_priv->ex->stats.refresh_dw); + free_percpu(lb_priv->pcpu_stats); + kfree(lb_priv->ex); +} + +static int lb_port_enter(struct team *team, struct team_port *port) +{ + struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); + + lb_port_priv->pcpu_stats = alloc_percpu(struct lb_stats); + if (!lb_port_priv->pcpu_stats) + return -ENOMEM; + return 0; +} + +static void lb_port_leave(struct team *team, struct team_port *port) +{ + struct lb_port_priv *lb_port_priv = get_lb_port_priv(port); + + free_percpu(lb_port_priv->pcpu_stats); +} + +static void lb_port_disabled(struct team *team, struct team_port *port) +{ + lb_tx_hash_to_port_mapping_null_port(team, port); } static const struct team_mode_ops lb_mode_ops = { .init = lb_init, .exit = lb_exit, + .port_enter = lb_port_enter, + .port_leave = lb_port_leave, + .port_disabled = lb_port_disabled, .transmit = lb_transmit, }; @@ -161,6 +643,7 @@ static const struct team_mode lb_mode = { .kind = "loadbalance", .owner = THIS_MODULE, .priv_size = sizeof(struct lb_priv), + .port_priv_size = sizeof(struct lb_port_priv), .ops = &lb_mode_ops, }; -- cgit v1.2.3 From 9b00cf2d1024d683cb2e206109e356dcac968568 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:18 +0000 Subject: team: implement multipart netlink messages for options transfers Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 198 ++++++++++++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 82 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index bc76f946e071..9e9d3e57a2f6 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1571,102 +1571,128 @@ err_fill: return err; } +typedef int team_nl_send_func_t(struct sk_buff *skb, + struct team *team, u32 pid); + +static int team_nl_send_unicast(struct sk_buff *skb, struct team *team, u32 pid) +{ + return genlmsg_unicast(dev_net(team->dev), skb, pid); +} + static int team_nl_fill_one_option_get(struct sk_buff *skb, struct team *team, struct team_option_inst *opt_inst) { struct nlattr *option_item; struct team_option *option = opt_inst->option; - struct team_option_inst_info *opt_inst_info; + struct team_option_inst_info *opt_inst_info = &opt_inst->info; struct team_gsetter_ctx ctx; int err; + ctx.info = opt_inst_info; + err = team_option_get(team, opt_inst, &ctx); + if (err) + return err; + option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION); if (!option_item) - goto nla_put_failure; - if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) - goto nla_put_failure; - if (opt_inst->changed) { - if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) - goto nla_put_failure; - opt_inst->changed = false; - } - if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) - goto nla_put_failure; + return -EMSGSIZE; - opt_inst_info = &opt_inst->info; + if (nla_put_string(skb, TEAM_ATTR_OPTION_NAME, option->name)) + goto nest_cancel; if (opt_inst_info->port && nla_put_u32(skb, TEAM_ATTR_OPTION_PORT_IFINDEX, opt_inst_info->port->dev->ifindex)) - goto nla_put_failure; + goto nest_cancel; if (opt_inst->option->array_size && nla_put_u32(skb, TEAM_ATTR_OPTION_ARRAY_INDEX, opt_inst_info->array_index)) - goto nla_put_failure; - ctx.info = opt_inst_info; + goto nest_cancel; switch (option->type) { case TEAM_OPTION_TYPE_U32: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; + goto nest_cancel; if (nla_put_u32(skb, TEAM_ATTR_OPTION_DATA, ctx.data.u32_val)) - goto nla_put_failure; + goto nest_cancel; break; case TEAM_OPTION_TYPE_STRING: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; + goto nest_cancel; if (nla_put_string(skb, TEAM_ATTR_OPTION_DATA, ctx.data.str_val)) - goto nla_put_failure; + goto nest_cancel; break; case TEAM_OPTION_TYPE_BINARY: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_BINARY)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; + goto nest_cancel; if (nla_put(skb, TEAM_ATTR_OPTION_DATA, ctx.data.bin_val.len, ctx.data.bin_val.ptr)) - goto nla_put_failure; + goto nest_cancel; break; case TEAM_OPTION_TYPE_BOOL: if (nla_put_u8(skb, TEAM_ATTR_OPTION_TYPE, NLA_FLAG)) - goto nla_put_failure; - err = team_option_get(team, opt_inst, &ctx); - if (err) - goto errout; + goto nest_cancel; if (ctx.data.bool_val && nla_put_flag(skb, TEAM_ATTR_OPTION_DATA)) - goto nla_put_failure; + goto nest_cancel; break; default: BUG(); } + if (opt_inst->removed && nla_put_flag(skb, TEAM_ATTR_OPTION_REMOVED)) + goto nest_cancel; + if (opt_inst->changed) { + if (nla_put_flag(skb, TEAM_ATTR_OPTION_CHANGED)) + goto nest_cancel; + opt_inst->changed = false; + } nla_nest_end(skb, option_item); return 0; -nla_put_failure: - err = -EMSGSIZE; -errout: - return err; +nest_cancel: + nla_nest_cancel(skb, option_item); + return -EMSGSIZE; +} + +static int __send_and_alloc_skb(struct sk_buff **pskb, + struct team *team, u32 pid, + team_nl_send_func_t *send_func) +{ + int err; + + if (*pskb) { + err = send_func(*pskb, team, pid); + if (err) + return err; + } + *pskb = genlmsg_new(NLMSG_DEFAULT_SIZE - GENL_HDRLEN, GFP_KERNEL); + if (!*pskb) + return -ENOMEM; + return 0; } -static int team_nl_fill_options_get(struct sk_buff *skb, - u32 pid, u32 seq, int flags, - struct team *team, +static int team_nl_send_options_get(struct team *team, u32 pid, u32 seq, + int flags, team_nl_send_func_t *send_func, struct list_head *sel_opt_inst_list) { struct nlattr *option_list; + struct nlmsghdr *nlh; void *hdr; struct team_option_inst *opt_inst; int err; + struct sk_buff *skb = NULL; + bool incomplete; + int i; - hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags, + opt_inst = list_first_entry(sel_opt_inst_list, + struct team_option_inst, tmp_list); + +start_again: + err = __send_and_alloc_skb(&skb, team, pid, send_func); + if (err) + return err; + + hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI, TEAM_CMD_OPTIONS_GET); if (IS_ERR(hdr)) return PTR_ERR(hdr); @@ -1677,46 +1703,62 @@ static int team_nl_fill_options_get(struct sk_buff *skb, if (!option_list) goto nla_put_failure; - list_for_each_entry(opt_inst, sel_opt_inst_list, tmp_list) { + i = 0; + incomplete = false; + list_for_each_entry_from(opt_inst, sel_opt_inst_list, tmp_list) { err = team_nl_fill_one_option_get(skb, team, opt_inst); - if (err) + if (err) { + if (err == -EMSGSIZE) { + if (!i) + goto errout; + incomplete = true; + break; + } goto errout; + } + i++; } nla_nest_end(skb, option_list); - return genlmsg_end(skb, hdr); + genlmsg_end(skb, hdr); + if (incomplete) + goto start_again; + +send_done: + nlh = nlmsg_put(skb, pid, seq, NLMSG_DONE, 0, flags | NLM_F_MULTI); + if (!nlh) { + err = __send_and_alloc_skb(&skb, team, pid, send_func); + if (err) + goto errout; + goto send_done; + } + + return send_func(skb, team, pid); nla_put_failure: err = -EMSGSIZE; errout: genlmsg_cancel(skb, hdr); + nlmsg_free(skb); return err; } -static int team_nl_fill_options_get_all(struct sk_buff *skb, - struct genl_info *info, int flags, - struct team *team) -{ - struct team_option_inst *opt_inst; - LIST_HEAD(sel_opt_inst_list); - - list_for_each_entry(opt_inst, &team->option_inst_list, list) - list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); - return team_nl_fill_options_get(skb, info->snd_pid, - info->snd_seq, NLM_F_ACK, - team, &sel_opt_inst_list); -} - static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) { struct team *team; + struct team_option_inst *opt_inst; int err; + LIST_HEAD(sel_opt_inst_list); team = team_nl_team_get(info); if (!team) return -EINVAL; - err = team_nl_send_generic(info, team, team_nl_fill_options_get_all); + list_for_each_entry(opt_inst, &team->option_inst_list, list) + list_add_tail(&opt_inst->tmp_list, &sel_opt_inst_list); + err = team_nl_send_options_get(team, info->snd_pid, info->snd_seq, + NLM_F_ACK, team_nl_send_unicast, + &sel_opt_inst_list); team_nl_team_put(team); @@ -1963,28 +2005,18 @@ static struct genl_multicast_group team_change_event_mcgrp = { .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME, }; +static int team_nl_send_multicast(struct sk_buff *skb, + struct team *team, u32 pid) +{ + return genlmsg_multicast_netns(dev_net(team->dev), skb, 0, + team_change_event_mcgrp.id, GFP_KERNEL); +} + static int team_nl_send_event_options_get(struct team *team, struct list_head *sel_opt_inst_list) { - struct sk_buff *skb; - int err; - struct net *net = dev_net(team->dev); - - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - err = team_nl_fill_options_get(skb, 0, 0, 0, team, sel_opt_inst_list); - if (err < 0) - goto err_fill; - - err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id, - GFP_KERNEL); - return err; - -err_fill: - nlmsg_free(skb); - return err; + return team_nl_send_options_get(team, 0, 0, 0, team_nl_send_multicast, + sel_opt_inst_list); } static int team_nl_send_event_port_list_get(struct team *team) @@ -2053,7 +2085,8 @@ static void __team_options_change_check(struct team *team) } err = team_nl_send_event_options_get(team, &sel_opt_inst_list); if (err) - netdev_warn(team->dev, "Failed to send options change via netlink\n"); + netdev_warn(team->dev, "Failed to send options change via netlink (err %d)\n", + err); } static void __team_option_inst_change(struct team *team, @@ -2066,7 +2099,8 @@ static void __team_option_inst_change(struct team *team, list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list); err = team_nl_send_event_options_get(team, &sel_opt_inst_list); if (err) - netdev_warn(team->dev, "Failed to send option change via netlink\n"); + netdev_warn(team->dev, "Failed to send option change via netlink (err %d)\n", + err); } /* rtnl lock is held */ -- cgit v1.2.3 From 35b384bd14f91c74e358e02969ee7bb2542e6d78 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:19 +0000 Subject: team: ensure correct order of netlink messages delivery currently, when port is created and per-port options are present, there options are sent to userspace with ifindex of port which userspace does not know about. Port add message goes right after. This patch corrects message ordering so userspace would not be confused. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 9e9d3e57a2f6..a7b391dfb3b0 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -347,24 +347,6 @@ void team_options_unregister(struct team *team, } EXPORT_SYMBOL(team_options_unregister); -static int team_option_port_add(struct team *team, struct team_port *port) -{ - int err; - - err = __team_option_inst_add_port(team, port); - if (err) - return err; - __team_options_change_check(team); - return 0; -} - -static void team_option_port_del(struct team *team, struct team_port *port) -{ - __team_option_inst_mark_removed_port(team, port); - __team_options_change_check(team); - __team_option_inst_del_port(team, port); -} - static int team_option_get(struct team *team, struct team_option_inst *opt_inst, struct team_gsetter_ctx *ctx) @@ -891,7 +873,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) goto err_handler_register; } - err = team_option_port_add(team, port); + err = __team_option_inst_add_port(team, port); if (err) { netdev_err(dev, "Device %s failed to add per-port options\n", portname); @@ -904,6 +886,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev) team_adjust_ops(team); __team_compute_features(team); __team_port_change_check(port, !!netif_carrier_ok(port_dev)); + __team_options_change_check(team); netdev_info(dev, "Port device %s added\n", portname); @@ -947,12 +930,14 @@ static int team_port_del(struct team *team, struct net_device *port_dev) return -ENOENT; } + __team_option_inst_mark_removed_port(team, port); + __team_options_change_check(team); + __team_option_inst_del_port(team, port); port->removed = true; __team_port_change_check(port, false); team_port_disable(team, port); list_del_rcu(&port->list); team_adjust_ops(team); - team_option_port_del(team, port); netdev_rx_handler_unregister(port_dev); netdev_set_master(port_dev, NULL); vlan_vids_del_by_dev(port_dev, dev); -- cgit v1.2.3 From 2fcdb2c9e6598e6ced232eca7aa4a61342691f3b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:20 +0000 Subject: team: allow to send multiple set events in one message When multiple sets are done, event message is generated for each. This patch accumulates these messages into one. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index a7b391dfb3b0..3a4a74be52d9 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -320,8 +320,6 @@ static void __team_options_unregister(struct team *team, } static void __team_options_change_check(struct team *team); -static void __team_option_inst_change(struct team *team, - struct team_option_inst *opt_inst); int team_options_register(struct team *team, const struct team_option *option, @@ -360,16 +358,9 @@ static int team_option_set(struct team *team, struct team_option_inst *opt_inst, struct team_gsetter_ctx *ctx) { - int err; - if (!opt_inst->option->setter) return -EOPNOTSUPP; - err = opt_inst->option->setter(team, ctx); - if (err) - return err; - - __team_option_inst_change(team, opt_inst); - return err; + return opt_inst->option->setter(team, ctx); } void team_option_inst_set_change(struct team_option_inst_info *opt_inst_info) @@ -1750,12 +1741,16 @@ static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info) return err; } +static int team_nl_send_event_options_get(struct team *team, + struct list_head *sel_opt_inst_list); + static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) { struct team *team; int err = 0; int i; struct nlattr *nl_option; + LIST_HEAD(opt_inst_list); team = team_nl_team_get(info); if (!team) @@ -1867,6 +1862,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) err = team_option_set(team, opt_inst, &ctx); if (err) goto team_put; + opt_inst->changed = true; + list_add(&opt_inst->tmp_list, &opt_inst_list); } if (!opt_found) { err = -ENOENT; @@ -1874,6 +1871,8 @@ static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info) } } + err = team_nl_send_event_options_get(team, &opt_inst_list); + team_put: team_nl_team_put(team); @@ -2074,20 +2073,6 @@ static void __team_options_change_check(struct team *team) err); } -static void __team_option_inst_change(struct team *team, - struct team_option_inst *sel_opt_inst) -{ - int err; - LIST_HEAD(sel_opt_inst_list); - - sel_opt_inst->changed = true; - list_add(&sel_opt_inst->tmp_list, &sel_opt_inst_list); - err = team_nl_send_event_options_get(team, &sel_opt_inst_list); - if (err) - netdev_warn(team->dev, "Failed to send option change via netlink (err %d)\n", - err); -} - /* rtnl lock is held */ static void __team_port_change_check(struct team_port *port, bool linkup) { -- cgit v1.2.3 From d1904fbd881e43cde2ec024117efaba83f8d9996 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 19 Jun 2012 05:54:21 +0000 Subject: team: use rcu_dereference_bh() in tx path Should be used instead of rcu_dereference, since rcu_read_lock_bh is held. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team_mode_activebackup.c | 2 +- drivers/net/team/team_mode_loadbalance.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index bcc7d6d2579a..2fe02a8713ea 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -40,7 +40,7 @@ static bool ab_transmit(struct team *team, struct sk_buff *skb) { struct team_port *active_port; - active_port = rcu_dereference(ab_priv(team)->active_port); + active_port = rcu_dereference_bh(ab_priv(team)->active_port); if (unlikely(!active_port)) goto drop; skb->dev = active_port->dev; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 33e30edeb202..45cc0951aa48 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -124,7 +124,7 @@ static struct team_port *lb_htpm_select_tx_port(struct team *team, struct sk_buff *skb, unsigned char hash) { - return rcu_dereference(LB_HTPM_PORT_BY_HASH(lb_priv, hash)); + return rcu_dereference_bh(LB_HTPM_PORT_BY_HASH(lb_priv, hash)); } struct lb_select_tx_port { @@ -179,7 +179,7 @@ static unsigned int lb_get_skb_hash(struct lb_priv *lb_priv, uint32_t lhash; unsigned char *c; - fp = rcu_dereference(lb_priv->fp); + fp = rcu_dereference_bh(lb_priv->fp); if (unlikely(!fp)) return 0; lhash = SK_RUN_FILTER(fp, skb); @@ -213,7 +213,7 @@ static bool lb_transmit(struct team *team, struct sk_buff *skb) unsigned int tx_bytes = skb->len; hash = lb_get_skb_hash(lb_priv, skb); - select_tx_port_func = rcu_dereference(lb_priv->select_tx_port_func); + select_tx_port_func = rcu_dereference_bh(lb_priv->select_tx_port_func); port = select_tx_port_func(team, lb_priv, skb, hash); if (unlikely(!port)) goto drop; -- cgit v1.2.3 From 853c24f79dd6f4a3d6d7b52f235fe121aee08b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 19 Jun 2012 00:41:59 +0000 Subject: net: qmi_wwan: define a structure for driver specific state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usbnet allocates a fixed size array for minidriver specific state. Naming the fields and taking advantage of type checking is a bit more failsafe than casting array elements each time they are referenced. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 49 +++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 3b206786b5e7..c7b9be81ad08 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -54,6 +54,13 @@ * corresponding management interface */ +/* driver specific data */ +struct qmi_wwan_state { + struct usb_driver *subdriver; + atomic_t pmcount; + unsigned long unused[3]; +}; + static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; @@ -65,9 +72,11 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) struct usb_cdc_ether_desc *cdc_ether = NULL; u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE; u32 found = 0; - atomic_t *pmcount = (void *)&dev->data[1]; + struct qmi_wwan_state *info = (void *)&dev->data; + + BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); - atomic_set(pmcount, 0); + atomic_set(&info->pmcount, 0); /* * assume a data interface has no additional descriptors and @@ -177,12 +186,12 @@ err: /* using a counter to merge subdriver requests with our own into a combined state */ static int qmi_wwan_manage_power(struct usbnet *dev, int on) { - atomic_t *pmcount = (void *)&dev->data[1]; + struct qmi_wwan_state *info = (void *)&dev->data; int rv = 0; - dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(pmcount), on); + dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); - if ((on && atomic_add_return(1, pmcount) == 1) || (!on && atomic_dec_and_test(pmcount))) { + if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { /* need autopm_get/put here to ensure the usbcore sees the new value */ rv = usb_autopm_get_interface(dev->intf); if (rv < 0) @@ -212,7 +221,7 @@ static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) { int rv; struct usb_driver *subdriver = NULL; - atomic_t *pmcount = (void *)&dev->data[1]; + struct qmi_wwan_state *info = (void *)&dev->data; /* ZTE makes devices where the interface descriptors and endpoint * configurations of two or more interfaces are identical, even @@ -228,7 +237,7 @@ static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) goto err; } - atomic_set(pmcount, 0); + atomic_set(&info->pmcount, 0); /* collect all three endpoints */ rv = usbnet_get_endpoints(dev, intf); @@ -251,7 +260,7 @@ static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) dev->status = NULL; /* save subdriver struct for suspend/resume wrappers */ - dev->data[0] = (unsigned long)subdriver; + info->subdriver = subdriver; err: return rv; @@ -282,12 +291,12 @@ err: static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf) { - struct usb_driver *subdriver = (void *)dev->data[0]; + struct qmi_wwan_state *info = (void *)&dev->data; - if (subdriver && subdriver->disconnect) - subdriver->disconnect(intf); + if (info->subdriver && info->subdriver->disconnect) + info->subdriver->disconnect(intf); - dev->data[0] = (unsigned long)NULL; + info->subdriver = NULL; } /* suspend/resume wrappers calling both usbnet and the cdc-wdm @@ -299,15 +308,15 @@ static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *int static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); - struct usb_driver *subdriver = (void *)dev->data[0]; + struct qmi_wwan_state *info = (void *)&dev->data; int ret; ret = usbnet_suspend(intf, message); if (ret < 0) goto err; - if (subdriver && subdriver->suspend) - ret = subdriver->suspend(intf, message); + if (info->subdriver && info->subdriver->suspend) + ret = info->subdriver->suspend(intf, message); if (ret < 0) usbnet_resume(intf); err: @@ -317,16 +326,16 @@ err: static int qmi_wwan_resume(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); - struct usb_driver *subdriver = (void *)dev->data[0]; + struct qmi_wwan_state *info = (void *)&dev->data; int ret = 0; - if (subdriver && subdriver->resume) - ret = subdriver->resume(intf); + if (info->subdriver && info->subdriver->resume) + ret = info->subdriver->resume(intf); if (ret < 0) goto err; ret = usbnet_resume(intf); - if (ret < 0 && subdriver && subdriver->resume && subdriver->suspend) - subdriver->suspend(intf, PMSG_SUSPEND); + if (ret < 0 && info->subdriver && info->subdriver->resume && info->subdriver->suspend) + info->subdriver->suspend(intf, PMSG_SUSPEND); err: return ret; } -- cgit v1.2.3 From f47cd1360f36e599815650522986673b9aa83393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 19 Jun 2012 00:42:00 +0000 Subject: net: qmi_wwan: rearranging to prepare for code sharing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the subdriver registration code can be reused for devices with separate control and data interfaces. Move the code a bit around to prepare for such reuse. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 128 +++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index c7b9be81ad08..6fcf54d43eab 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -58,9 +58,80 @@ struct qmi_wwan_state { struct usb_driver *subdriver; atomic_t pmcount; - unsigned long unused[3]; + unsigned long unused; + struct usb_interface *control; + struct usb_interface *data; }; +/* using a counter to merge subdriver requests with our own into a combined state */ +static int qmi_wwan_manage_power(struct usbnet *dev, int on) +{ + struct qmi_wwan_state *info = (void *)&dev->data; + int rv = 0; + + dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); + + if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { + /* need autopm_get/put here to ensure the usbcore sees the new value */ + rv = usb_autopm_get_interface(dev->intf); + if (rv < 0) + goto err; + dev->intf->needs_remote_wakeup = on; + usb_autopm_put_interface(dev->intf); + } +err: + return rv; +} + +static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) +{ + struct usbnet *dev = usb_get_intfdata(intf); + return qmi_wwan_manage_power(dev, on); +} + +/* collect all three endpoints and register subdriver */ +static int qmi_wwan_register_subdriver(struct usbnet *dev) +{ + int rv; + struct usb_driver *subdriver = NULL; + struct qmi_wwan_state *info = (void *)&dev->data; + + /* collect bulk endpoints */ + rv = usbnet_get_endpoints(dev, info->data); + if (rv < 0) + goto err; + + /* update status endpoint if separate control interface */ + if (info->control != info->data) + dev->status = &info->control->cur_altsetting->endpoint[0]; + + /* require interrupt endpoint for subdriver */ + if (!dev->status) { + rv = -EINVAL; + goto err; + } + + /* for subdriver power management */ + atomic_set(&info->pmcount, 0); + + /* register subdriver */ + subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, 512, &qmi_wwan_cdc_wdm_manage_power); + if (IS_ERR(subdriver)) { + dev_err(&info->control->dev, "subdriver registration failed\n"); + rv = PTR_ERR(subdriver); + goto err; + } + + /* prevent usbnet from using status endpoint */ + dev->status = NULL; + + /* save subdriver struct for suspend/resume wrappers */ + info->subdriver = subdriver; + +err: + return rv; +} + static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; @@ -183,32 +254,6 @@ err: return status; } -/* using a counter to merge subdriver requests with our own into a combined state */ -static int qmi_wwan_manage_power(struct usbnet *dev, int on) -{ - struct qmi_wwan_state *info = (void *)&dev->data; - int rv = 0; - - dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); - - if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { - /* need autopm_get/put here to ensure the usbcore sees the new value */ - rv = usb_autopm_get_interface(dev->intf); - if (rv < 0) - goto err; - dev->intf->needs_remote_wakeup = on; - usb_autopm_put_interface(dev->intf); - } -err: - return rv; -} - -static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) -{ - struct usbnet *dev = usb_get_intfdata(intf); - return qmi_wwan_manage_power(dev, on); -} - /* Some devices combine the "control" and "data" functions into a * single interface with all three endpoints: interrupt + bulk in and * out @@ -220,7 +265,6 @@ static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) { int rv; - struct usb_driver *subdriver = NULL; struct qmi_wwan_state *info = (void *)&dev->data; /* ZTE makes devices where the interface descriptors and endpoint @@ -237,30 +281,10 @@ static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) goto err; } - atomic_set(&info->pmcount, 0); - - /* collect all three endpoints */ - rv = usbnet_get_endpoints(dev, intf); - if (rv < 0) - goto err; - - /* require interrupt endpoint for subdriver */ - if (!dev->status) { - rv = -EINVAL; - goto err; - } - - subdriver = usb_cdc_wdm_register(intf, &dev->status->desc, 512, &qmi_wwan_cdc_wdm_manage_power); - if (IS_ERR(subdriver)) { - rv = PTR_ERR(subdriver); - goto err; - } - - /* can't let usbnet use the interrupt endpoint */ - dev->status = NULL; - - /* save subdriver struct for suspend/resume wrappers */ - info->subdriver = subdriver; + /* control and data is shared */ + info->control = intf; + info->data = intf; + rv = qmi_wwan_register_subdriver(dev); err: return rv; -- cgit v1.2.3 From 230718bda1be24119d9e25623d90fa5da3079aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 19 Jun 2012 00:42:01 +0000 Subject: net: qmi_wwan: bind to both control and data interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Always bind to control interface regardless of whether it is a shared interface or not. A QMI/wwan function is required to provide both a control interface (QMI) and a data interface (wwan). All devices supported by this driver do so. But the vendors may choose to use different USB descriptor layouts, and some vendors even allow the same device to present different layouts. Most of these devices use a USB descriptor layout with a single USB interface for both control and data. But some split control and data into two interfaces, bound together by a CDC Union descriptor on the control interface. Before the cdc-wdm subdriver support was added, this split was used to let cdc-wdm drive the QMI control interface and qmi_wwan drive the wwna data interface. This split driver model has a number of issues: - qmi_wwan must match on the data interface descriptor, which often are indistiguishable from data interfaces belonging to other CDC (like) functions like ACM - supporting a single QMI/wwan function requires adding the device to two drivers - syncronizing the probes among a number of drivers, to ensure selecting the correct driver, is difficult unless all drivers match on the same interface This patch resolves these problems by using the same probing mechanism as cdc-ether for devices with a two- interface USB descriptor layout. This makes the driver behave consistently, supporting both the control and data part of the QMI/wwan function, regardless of the USB descriptors. Cc: Thomas Schäfer Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 131 ++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 68 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 6fcf54d43eab..05571fcbd70a 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1,6 +1,10 @@ /* * Copyright (c) 2012 Bjørn Mork * + * The probing code is heavily inspired by cdc_ether, which is: + * Copyright (C) 2003-2005 by David Brownell + * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) + * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. @@ -15,11 +19,7 @@ #include #include -/* The name of the CDC Device Management driver */ -#define DM_DRIVER "cdc_wdm" - -/* - * This driver supports wwan (3G/LTE/?) devices using a vendor +/* This driver supports wwan (3G/LTE/?) devices using a vendor * specific management protocol called Qualcomm MSM Interface (QMI) - * in addition to the more common AT commands over serial interface * management @@ -31,27 +31,16 @@ * management protocol is used in place of the standard CDC * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE * + * Alternatively, control and data functions can be combined in a + * single USB interface. + * * Handling a protocol like QMI is out of the scope for any driver. - * It can be exported as a character device using the cdc-wdm driver, - * which will enable userspace applications ("modem managers") to - * handle it. This may be required to use the network interface - * provided by the driver. + * It is exported as a character device using the cdc-wdm driver as + * a subdriver, enabling userspace applications ("modem managers") to + * handle it. * * These devices may alternatively/additionally be configured using AT - * commands on any of the serial interfaces driven by the option driver - * - * This driver binds only to the data ("slave") interface to enable - * the cdc-wdm driver to bind to the control interface. It still - * parses the CDC functional descriptors on the control interface to - * a) verify that this is indeed a handled interface (CDC Union - * header lists it as slave) - * b) get MAC address and other ethernet config from the CDC Ethernet - * header - * c) enable user bind requests against the control interface, which - * is the common way to bind to CDC Ethernet Control Model type - * interfaces - * d) provide a hint to the user about which interface is the - * corresponding management interface + * commands on a serial interface */ /* driver specific data */ @@ -135,7 +124,6 @@ err: static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) { int status = -1; - struct usb_interface *control = NULL; u8 *buf = intf->cur_altsetting->extra; int len = intf->cur_altsetting->extralen; struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; @@ -143,27 +131,14 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) struct usb_cdc_ether_desc *cdc_ether = NULL; u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE; u32 found = 0; + struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); - atomic_set(&info->pmcount, 0); - - /* - * assume a data interface has no additional descriptors and - * that the control and data interface are numbered - * consecutively - this holds for the Huawei device at least - */ - if (len == 0 && desc->bInterfaceNumber > 0) { - control = usb_ifnum_to_if(dev->udev, desc->bInterfaceNumber - 1); - if (!control) - goto err; - - buf = control->cur_altsetting->extra; - len = control->cur_altsetting->extralen; - dev_dbg(&intf->dev, "guessing \"control\" => %s, \"data\" => this\n", - dev_name(&control->dev)); - } + /* require a single interrupt status endpoint for subdriver */ + if (intf->cur_altsetting->desc.bNumEndpoints != 1) + goto err; while (len > 3) { struct usb_descriptor_header *h = (void *)buf; @@ -227,10 +202,17 @@ next_desc: goto err; } - /* give the user a helpful hint if trying to bind to the wrong interface */ - if (cdc_union && desc->bInterfaceNumber == cdc_union->bMasterInterface0) { - dev_err(&intf->dev, "leaving \"control\" interface for " DM_DRIVER " - try binding to %s instead!\n", - dev_name(&usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0)->dev)); + /* verify CDC Union */ + if (desc->bInterfaceNumber != cdc_union->bMasterInterface0) { + dev_err(&intf->dev, "bogus CDC Union: master=%u\n", cdc_union->bMasterInterface0); + goto err; + } + + /* need to save these for unbind */ + info->control = intf; + info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); + if (!info->data) { + dev_err(&intf->dev, "bogus CDC Union: slave=%u\n", cdc_union->bSlaveInterface0); goto err; } @@ -240,15 +222,16 @@ next_desc: usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); } - /* success! point the user to the management interface */ - if (control) - dev_info(&intf->dev, "Use \"" DM_DRIVER "\" for QMI interface %s\n", - dev_name(&control->dev)); - - /* XXX: add a sysfs symlink somewhere to help management applications find it? */ + /* claim data interface and set it up */ + status = usb_driver_claim_interface(driver, info->data, dev); + if (status < 0) + goto err; - /* collect bulk endpoints now that we know intf == "data" interface */ - status = usbnet_get_endpoints(dev, intf); + status = qmi_wwan_register_subdriver(dev); + if (status < 0) { + usb_set_intfdata(info->data, NULL); + usb_driver_release_interface(driver, info->data); + } err: return status; @@ -257,11 +240,7 @@ err: /* Some devices combine the "control" and "data" functions into a * single interface with all three endpoints: interrupt + bulk in and * out - * - * Setting up cdc-wdm as a subdriver owning the interrupt endpoint - * will let it provide userspace access to the encapsulated QMI - * protocol without interfering with the usbnet operations. - */ + */ static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf) { int rv; @@ -313,14 +292,30 @@ err: return rv; } -static void qmi_wwan_unbind_shared(struct usbnet *dev, struct usb_interface *intf) +static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) { struct qmi_wwan_state *info = (void *)&dev->data; + struct usb_driver *driver = driver_of(intf); + struct usb_interface *other; if (info->subdriver && info->subdriver->disconnect) - info->subdriver->disconnect(intf); + info->subdriver->disconnect(info->control); + + /* allow user to unbind using either control or data */ + if (intf == info->control) + other = info->data; + else + other = info->control; + + /* only if not shared */ + if (other && intf != other) { + usb_set_intfdata(other, NULL); + usb_driver_release_interface(driver, other); + } info->subdriver = NULL; + info->data = NULL; + info->control = NULL; } /* suspend/resume wrappers calling both usbnet and the cdc-wdm @@ -364,11 +359,11 @@ err: return ret; } - static const struct driver_info qmi_wwan_info = { .description = "QMI speaking wwan device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind, + .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, }; @@ -376,7 +371,7 @@ static const struct driver_info qmi_wwan_shared = { .description = "QMI speaking wwan device with combined interface", .flags = FLAG_WWAN, .bind = qmi_wwan_bind_shared, - .unbind = qmi_wwan_unbind_shared, + .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, }; @@ -384,7 +379,7 @@ static const struct driver_info qmi_wwan_gobi = { .description = "Qualcomm Gobi wwan/QMI device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind_gobi, - .unbind = qmi_wwan_unbind_shared, + .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, }; @@ -393,7 +388,7 @@ static const struct driver_info qmi_wwan_force_int1 = { .description = "Qualcomm WWAN/QMI device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind_shared, - .unbind = qmi_wwan_unbind_shared, + .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, .data = BIT(1), /* interface whitelist bitmap */ }; @@ -402,7 +397,7 @@ static const struct driver_info qmi_wwan_force_int4 = { .description = "Qualcomm WWAN/QMI device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind_shared, - .unbind = qmi_wwan_unbind_shared, + .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, .data = BIT(4), /* interface whitelist bitmap */ }; @@ -424,7 +419,7 @@ static const struct driver_info qmi_wwan_sierra = { .description = "Sierra Wireless wwan/QMI device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind_gobi, - .unbind = qmi_wwan_unbind_shared, + .unbind = qmi_wwan_unbind, .manage_power = qmi_wwan_manage_power, .data = BIT(8) | BIT(19), /* interface whitelist bitmap */ }; @@ -440,7 +435,7 @@ static const struct usb_device_id products[] = { .idVendor = HUAWEI_VENDOR_ID, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, - .bInterfaceProtocol = 8, /* NOTE: This is the *slave* interface of the CDC Union! */ + .bInterfaceProtocol = 9, /* CDC Ethernet *control* interface */ .driver_info = (unsigned long)&qmi_wwan_info, }, { /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */ @@ -448,7 +443,7 @@ static const struct usb_device_id products[] = { .idVendor = HUAWEI_VENDOR_ID, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, - .bInterfaceProtocol = 56, /* NOTE: This is the *slave* interface of the CDC Union! */ + .bInterfaceProtocol = 57, /* CDC Ethernet *control* interface */ .driver_info = (unsigned long)&qmi_wwan_info, }, { /* Huawei E392, E398 and possibly others in "Windows mode" -- cgit v1.2.3 From a40345b5b4a09ff8539aaafeb2c612db6d0b4d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 19 Jun 2012 00:42:02 +0000 Subject: net: qmi_wwan: shorten driver description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The description is used in ethtool fixed length fields. Make it shorter to avoid truncation. Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 05571fcbd70a..f12ba3c2134c 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -360,7 +360,7 @@ err: } static const struct driver_info qmi_wwan_info = { - .description = "QMI speaking wwan device", + .description = "WWAN/QMI device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind, .unbind = qmi_wwan_unbind, @@ -368,7 +368,7 @@ static const struct driver_info qmi_wwan_info = { }; static const struct driver_info qmi_wwan_shared = { - .description = "QMI speaking wwan device with combined interface", + .description = "WWAN/QMI device", .flags = FLAG_WWAN, .bind = qmi_wwan_bind_shared, .unbind = qmi_wwan_unbind, -- cgit v1.2.3 From 677a3d60fb3153f786a0d28fcf0287670e7bd3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Tue, 19 Jun 2012 00:42:03 +0000 Subject: net: qmi_wwan: use module_usb_driver macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index f12ba3c2134c..f1e779135899 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -593,17 +593,7 @@ static struct usb_driver qmi_wwan_driver = { .disable_hub_initiated_lpm = 1, }; -static int __init qmi_wwan_init(void) -{ - return usb_register(&qmi_wwan_driver); -} -module_init(qmi_wwan_init); - -static void __exit qmi_wwan_exit(void) -{ - usb_deregister(&qmi_wwan_driver); -} -module_exit(qmi_wwan_exit); +module_usb_driver(qmi_wwan_driver); MODULE_AUTHOR("Bjørn Mork "); MODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); -- cgit v1.2.3 From af8b5fc31099abd7f3b297332c9e280ec0b30a71 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Tue, 19 Jun 2012 07:08:22 +0000 Subject: regulator: add new regulator driver for lp872x This driver supports TI/National LP8720, LP8725 PMIC. Signed-off-by: Milo(Woogyom) Kim Reviewed-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/lp872x.c | 957 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 965 insertions(+) create mode 100644 drivers/regulator/lp872x.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 00ffe05d8026..870f7300b156 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -224,6 +224,13 @@ config REGULATOR_LP3972 Say Y here to support the voltage regulators and convertors on National Semiconductors LP3972 PMIC +config REGULATOR_LP872X + tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators" + depends on I2C + select REGMAP_I2C + help + This driver supports LP8720/LP8725 PMIC + config REGULATOR_PCF50633 tristate "NXP PCF50633 regulator driver" depends on MFD_PCF50633 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index d8544539efec..e6f700748a3b 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o +obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c new file mode 100644 index 000000000000..d51d09852041 --- /dev/null +++ b/drivers/regulator/lp872x.c @@ -0,0 +1,957 @@ +/* + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers : LP8720/8725 shared */ +#define LP872X_GENERAL_CFG 0x00 +#define LP872X_LDO1_VOUT 0x01 +#define LP872X_LDO2_VOUT 0x02 +#define LP872X_LDO3_VOUT 0x03 +#define LP872X_LDO4_VOUT 0x04 +#define LP872X_LDO5_VOUT 0x05 + +/* Registers : LP8720 */ +#define LP8720_BUCK_VOUT1 0x06 +#define LP8720_BUCK_VOUT2 0x07 +#define LP8720_ENABLE 0x08 + +/* Registers : LP8725 */ +#define LP8725_LILO1_VOUT 0x06 +#define LP8725_LILO2_VOUT 0x07 +#define LP8725_BUCK1_VOUT1 0x08 +#define LP8725_BUCK1_VOUT2 0x09 +#define LP8725_BUCK2_VOUT1 0x0A +#define LP8725_BUCK2_VOUT2 0x0B +#define LP8725_BUCK_CTRL 0x0C +#define LP8725_LDO_CTRL 0x0D + +/* Mask/shift : LP8720/LP8725 shared */ +#define LP872X_VOUT_M 0x1F +#define LP872X_START_DELAY_M 0xE0 +#define LP872X_START_DELAY_S 5 +#define LP872X_EN_LDO1_M BIT(0) +#define LP872X_EN_LDO2_M BIT(1) +#define LP872X_EN_LDO3_M BIT(2) +#define LP872X_EN_LDO4_M BIT(3) +#define LP872X_EN_LDO5_M BIT(4) + +/* Mask/shift : LP8720 */ +#define LP8720_TIMESTEP_S 0 /* Addr 00h */ +#define LP8720_TIMESTEP_M BIT(0) +#define LP8720_EXT_DVS_M BIT(2) +#define LP8720_BUCK_FPWM_S 5 /* Addr 07h */ +#define LP8720_BUCK_FPWM_M BIT(5) +#define LP8720_EN_BUCK_M BIT(5) /* Addr 08h */ +#define LP8720_DVS_SEL_M BIT(7) + +/* Mask/shift : LP8725 */ +#define LP8725_TIMESTEP_M 0xC0 /* Addr 00h */ +#define LP8725_TIMESTEP_S 6 +#define LP8725_BUCK1_EN_M BIT(0) +#define LP8725_DVS1_M BIT(2) +#define LP8725_DVS2_M BIT(3) +#define LP8725_BUCK2_EN_M BIT(4) +#define LP8725_BUCK_CL_M 0xC0 /* Addr 09h, 0Bh */ +#define LP8725_BUCK_CL_S 6 +#define LP8725_BUCK1_FPWM_S 1 /* Addr 0Ch */ +#define LP8725_BUCK1_FPWM_M BIT(1) +#define LP8725_BUCK2_FPWM_S 5 +#define LP8725_BUCK2_FPWM_M BIT(5) +#define LP8725_EN_LILO1_M BIT(5) /* Addr 0Dh */ +#define LP8725_EN_LILO2_M BIT(6) + +/* PWM mode */ +#define LP872X_FORCE_PWM 1 +#define LP872X_AUTO_PWM 0 + +#define LP8720_NUM_REGULATORS 6 +#define LP8725_NUM_REGULATORS 9 +#define EXTERN_DVS_USED 0 +#define MAX_DELAY 6 + +/* dump registers in regmap-debugfs */ +#define MAX_REGISTERS 0x0F + +enum lp872x_id { + LP8720, + LP8725, +}; + +struct lp872x { + struct regmap *regmap; + struct device *dev; + enum lp872x_id chipid; + struct lp872x_platform_data *pdata; + struct regulator_dev **regulators; + int num_regulators; + enum lp872x_dvs_state dvs_pin; + int dvs_gpio; +}; + +/* LP8720/LP8725 shared voltage table for LDOs */ +static const unsigned int lp872x_ldo_vtbl[] = { + 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 2000000, + 2100000, 2200000, 2300000, 2400000, 2500000, 2600000, 2650000, 2700000, + 2750000, 2800000, 2850000, 2900000, 2950000, 3000000, 3100000, 3300000, +}; + +/* LP8720 LDO4 voltage table */ +static const unsigned int lp8720_ldo4_vtbl[] = { + 800000, 850000, 900000, 1000000, 1100000, 1200000, 1250000, 1300000, + 1350000, 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, +}; + +/* LP8725 LILO(Low Input Low Output) voltage table */ +static const unsigned int lp8725_lilo_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1800000, 1900000, 2000000, 2100000, 2200000, 2300000, 2400000, 2500000, + 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000, +}; + +/* LP8720 BUCK voltage table */ +#define EXT_R 0 /* external resistor divider */ +static const unsigned int lp8720_buck_vtbl[] = { + EXT_R, 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, + 1150000, 1200000, 1250000, 1300000, 1350000, 1400000, 1450000, 1500000, + 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, + 1950000, 2000000, 2050000, 2100000, 2150000, 2200000, 2250000, 2300000, +}; + +/* LP8725 BUCK voltage table */ +static const unsigned int lp8725_buck_vtbl[] = { + 800000, 850000, 900000, 950000, 1000000, 1050000, 1100000, 1150000, + 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1700000, + 1750000, 1800000, 1850000, 1900000, 2000000, 2100000, 2200000, 2300000, + 2400000, 2500000, 2600000, 2700000, 2800000, 2850000, 2900000, 3000000, +}; + +/* LP8725 BUCK current limit */ +static const unsigned int lp8725_buck_uA[] = { + 460000, 780000, 1050000, 1370000, +}; + +static int lp872x_read_byte(struct lp872x *lp, u8 addr, u8 *data) +{ + int ret; + unsigned int val; + + ret = regmap_read(lp->regmap, addr, &val); + if (ret < 0) { + dev_err(lp->dev, "failed to read 0x%.2x\n", addr); + return ret; + } + + *data = (u8)val; + return 0; +} + +static inline int lp872x_write_byte(struct lp872x *lp, u8 addr, u8 data) +{ + return regmap_write(lp->regmap, addr, data); +} + +static inline int lp872x_update_bits(struct lp872x *lp, u8 addr, + unsigned int mask, u8 data) +{ + return regmap_update_bits(lp->regmap, addr, mask, data); +} + +static int _rdev_to_offset(struct regulator_dev *rdev) +{ + enum lp872x_regulator_id id = rdev_get_id(rdev); + + switch (id) { + case LP8720_ID_LDO1 ... LP8720_ID_BUCK: + return id; + case LP8725_ID_LDO1 ... LP8725_ID_BUCK2: + return id - LP8725_ID_BASE; + default: + return -EINVAL; + } +} + +static int lp872x_get_timestep_usec(struct lp872x *lp) +{ + enum lp872x_id chip = lp->chipid; + u8 val, mask, shift; + int *time_usec, size, ret; + int lp8720_time_usec[] = { 25, 50 }; + int lp8725_time_usec[] = { 32, 64, 128, 256 }; + + switch (chip) { + case LP8720: + mask = LP8720_TIMESTEP_M; + shift = LP8720_TIMESTEP_S; + time_usec = &lp8720_time_usec[0]; + size = ARRAY_SIZE(lp8720_time_usec); + break; + case LP8725: + mask = LP8725_TIMESTEP_M; + shift = LP8725_TIMESTEP_S; + time_usec = &lp8725_time_usec[0]; + size = ARRAY_SIZE(lp8725_time_usec); + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return -EINVAL; + + val = (val & mask) >> shift; + if (val >= size) + return -EINVAL; + + return *(time_usec + val); +} + +static int lp872x_regulator_enable_time(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id regulator = rdev_get_id(rdev); + int time_step_us = lp872x_get_timestep_usec(lp); + int ret, offset; + u8 addr, val; + + if (time_step_us < 0) + return -EINVAL; + + switch (regulator) { + case LP8720_ID_LDO1 ... LP8720_ID_LDO5: + case LP8725_ID_LDO1 ... LP8725_ID_LILO2: + offset = _rdev_to_offset(rdev); + if (offset < 0) + return -EINVAL; + + addr = LP872X_LDO1_VOUT + offset; + break; + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT1; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT1; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT1; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP872X_START_DELAY_M) >> LP872X_START_DELAY_S; + + return val > MAX_DELAY ? 0 : val * time_step_us; +} + +static void lp872x_set_dvs(struct lp872x *lp, int gpio) +{ + enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel; + enum lp872x_dvs_state state; + + state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW; + gpio_set_value(gpio, state); + lp->dvs_pin = state; +} + +static u8 lp872x_select_buck_vout_addr(struct lp872x *lp, + enum lp872x_regulator_id buck) +{ + u8 val, addr; + + if (lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val)) + return 0; + + switch (buck) { + case LP8720_ID_BUCK: + if (val & LP8720_EXT_DVS_M) { + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } else { + if (lp872x_read_byte(lp, LP8720_ENABLE, &val)) + return 0; + + addr = val & LP8720_DVS_SEL_M ? + LP8720_BUCK_VOUT1 : LP8720_BUCK_VOUT2; + } + break; + case LP8725_ID_BUCK1: + if (val & LP8725_DVS1_M) + addr = LP8725_BUCK1_VOUT1; + else + addr = (lp->dvs_pin == DVS_HIGH) ? + LP8725_BUCK1_VOUT1 : LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = val & LP8725_DVS2_M ? + LP8725_BUCK2_VOUT1 : LP8725_BUCK2_VOUT2; + break; + default: + return 0; + } + + return addr; +} + +static bool lp872x_is_valid_buck_addr(u8 addr) +{ + switch (addr) { + case LP8720_BUCK_VOUT1: + case LP8720_BUCK_VOUT2: + case LP8725_BUCK1_VOUT1: + case LP8725_BUCK1_VOUT2: + case LP8725_BUCK2_VOUT1: + case LP8725_BUCK2_VOUT2: + return true; + default: + return false; + } +} + +static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask = LP872X_VOUT_M; + struct lp872x_dvs *dvs = lp->pdata->dvs; + + if (dvs && gpio_is_valid(dvs->gpio)) + lp872x_set_dvs(lp, dvs->gpio); + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, selector); +} + +static int lp872x_buck_get_voltage_sel(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + addr = lp872x_select_buck_vout_addr(lp, buck); + if (!lp872x_is_valid_buck_addr(addr)) + return -EINVAL; + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & LP872X_VOUT_M; +} + +static int lp8725_buck_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + int i, max = ARRAY_SIZE(lp8725_buck_uA); + u8 addr, val; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + for (i = 0 ; i < max ; i++) + if (lp8725_buck_uA[i] >= min_uA && + lp8725_buck_uA[i] <= max_uA) + break; + + if (i == max) + return -EINVAL; + + val = i << LP8725_BUCK_CL_S; + + return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val); +} + +static int lp8725_buck_get_current_limit(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, val; + int ret; + + switch (buck) { + case LP8725_ID_BUCK1: + addr = LP8725_BUCK1_VOUT2; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK2_VOUT2; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + val = (val & LP8725_BUCK_CL_M) >> LP8725_BUCK_CL_S; + + return (val < ARRAY_SIZE(lp8725_buck_uA)) ? + lp8725_buck_uA[val] : -EINVAL; +} + +static int lp872x_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, shift, val; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + shift = LP8720_BUCK_FPWM_S; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + shift = LP8725_BUCK1_FPWM_S; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + shift = LP8725_BUCK2_FPWM_S; + break; + default: + return -EINVAL; + } + + if (mode == REGULATOR_MODE_FAST) + val = LP872X_FORCE_PWM << shift; + else if (mode == REGULATOR_MODE_NORMAL) + val = LP872X_AUTO_PWM << shift; + else + return -EINVAL; + + return lp872x_update_bits(lp, addr, mask, val); +} + +static unsigned int lp872x_buck_get_mode(struct regulator_dev *rdev) +{ + struct lp872x *lp = rdev_get_drvdata(rdev); + enum lp872x_regulator_id buck = rdev_get_id(rdev); + u8 addr, mask, val; + int ret; + + switch (buck) { + case LP8720_ID_BUCK: + addr = LP8720_BUCK_VOUT2; + mask = LP8720_BUCK_FPWM_M; + break; + case LP8725_ID_BUCK1: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK1_FPWM_M; + break; + case LP8725_ID_BUCK2: + addr = LP8725_BUCK_CTRL; + mask = LP8725_BUCK2_FPWM_M; + break; + default: + return -EINVAL; + } + + ret = lp872x_read_byte(lp, addr, &val); + if (ret) + return ret; + + return val & mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops lp872x_ldo_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, +}; + +static struct regulator_ops lp8720_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, +}; + +static struct regulator_ops lp8725_buck_ops = { + .list_voltage = regulator_list_voltage_table, + .set_voltage_sel = lp872x_buck_set_voltage_sel, + .get_voltage_sel = lp872x_buck_get_voltage_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .enable_time = lp872x_regulator_enable_time, + .set_mode = lp872x_buck_set_mode, + .get_mode = lp872x_buck_get_mode, + .set_current_limit = lp8725_buck_set_current_limit, + .get_current_limit = lp8725_buck_get_current_limit, +}; + +static struct regulator_desc lp8720_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8720_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8720_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8720_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8720_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8720_ldo4_vtbl), + .volt_table = lp8720_ldo4_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8720_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "buck", + .id = LP8720_ID_BUCK, + .ops = &lp8720_buck_ops, + .n_voltages = ARRAY_SIZE(lp8720_buck_vtbl), + .volt_table = lp8720_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP8720_ENABLE, + .enable_mask = LP8720_EN_BUCK_M, + }, +}; + +static struct regulator_desc lp8725_regulator_desc[] = { + { + .name = "ldo1", + .id = LP8725_ID_LDO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO1_M, + }, + { + .name = "ldo2", + .id = LP8725_ID_LDO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO2_M, + }, + { + .name = "ldo3", + .id = LP8725_ID_LDO3, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO3_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO3_M, + }, + { + .name = "ldo4", + .id = LP8725_ID_LDO4, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO4_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO4_M, + }, + { + .name = "ldo5", + .id = LP8725_ID_LDO5, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp872x_ldo_vtbl), + .volt_table = lp872x_ldo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP872X_LDO5_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP872X_EN_LDO5_M, + }, + { + .name = "lilo1", + .id = LP8725_ID_LILO1, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO1_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO1_M, + }, + { + .name = "lilo2", + .id = LP8725_ID_LILO2, + .ops = &lp872x_ldo_ops, + .n_voltages = ARRAY_SIZE(lp8725_lilo_vtbl), + .volt_table = lp8725_lilo_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .vsel_reg = LP8725_LILO2_VOUT, + .vsel_mask = LP872X_VOUT_M, + .enable_reg = LP8725_LDO_CTRL, + .enable_mask = LP8725_EN_LILO2_M, + }, + { + .name = "buck1", + .id = LP8725_ID_BUCK1, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK1_EN_M, + }, + { + .name = "buck2", + .id = LP8725_ID_BUCK2, + .ops = &lp8725_buck_ops, + .n_voltages = ARRAY_SIZE(lp8725_buck_vtbl), + .volt_table = lp8725_buck_vtbl, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .enable_reg = LP872X_GENERAL_CFG, + .enable_mask = LP8725_BUCK2_EN_M, + }, +}; + +static int lp872x_check_dvs_validity(struct lp872x *lp) +{ + struct lp872x_dvs *dvs = lp->pdata->dvs; + u8 val = 0; + int ret; + + ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val); + if (ret) + return ret; + + ret = 0; + if (lp->chipid == LP8720) { + if (val & LP8720_EXT_DVS_M) + ret = dvs ? 0 : -EINVAL; + } else { + if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED) + ret = dvs ? 0 : -EINVAL; + } + + return ret; +} + +static int lp872x_init_dvs(struct lp872x *lp) +{ + int ret, gpio; + struct lp872x_dvs *dvs = lp->pdata->dvs; + enum lp872x_dvs_state pinstate; + + ret = lp872x_check_dvs_validity(lp); + if (ret) { + dev_warn(lp->dev, "invalid dvs data: %d\n", ret); + return ret; + } + + gpio = dvs->gpio; + if (!gpio_is_valid(gpio)) { + dev_err(lp->dev, "invalid gpio: %d\n", gpio); + return -EINVAL; + } + + pinstate = dvs->init_state; + ret = devm_gpio_request_one(lp->dev, gpio, pinstate, "LP872X DVS"); + if (ret) { + dev_err(lp->dev, "gpio request err: %d\n", ret); + return ret; + } + + lp->dvs_pin = pinstate; + lp->dvs_gpio = gpio; + + return 0; +} + +static int lp872x_config(struct lp872x *lp) +{ + struct lp872x_platform_data *pdata = lp->pdata; + int ret; + + if (!pdata) { + dev_warn(lp->dev, "no platform data\n"); + return 0; + } + + if (!pdata->update_config) + return 0; + + ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config); + if (ret) + return ret; + + return lp872x_init_dvs(lp); +} + +static struct regulator_init_data +*lp872x_find_regulator_init_data(int idx, struct lp872x *lp) +{ + int i, base, id, max_regulators; + + switch (lp->chipid) { + case LP8720: + base = LP8720_ID_BASE; + max_regulators = LP8720_NUM_REGULATORS; + break; + case LP8725: + base = LP8725_ID_BASE; + max_regulators = LP8725_NUM_REGULATORS; + break; + default: + return NULL; + } + + id = base + idx; + for (i = 0 ; i < max_regulators ; i++) + if (lp->pdata->regulator_data[i].id == id) + break; + + return (i == max_regulators) ? NULL : + lp->pdata->regulator_data[i].init_data; +} + +static int lp872x_regulator_register(struct lp872x *lp) +{ + struct regulator_desc *desc; + struct regulator_config cfg = { }; + struct regulator_dev *rdev; + int i, ret; + + for (i = 0 ; i < lp->num_regulators ; i++) { + desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] : + &lp8725_regulator_desc[i]; + + cfg.dev = lp->dev; + cfg.init_data = lp872x_find_regulator_init_data(i, lp); + cfg.driver_data = lp; + cfg.regmap = lp->regmap; + + rdev = regulator_register(desc, &cfg); + if (IS_ERR(rdev)) { + dev_err(lp->dev, "regulator register err"); + ret = PTR_ERR(rdev); + goto err; + } + + *(lp->regulators + i) = rdev; + } + + return 0; +err: + while (--i >= 0) { + rdev = *(lp->regulators + i); + regulator_unregister(rdev); + } + return ret; +} + +static void lp872x_regulator_unregister(struct lp872x *lp) +{ + struct regulator_dev *rdev; + int i; + + for (i = 0 ; i < lp->num_regulators ; i++) { + rdev = *(lp->regulators + i); + regulator_unregister(rdev); + } +} + +static const struct regmap_config lp872x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX_REGISTERS, +}; + +static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct lp872x *lp; + struct lp872x_platform_data *pdata = cl->dev.platform_data; + int ret, size, num_regulators; + const int lp872x_num_regulators[] = { + [LP8720] = LP8720_NUM_REGULATORS, + [LP8725] = LP8725_NUM_REGULATORS, + }; + + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); + if (!lp) + goto err_mem; + + num_regulators = lp872x_num_regulators[id->driver_data]; + size = sizeof(struct regulator_dev *) * num_regulators; + + lp->regulators = devm_kzalloc(&cl->dev, size, GFP_KERNEL); + if (!lp->regulators) + goto err_mem; + + lp->regmap = devm_regmap_init_i2c(cl, &lp872x_regmap_config); + if (IS_ERR(lp->regmap)) { + ret = PTR_ERR(lp->regmap); + dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); + goto err_dev; + } + + lp->dev = &cl->dev; + lp->pdata = pdata; + lp->chipid = id->driver_data; + lp->num_regulators = num_regulators; + i2c_set_clientdata(cl, lp); + + ret = lp872x_config(lp); + if (ret) + goto err_dev; + + return lp872x_regulator_register(lp); + +err_mem: + return -ENOMEM; +err_dev: + return ret; +} + +static int __devexit lp872x_remove(struct i2c_client *cl) +{ + struct lp872x *lp = i2c_get_clientdata(cl); + + lp872x_regulator_unregister(lp); + return 0; +} + +static const struct i2c_device_id lp872x_ids[] = { + {"lp8720", LP8720}, + {"lp8725", LP8725}, + { } +}; +MODULE_DEVICE_TABLE(i2c, lp872x_ids); + +static struct i2c_driver lp872x_driver = { + .driver = { + .name = "lp872x", + .owner = THIS_MODULE, + }, + .probe = lp872x_probe, + .remove = __devexit_p(lp872x_remove), + .id_table = lp872x_ids, +}; + +module_i2c_driver(lp872x_driver); + +MODULE_DESCRIPTION("TI/National Semiconductor LP872x PMU Regulator Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From dfad84aeab5f71b33a12e6803a809f698bdef5a2 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 19 Jun 2012 17:43:56 +0200 Subject: regulator: support multiple dummy fixed regulators Currently regulator_register_fixed() uses a constant name to register a fixed dummy regulator. This is sufficient in principle, since there is no reason to register multiple such regulators. The user can simply supply all consumers in one array and use it to initialise such a regulator. However, in some cases it can be convenient to register multiple such regulators. This is also a prerequisite for the upcoming patch, that will add a voltage parameter to this function. The original function is provided as a wrapper macro. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mark Brown --- drivers/regulator/fixed-helper.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c index cacd33c9d042..3aa268dfdb1d 100644 --- a/drivers/regulator/fixed-helper.c +++ b/drivers/regulator/fixed-helper.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -13,16 +14,18 @@ static void regulator_fixed_release(struct device *dev) { struct fixed_regulator_data *data = container_of(dev, struct fixed_regulator_data, pdev.dev); + kfree(data->cfg.supply_name); kfree(data); } /** - * regulator_register_fixed - register a no-op fixed regulator + * regulator_register_fixed_name - register a no-op fixed regulator * @id: platform device id + * @name: name to be used for the regulator * @supplies: consumers for this regulator * @num_supplies: number of consumers */ -struct platform_device *regulator_register_fixed(int id, +struct platform_device *regulator_register_always_on(int id, const char *name, struct regulator_consumer_supply *supplies, int num_supplies) { struct fixed_regulator_data *data; @@ -31,7 +34,12 @@ struct platform_device *regulator_register_fixed(int id, if (!data) return NULL; - data->cfg.supply_name = "fixed-dummy"; + data->cfg.supply_name = kstrdup(name, GFP_KERNEL); + if (!data->cfg.supply_name) { + kfree(data); + return NULL; + } + data->cfg.microvolts = 0; data->cfg.gpio = -EINVAL; data->cfg.enabled_at_boot = 1; -- cgit v1.2.3 From 15719ccc274981b19ad8fe9ac20c94249de8a257 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 19 Jun 2012 17:44:39 +0200 Subject: regulator: extend the fixed dummy voltage regulator to accept voltage Trivially extend the regulator_register_always_on() helper function to be even more useful by adding a voltage parameter to it. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mark Brown --- drivers/regulator/fixed-helper.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed-helper.c b/drivers/regulator/fixed-helper.c index 3aa268dfdb1d..f9d027992aae 100644 --- a/drivers/regulator/fixed-helper.c +++ b/drivers/regulator/fixed-helper.c @@ -24,9 +24,10 @@ static void regulator_fixed_release(struct device *dev) * @name: name to be used for the regulator * @supplies: consumers for this regulator * @num_supplies: number of consumers + * @uv: voltage in microvolts */ struct platform_device *regulator_register_always_on(int id, const char *name, - struct regulator_consumer_supply *supplies, int num_supplies) + struct regulator_consumer_supply *supplies, int num_supplies, int uv) { struct fixed_regulator_data *data; @@ -40,7 +41,7 @@ struct platform_device *regulator_register_always_on(int id, const char *name, return NULL; } - data->cfg.microvolts = 0; + data->cfg.microvolts = uv; data->cfg.gpio = -EINVAL; data->cfg.enabled_at_boot = 1; data->cfg.init_data = &data->init_data; -- cgit v1.2.3 From 4969c32bab70d71e07b0a76e1699782857d5aa22 Mon Sep 17 00:00:00 2001 From: Boojin Kim Date: Tue, 19 Jun 2012 13:27:03 +0900 Subject: spi/s3c64xx: Add the use of DMA config operation Config operation is separated from request operation in DMA common operation. Because spi driver can change the DMA config for every transfer. So this patch is using the separated DMA config operation. Signed-off-by: Boojin Kim Signed-off-by: Kyoungil Kim Signed-off-by: Kukjin Kim --- drivers/spi/spi-s3c64xx.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 972a94c58be3..3c36cfaa1b93 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -262,14 +262,24 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, unsigned len, dma_addr_t buf) { struct s3c64xx_spi_driver_data *sdd; - struct samsung_dma_prep_info info; + struct samsung_dma_prep info; + struct samsung_dma_config config; - if (dma->direction == DMA_DEV_TO_MEM) + if (dma->direction == DMA_DEV_TO_MEM) { sdd = container_of((void *)dma, struct s3c64xx_spi_driver_data, rx_dma); - else + config.direction = sdd->rx_dma.direction; + config.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; + config.width = sdd->cur_bpw / 8; + sdd->ops->config(sdd->rx_dma.ch, &config); + } else { sdd = container_of((void *)dma, struct s3c64xx_spi_driver_data, tx_dma); + config.direction = sdd->tx_dma.direction; + config.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; + config.width = sdd->cur_bpw / 8; + sdd->ops->config(sdd->tx_dma.ch, &config); + } info.cap = DMA_SLAVE; info.len = len; @@ -284,20 +294,15 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) { - struct samsung_dma_info info; + struct samsung_dma_req req; sdd->ops = samsung_dma_get_ops(); - info.cap = DMA_SLAVE; - info.client = &s3c64xx_spi_dma_client; - info.width = sdd->cur_bpw / 8; - - info.direction = sdd->rx_dma.direction; - info.fifo = sdd->sfr_start + S3C64XX_SPI_RX_DATA; - sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &info); - info.direction = sdd->tx_dma.direction; - info.fifo = sdd->sfr_start + S3C64XX_SPI_TX_DATA; - sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &info); + req.cap = DMA_SLAVE; + req.client = &s3c64xx_spi_dma_client; + + sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req); + sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req); return 1; } -- cgit v1.2.3 From 63d76dc0b97230e012a128a898f56a55e763d91a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Jun 2012 08:32:49 +0200 Subject: iwlwifi: fix 11n_disable EEPROM refactoring regression My commit 26a7ca9a71a ("iwlwifi: refactor EEPROM reading/parsing") broke the 11n_disable module parameter's BIT(0) to disable all HT operation (using the other bits to disable aggregation only was unaffected). Restore this by overriding the SKU when parsing the EEPROM if the module parameter is set. Reported-by: Matthijs Kooijman Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index c87a05cbec12..f10170fe8799 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -853,6 +853,9 @@ iwl_parse_eeprom_data(struct device *dev, const struct iwl_cfg *cfg, EEPROM_RADIO_CONFIG); data->sku = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_SKU_CAP); + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) + data->sku &= ~EEPROM_SKU_CAP_11N_ENABLE; + data->eeprom_version = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_VERSION); -- cgit v1.2.3 From 0d4e07726ea468c0f9bb7f84b02a9f1bfd16b69e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 18 Jun 2012 10:17:12 +0200 Subject: iwlwifi: use minimal time for radio reset scan The effect of using a short single-channel scan to reset the radio is that scanning a channel that isn't in use needs to re-tune the radio. This means that the dwell time is irrelevant, so use a shorter time. While at it, clean up the code for this a bit. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/scan.c | 41 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index 2f271c96ed39..f5b1452a60b3 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -51,6 +51,9 @@ #define IWL_CHANNEL_TUNE_TIME 5 #define MAX_SCAN_CHANNEL 50 +/* For reset radio, need minimal dwell time only */ +#define IWL_RADIO_RESET_DWELL_TIME 5 + static int iwl_send_scan_abort(struct iwl_priv *priv) { int ret; @@ -469,45 +472,39 @@ static u8 iwl_get_single_channel_number(struct iwl_priv *priv, return 0; } -static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - struct iwl_scan_channel *scan_ch) +static int iwl_get_channel_for_reset_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + struct iwl_scan_channel *scan_ch) { const struct ieee80211_supported_band *sband; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added = 0; - u16 channel = 0; + u16 channel; sband = iwl_get_hw_mode(priv, band); if (!sband) { IWL_ERR(priv, "invalid band\n"); - return added; + return 0; } - active_dwell = iwl_get_active_dwell_time(priv, band, 0); - passive_dwell = iwl_get_passive_dwell_time(priv, band); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - channel = iwl_get_single_channel_number(priv, band); if (channel) { scan_ch->channel = cpu_to_le16(channel); scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + scan_ch->active_dwell = + cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); + scan_ch->passive_dwell = + cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); /* Set txpower levels to defaults */ scan_ch->dsp_atten = 110; if (band == IEEE80211_BAND_5GHZ) scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; else scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - added++; - } else - IWL_ERR(priv, "no valid channel found\n"); - return added; + return 1; + } + + IWL_ERR(priv, "no valid channel found\n"); + return 0; } static int iwl_get_channels_for_scan(struct iwl_priv *priv, @@ -896,7 +893,7 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) switch (priv->scan_type) { case IWL_SCAN_RADIO_RESET: scan->channel_count = - iwl_get_single_channel_for_scan(priv, vif, band, + iwl_get_channel_for_reset_scan(priv, vif, band, (void *)&scan->data[cmd_len]); break; case IWL_SCAN_NORMAL: -- cgit v1.2.3 From 8d40f4eebf43bd73aa7c6e06665fd7a26012f9c9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Jun 2012 08:42:08 +0200 Subject: iwlwifi: remove sku field from hw_params Now that the eeprom parsing code overrides the sku field directly with 11n_disable parameters, there's no longer a need to keep a copy of this field. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/dev.h | 2 -- drivers/net/wireless/iwlwifi/dvm/lib.c | 2 +- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 4 ++-- drivers/net/wireless/iwlwifi/dvm/main.c | 13 +++++-------- 4 files changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 4620b657948a..54cf085ddc89 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -568,7 +568,6 @@ enum iwl_scan_type { * * @tx_chains_num: Number of TX chains * @rx_chains_num: Number of RX chains - * @sku: sku read from EEPROM * @ct_kill_threshold: temperature threshold - in hw dependent unit * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit * relevant for 1000, 6000 and up @@ -579,7 +578,6 @@ struct iwl_hw_params { u8 tx_chains_num; u8 rx_chains_num; bool use_rts_for_aggregation; - u16 sku; u32 ct_kill_threshold; u32 ct_kill_exit_threshold; diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 76f259283c3a..207ae91a83aa 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -160,7 +160,7 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | IWL_PAN_SCD_MULTICAST_MSK; - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) + if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index c57726348ffe..b581f035d4b0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -164,7 +164,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; */ - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE) + if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; @@ -649,7 +649,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", sta->addr, tid); - if (!(priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE)) + if (!(priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE)) return -EACCES; IWL_DEBUG_MAC80211(priv, "enter\n"); diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index c642c217e29f..abfd7916bde6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1187,9 +1187,6 @@ static void iwl_set_hw_params(struct iwl_priv *priv) priv->hw_params.use_rts_for_aggregation = priv->cfg->ht_params->use_rts_for_aggregation; - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) - priv->hw_params.sku &= ~EEPROM_SKU_CAP_11N_ENABLE; - /* Device-specific setup */ priv->lib->set_hw_params(priv); } @@ -1234,20 +1231,20 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) { u16 radio_cfg; - priv->hw_params.sku = priv->eeprom_data->sku; + priv->eeprom_data->sku = priv->eeprom_data->sku; - if (priv->hw_params.sku & EEPROM_SKU_CAP_11N_ENABLE && + if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE && !priv->cfg->ht_params) { IWL_ERR(priv, "Invalid 11n configuration\n"); return -EINVAL; } - if (!priv->hw_params.sku) { + if (!priv->eeprom_data->sku) { IWL_ERR(priv, "Invalid device sku\n"); return -EINVAL; } - IWL_INFO(priv, "Device SKU: 0x%X\n", priv->hw_params.sku); + IWL_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku); radio_cfg = priv->eeprom_data->radio_cfg; @@ -1453,7 +1450,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, ************************/ iwl_set_hw_params(priv); - if (!(priv->hw_params.sku & EEPROM_SKU_CAP_IPAN_ENABLE)) { + if (!(priv->eeprom_data->sku & EEPROM_SKU_CAP_IPAN_ENABLE)) { IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN"); ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; /* -- cgit v1.2.3 From 7500673be30f9467cd1a0e065c93e75c5213b44d Mon Sep 17 00:00:00 2001 From: Tushar Dave Date: Tue, 12 Jun 2012 13:03:29 +0000 Subject: e1000: Combining Bitwise OR in one expression. Signed-off-by: Tushar Dave Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/e1000/e1000_main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 7483ca0a6282..183a4a3224ba 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -1078,18 +1078,18 @@ static int __devinit e1000_probe(struct pci_dev *pdev, netdev->priv_flags |= IFF_SUPP_NOFCS; netdev->features |= netdev->hw_features; - netdev->hw_features |= NETIF_F_RXCSUM; - netdev->hw_features |= NETIF_F_RXALL; - netdev->hw_features |= NETIF_F_RXFCS; + netdev->hw_features |= (NETIF_F_RXCSUM | + NETIF_F_RXALL | + NETIF_F_RXFCS); if (pci_using_dac) { netdev->features |= NETIF_F_HIGHDMA; netdev->vlan_features |= NETIF_F_HIGHDMA; } - netdev->vlan_features |= NETIF_F_TSO; - netdev->vlan_features |= NETIF_F_HW_CSUM; - netdev->vlan_features |= NETIF_F_SG; + netdev->vlan_features |= (NETIF_F_TSO | + NETIF_F_HW_CSUM | + NETIF_F_SG); netdev->priv_flags |= IFF_UNICAST_FLT; -- cgit v1.2.3 From f00b0da776fda1abc481578e3932a668f603d72d Mon Sep 17 00:00:00 2001 From: Lior Levy Date: Sat, 4 Jun 2011 06:05:03 +0000 Subject: igb: A fix to VF TX rate limit There is a need to configure MMW_SIZE in register RTTBCNRM with a correct value. For 82576 device, the value should be 0x14. Signed-off-by: Lior Levy Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/e1000_regs.h | 1 + drivers/net/ethernet/intel/igb/igb_main.c | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h index 35d1e4f2c92c..10efcd88dca0 100644 --- a/drivers/net/ethernet/intel/igb/e1000_regs.h +++ b/drivers/net/ethernet/intel/igb/e1000_regs.h @@ -117,6 +117,7 @@ /* TX Rate Limit Registers */ #define E1000_RTTDQSEL 0x3604 /* Tx Desc Plane Queue Select - WO */ +#define E1000_RTTBCNRM 0x3690 /* Tx BCN Rate-scheduler MMW */ #define E1000_RTTBCNRC 0x36B0 /* Tx BCN Rate-Scheduler Config - WO */ /* Split and Replication RX Control - RW */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index dd3bfe8cd36c..64090549722d 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6997,6 +6997,11 @@ static void igb_set_vf_rate_limit(struct e1000_hw *hw, int vf, int tx_rate, } wr32(E1000_RTTDQSEL, vf); /* vf X uses queue X */ + /* + * Set global transmit compensation time to the MMW_SIZE in RTTBCNRM + * register. MMW_SIZE=0x014 if 9728-byte jumbo is supported. + */ + wr32(E1000_RTTBCNRM, 0x14); wr32(E1000_RTTBCNRC, bcnrc_val); } -- cgit v1.2.3 From d3eef8c8a033a5ee56ab5d923068eb8cd5d53887 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Wed, 16 May 2012 01:46:00 +0000 Subject: igb: Add switch case for supported hardware to igb_ptp_remove. PTP initialization is only done on supported parts, so remove needs same checks or it will cause crashes on systems with igb devices that don't support PTP. This patch adds those checks to the exit function. Signed-off-by: Carolyn Wyborny Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ptp.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index d5ee7fa50723..c846ea9131a3 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -330,7 +330,17 @@ void igb_ptp_init(struct igb_adapter *adapter) void igb_ptp_remove(struct igb_adapter *adapter) { - cancel_delayed_work_sync(&adapter->overflow_work); + switch (adapter->hw.mac.type) { + case e1000_i211: + case e1000_i210: + case e1000_i350: + case e1000_82580: + case e1000_82576: + cancel_delayed_work_sync(&adapter->overflow_work); + break; + default: + return; + } if (adapter->ptp_clock) { ptp_clock_unregister(adapter->ptp_clock); -- cgit v1.2.3 From cb41145ee78585282af56a9203f391c0d84366b1 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Wed, 4 Apr 2012 17:43:59 +0000 Subject: igb: Support the get_ts_info ethtool method. Based on original patch from Richard Cochran Original patch caused build errors without CONFIG_IGB_1588_CLOCK and CONFIG_PPS enabled, since the added code was not properly wrapped. CC: Richard Cochran Signed-off-by: Carolyn Wyborny Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_ethtool.c | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 812d4f963bd1..59d0f04d59af 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -2271,6 +2271,38 @@ static void igb_ethtool_complete(struct net_device *netdev) pm_runtime_put(&adapter->pdev->dev); } +#ifdef CONFIG_IGB_PTP +static int igb_ethtool_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct igb_adapter *adapter = netdev_priv(dev); + + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + if (adapter->ptp_clock) + info->phc_index = ptp_clock_index(adapter->ptp_clock); + else + info->phc_index = -1; + + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL) | + (1 << HWTSTAMP_FILTER_SOME) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT); + + return 0; +} + +#endif static const struct ethtool_ops igb_ethtool_ops = { .get_settings = igb_get_settings, .set_settings = igb_set_settings, @@ -2299,6 +2331,9 @@ static const struct ethtool_ops igb_ethtool_ops = { .set_coalesce = igb_set_coalesce, .begin = igb_ethtool_begin, .complete = igb_ethtool_complete, +#ifdef CONFIG_IGB_PTP + .get_ts_info = igb_ethtool_get_ts_info, +#endif }; void igb_set_ethtool_ops(struct net_device *netdev) -- cgit v1.2.3 From b3c185a7614cd95ea9b68d89a8d1ee4227ee9018 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 20 Jun 2012 17:29:04 +0900 Subject: sh: pfc: Split out gpio chip support. This implements a bit of rework for the PFC code, making the core itself slightly more pluggable and moving out the gpio chip handling completely. The API is preserved in such a way that platforms that depend on it for early configuration are still able to do so, while making it possible to migrate to alternate interfaces going forward. This is the first step of chainsawing necessary to support the pinctrl API, with the eventual goal being able to decouple pin function state from the gpio API while retaining gpio chip tie-in for gpio pin functions only, relying on the pinctrl/pinmux API for non-gpio function demux. Signed-off-by: Paul Mundt --- drivers/sh/Makefile | 2 +- drivers/sh/pfc-gpio.c | 309 +++++++++++++++++++++++++++++++++++++++++ drivers/sh/pfc.c | 376 ++++++++++++++------------------------------------ 3 files changed, 415 insertions(+), 272 deletions(-) create mode 100644 drivers/sh/pfc-gpio.c (limited to 'drivers') diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 7139ad2f2086..be5b2934f067 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -6,5 +6,5 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_GENERIC_GPIO) += pfc.o +obj-$(CONFIG_GENERIC_GPIO) += pfc.o pfc-gpio.o obj-y += pm_runtime.o diff --git a/drivers/sh/pfc-gpio.c b/drivers/sh/pfc-gpio.c new file mode 100644 index 000000000000..d74e5a96024b --- /dev/null +++ b/drivers/sh/pfc-gpio.c @@ -0,0 +1,309 @@ +/* + * SuperH Pin Function Controller GPIO driver. + * + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +struct sh_pfc_chip { + struct sh_pfc *pfc; + struct gpio_chip gpio_chip; +}; + +static struct sh_pfc_chip *gpio_to_pfc_chip(struct gpio_chip *gc) +{ + return container_of(gc, struct sh_pfc_chip, gpio_chip); +} + +static struct sh_pfc *gpio_to_pfc(struct gpio_chip *gc) +{ + return gpio_to_pfc_chip(gc)->pfc; +} + +static int sh_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + struct pinmux_data_reg *dummy; + unsigned long flags; + int i, ret, pinmux_type; + + ret = -EINVAL; + + if (!pfc) + goto err_out; + + spin_lock_irqsave(&pfc->lock, flags); + + if ((pfc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) + goto err_unlock; + + /* setup pin function here if no data is associated with pin */ + + if (sh_pfc_get_data_reg(pfc, offset, &dummy, &i) != 0) + pinmux_type = PINMUX_TYPE_FUNCTION; + else + pinmux_type = PINMUX_TYPE_GPIO; + + if (pinmux_type == PINMUX_TYPE_FUNCTION) { + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_DRYRUN) != 0) + goto err_unlock; + + if (sh_pfc_config_gpio(pfc, offset, + pinmux_type, + GPIO_CFG_REQ) != 0) + BUG(); + } + + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= pinmux_type; + + ret = 0; + err_unlock: + spin_unlock_irqrestore(&pfc->lock, flags); + err_out: + return ret; +} + +static void sh_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int pinmux_type; + + if (!pfc) + return; + + spin_lock_irqsave(&pfc->lock, flags); + + pinmux_type = pfc->gpios[offset].flags & PINMUX_FLAG_TYPE; + sh_pfc_config_gpio(pfc, offset, pinmux_type, GPIO_CFG_FREE); + pfc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[offset].flags |= PINMUX_TYPE_NONE; + + spin_unlock_irqrestore(&pfc->lock, flags); +} + +static int sh_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_INPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static void sh_gpio_set_value(struct sh_pfc *pfc, unsigned gpio, int value) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + BUG(); + else + sh_pfc_write_bit(dr, bit, value); +} + +static int sh_gpio_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + unsigned long flags; + int ret; + + sh_gpio_set_value(pfc, offset, value); + + spin_lock_irqsave(&pfc->lock, flags); + ret = sh_pfc_set_direction(pfc, offset, PINMUX_TYPE_OUTPUT); + spin_unlock_irqrestore(&pfc->lock, flags); + + return ret; +} + +static int sh_gpio_get_value(struct sh_pfc *pfc, unsigned gpio) +{ + struct pinmux_data_reg *dr = NULL; + int bit = 0; + + if (!pfc || sh_pfc_get_data_reg(pfc, gpio, &dr, &bit) != 0) + return -EINVAL; + + return sh_pfc_read_bit(dr, bit); +} + +static int sh_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + return sh_gpio_get_value(gpio_to_pfc(gc), offset); +} + +static void sh_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + sh_gpio_set_value(gpio_to_pfc(gc), offset, value); +} + +static int sh_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct sh_pfc *pfc = gpio_to_pfc(gc); + pinmux_enum_t enum_id; + pinmux_enum_t *enum_ids; + int i, k, pos; + + pos = 0; + enum_id = 0; + while (1) { + pos = sh_pfc_gpio_to_enum(pfc, offset, pos, &enum_id); + if (pos <= 0 || !enum_id) + break; + + for (i = 0; i < pfc->gpio_irq_size; i++) { + enum_ids = pfc->gpio_irq[i].enum_ids; + for (k = 0; enum_ids[k]; k++) { + if (enum_ids[k] == enum_id) + return pfc->gpio_irq[i].irq; + } + } + } + + return -ENOSYS; +} + +static void sh_pfc_gpio_setup(struct sh_pfc_chip *chip) +{ + struct sh_pfc *pfc = chip->pfc; + struct gpio_chip *gc = &chip->gpio_chip; + + gc->request = sh_gpio_request; + gc->free = sh_gpio_free; + gc->direction_input = sh_gpio_direction_input; + gc->get = sh_gpio_get; + gc->direction_output = sh_gpio_direction_output; + gc->set = sh_gpio_set; + gc->to_irq = sh_gpio_to_irq; + + WARN_ON(pfc->first_gpio != 0); /* needs testing */ + + gc->label = pfc->name; + gc->owner = THIS_MODULE; + gc->base = pfc->first_gpio; + gc->ngpio = (pfc->last_gpio - pfc->first_gpio) + 1; +} + +int sh_pfc_register_gpiochip(struct sh_pfc *pfc) +{ + struct sh_pfc_chip *chip; + int ret; + + chip = kzalloc(sizeof(struct sh_pfc_chip), GFP_KERNEL); + if (unlikely(!chip)) + return -ENOMEM; + + chip->pfc = pfc; + + sh_pfc_gpio_setup(chip); + + ret = gpiochip_add(&chip->gpio_chip); + if (unlikely(ret < 0)) + kfree(chip); + + pr_info("%s handling gpio %d -> %d\n", + pfc->name, pfc->first_gpio, pfc->last_gpio); + + return ret; +} +EXPORT_SYMBOL_GPL(sh_pfc_register_gpiochip); + +static int sh_pfc_gpio_match(struct gpio_chip *gc, void *data) +{ + return !!strstr(gc->label, data); +} + +static int __devinit sh_pfc_gpio_probe(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip; + struct gpio_chip *gc; + + gc = gpiochip_find("_pfc", sh_pfc_gpio_match); + if (unlikely(!gc)) { + pr_err("Cant find gpio chip\n"); + return -ENODEV; + } + + chip = gpio_to_pfc_chip(gc); + platform_set_drvdata(pdev, chip); + + pr_info("attaching to GPIO chip %s\n", chip->pfc->name); + + return 0; +} + +static int __devexit sh_pfc_gpio_remove(struct platform_device *pdev) +{ + struct sh_pfc_chip *chip = platform_get_drvdata(pdev); + int ret; + + ret = gpiochip_remove(&chip->gpio_chip); + if (unlikely(ret < 0)) + return ret; + + kfree(chip); + return 0; +} + +static struct platform_driver sh_pfc_gpio_driver = { + .probe = sh_pfc_gpio_probe, + .remove = __devexit_p(sh_pfc_gpio_remove), + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, +}; + +static struct platform_device sh_pfc_gpio_device = { + .name = KBUILD_MODNAME, + .id = -1, +}; + +static int __init sh_pfc_gpio_init(void) +{ + int rc; + + rc = platform_driver_register(&sh_pfc_gpio_driver); + if (likely(!rc)) { + rc = platform_device_register(&sh_pfc_gpio_device); + if (unlikely(rc)) + platform_driver_unregister(&sh_pfc_gpio_driver); + } + + return rc; +} + +static void __exit sh_pfc_gpio_exit(void) +{ + platform_device_unregister(&sh_pfc_gpio_device); + platform_driver_unregister(&sh_pfc_gpio_driver); +} + +module_init(sh_pfc_gpio_init); +module_exit(sh_pfc_gpio_exit); + +MODULE_AUTHOR("Magnus Damm, Paul Mundt"); +MODULE_DESCRIPTION("GPIO driver for SuperH pin function controller"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pfc-gpio"); diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 522c6c46d1be..8a9ae09603d6 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -1,7 +1,8 @@ /* - * Pinmuxed GPIO support for SuperH. + * SuperH Pin Function Controller support. * * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2009 - 2012 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -11,70 +12,74 @@ #include #include -#include +#include #include -#include #include #include -#include #include -#include #include #include -static void pfc_iounmap(struct pinmux_info *pip) +static struct sh_pfc *sh_pfc __read_mostly; + +static inline bool sh_pfc_initialized(void) +{ + return !!sh_pfc; +} + +static void pfc_iounmap(struct sh_pfc *pfc) { int k; - for (k = 0; k < pip->num_resources; k++) - if (pip->window[k].virt) - iounmap(pip->window[k].virt); + for (k = 0; k < pfc->num_resources; k++) + if (pfc->window[k].virt) + iounmap(pfc->window[k].virt); - kfree(pip->window); - pip->window = NULL; + kfree(pfc->window); + pfc->window = NULL; } -static int pfc_ioremap(struct pinmux_info *pip) +static int pfc_ioremap(struct sh_pfc *pfc) { struct resource *res; int k; - if (!pip->num_resources) + if (!pfc->num_resources) return 0; - pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), + pfc->window = kzalloc(pfc->num_resources * sizeof(*pfc->window), GFP_NOWAIT); - if (!pip->window) + if (!pfc->window) goto err1; - for (k = 0; k < pip->num_resources; k++) { - res = pip->resource + k; + for (k = 0; k < pfc->num_resources; k++) { + res = pfc->resource + k; WARN_ON(resource_type(res) != IORESOURCE_MEM); - pip->window[k].phys = res->start; - pip->window[k].size = resource_size(res); - pip->window[k].virt = ioremap_nocache(res->start, + pfc->window[k].phys = res->start; + pfc->window[k].size = resource_size(res); + pfc->window[k].virt = ioremap_nocache(res->start, resource_size(res)); - if (!pip->window[k].virt) + if (!pfc->window[k].virt) goto err2; } return 0; err2: - pfc_iounmap(pip); + pfc_iounmap(pfc); err1: return -1; } -static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, +static void __iomem *pfc_phys_to_virt(struct sh_pfc *pfc, unsigned long address) { struct pfc_window *window; int k; /* scan through physical windows and convert address */ - for (k = 0; k < pip->num_resources; k++) { - window = pip->window + k; + for (k = 0; k < pfc->num_resources; k++) { + window = pfc->window + k; if (address < window->phys) continue; @@ -135,8 +140,7 @@ static void gpio_write_raw_reg(void __iomem *mapped_reg, BUG(); } -static int gpio_read_bit(struct pinmux_data_reg *dr, - unsigned long in_pos) +int sh_pfc_read_bit(struct pinmux_data_reg *dr, unsigned long in_pos) { unsigned long pos; @@ -147,9 +151,10 @@ static int gpio_read_bit(struct pinmux_data_reg *dr, return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; } +EXPORT_SYMBOL_GPL(sh_pfc_read_bit); -static void gpio_write_bit(struct pinmux_data_reg *dr, - unsigned long in_pos, unsigned long value) +void sh_pfc_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, + unsigned long value) { unsigned long pos; @@ -166,8 +171,9 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, gpio_write_raw_reg(dr->mapped_reg, dr->reg_width, dr->reg_shadow); } +EXPORT_SYMBOL_GPL(sh_pfc_write_bit); -static void config_reg_helper(struct pinmux_info *gpioc, +static void config_reg_helper(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long in_pos, void __iomem **mapped_regp, @@ -176,7 +182,7 @@ static void config_reg_helper(struct pinmux_info *gpioc, { int k; - *mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); + *mapped_regp = pfc_phys_to_virt(pfc, crp->reg); if (crp->field_width) { *maskp = (1 << crp->field_width) - 1; @@ -189,14 +195,14 @@ static void config_reg_helper(struct pinmux_info *gpioc, } } -static int read_config_reg(struct pinmux_info *gpioc, +static int read_config_reg(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long field) { void __iomem *mapped_reg; unsigned long mask, pos; - config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); pr_debug("read_reg: addr = %lx, field = %ld, " "r_width = %ld, f_width = %ld\n", @@ -205,14 +211,14 @@ static int read_config_reg(struct pinmux_info *gpioc, return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> pos) & mask; } -static void write_config_reg(struct pinmux_info *gpioc, +static void write_config_reg(struct sh_pfc *pfc, struct pinmux_cfg_reg *crp, unsigned long field, unsigned long value) { void __iomem *mapped_reg; unsigned long mask, pos, data; - config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + config_reg_helper(pfc, crp, field, &mapped_reg, &mask, &pos); pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " "r_width = %ld, f_width = %ld\n", @@ -225,30 +231,30 @@ static void write_config_reg(struct pinmux_info *gpioc, data &= mask; data |= value; - if (gpioc->unlock_reg) - gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), + if (pfc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(pfc, pfc->unlock_reg), 32, ~data); gpio_write_raw_reg(mapped_reg, crp->reg_width, data); } -static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) +static int setup_data_reg(struct sh_pfc *pfc, unsigned gpio) { - struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; struct pinmux_data_reg *data_reg; int k, n; - if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + if (!enum_in_range(gpiop->enum_id, &pfc->data)) return -1; k = 0; while (1) { - data_reg = gpioc->data_regs + k; + data_reg = pfc->data_regs + k; if (!data_reg->reg_width) break; - data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); + data_reg->mapped_reg = pfc_phys_to_virt(pfc, data_reg->reg); for (n = 0; n < data_reg->reg_width; n++) { if (data_reg->enum_ids[n] == gpiop->enum_id) { @@ -267,17 +273,17 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) return -1; } -static void setup_data_regs(struct pinmux_info *gpioc) +static void setup_data_regs(struct sh_pfc *pfc) { struct pinmux_data_reg *drp; int k; - for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++) - setup_data_reg(gpioc, k); + for (k = pfc->first_gpio; k <= pfc->last_gpio; k++) + setup_data_reg(pfc, k); k = 0; while (1) { - drp = gpioc->data_regs + k; + drp = pfc->data_regs + k; if (!drp->reg_width) break; @@ -288,23 +294,24 @@ static void setup_data_regs(struct pinmux_info *gpioc) } } -static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, +int sh_pfc_get_data_reg(struct sh_pfc *pfc, unsigned gpio, struct pinmux_data_reg **drp, int *bitp) { - struct pinmux_gpio *gpiop = &gpioc->gpios[gpio]; + struct pinmux_gpio *gpiop = &pfc->gpios[gpio]; int k, n; - if (!enum_in_range(gpiop->enum_id, &gpioc->data)) + if (!enum_in_range(gpiop->enum_id, &pfc->data)) return -1; k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT; n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT; - *drp = gpioc->data_regs + k; + *drp = pfc->data_regs + k; *bitp = n; return 0; } +EXPORT_SYMBOL_GPL(sh_pfc_get_data_reg); -static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, +static int get_config_reg(struct sh_pfc *pfc, pinmux_enum_t enum_id, struct pinmux_cfg_reg **crp, int *fieldp, int *valuep, unsigned long **cntp) @@ -315,7 +322,7 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, k = 0; while (1) { - config_reg = gpioc->cfg_regs + k; + config_reg = pfc->cfg_regs + k; r_width = config_reg->reg_width; f_width = config_reg->field_width; @@ -350,15 +357,15 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, return -1; } -static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, - int pos, pinmux_enum_t *enum_idp) +int sh_pfc_gpio_to_enum(struct sh_pfc *pfc, unsigned gpio, int pos, + pinmux_enum_t *enum_idp) { - pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id; - pinmux_enum_t *data = gpioc->gpio_data; + pinmux_enum_t enum_id = pfc->gpios[gpio].enum_id; + pinmux_enum_t *data = pfc->gpio_data; int k; - if (!enum_in_range(enum_id, &gpioc->data)) { - if (!enum_in_range(enum_id, &gpioc->mark)) { + if (!enum_in_range(enum_id, &pfc->data)) { + if (!enum_in_range(enum_id, &pfc->mark)) { pr_err("non data/mark enum_id for gpio %d\n", gpio); return -1; } @@ -369,7 +376,7 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, return pos + 1; } - for (k = 0; k < gpioc->gpio_data_size; k++) { + for (k = 0; k < pfc->gpio_data_size; k++) { if (data[k] == enum_id) { *enum_idp = data[k + 1]; return k + 1; @@ -379,11 +386,10 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio, pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio); return -1; } +EXPORT_SYMBOL_GPL(sh_pfc_gpio_to_enum); -enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE }; - -static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, - int pinmux_type, int cfg_mode) +int sh_pfc_config_gpio(struct sh_pfc *pfc, unsigned gpio, int pinmux_type, + int cfg_mode) { struct pinmux_cfg_reg *cr = NULL; pinmux_enum_t enum_id; @@ -398,19 +404,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, break; case PINMUX_TYPE_OUTPUT: - range = &gpioc->output; + range = &pfc->output; break; case PINMUX_TYPE_INPUT: - range = &gpioc->input; + range = &pfc->input; break; case PINMUX_TYPE_INPUT_PULLUP: - range = &gpioc->input_pu; + range = &pfc->input_pu; break; case PINMUX_TYPE_INPUT_PULLDOWN: - range = &gpioc->input_pd; + range = &pfc->input_pd; break; default: @@ -422,7 +428,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, field = 0; value = 0; while (1) { - pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); + pos = sh_pfc_gpio_to_enum(pfc, gpio, pos, &enum_id); if (pos <= 0) goto out_err; @@ -430,7 +436,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, break; /* first check if this is a function enum */ - in_range = enum_in_range(enum_id, &gpioc->function); + in_range = enum_in_range(enum_id, &pfc->function); if (!in_range) { /* not a function enum */ if (range) { @@ -467,19 +473,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, if (!in_range) continue; - if (get_config_reg(gpioc, enum_id, &cr, + if (get_config_reg(pfc, enum_id, &cr, &field, &value, &cntp) != 0) goto out_err; switch (cfg_mode) { case GPIO_CFG_DRYRUN: if (!*cntp || - (read_config_reg(gpioc, cr, field) != value)) + (read_config_reg(pfc, cr, field) != value)) continue; break; case GPIO_CFG_REQ: - write_config_reg(gpioc, cr, field, value); + write_config_reg(pfc, cr, field, value); *cntp = *cntp + 1; break; @@ -493,89 +499,18 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, out_err: return -1; } +EXPORT_SYMBOL_GPL(sh_pfc_config_gpio); -static DEFINE_SPINLOCK(gpio_lock); - -static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip) -{ - return container_of(chip, struct pinmux_info, chip); -} - -static int sh_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - struct pinmux_data_reg *dummy; - unsigned long flags; - int i, ret, pinmux_type; - - ret = -EINVAL; - - if (!gpioc) - goto err_out; - - spin_lock_irqsave(&gpio_lock, flags); - - if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE) - goto err_unlock; - - /* setup pin function here if no data is associated with pin */ - - if (get_data_reg(gpioc, offset, &dummy, &i) != 0) - pinmux_type = PINMUX_TYPE_FUNCTION; - else - pinmux_type = PINMUX_TYPE_GPIO; - - if (pinmux_type == PINMUX_TYPE_FUNCTION) { - if (pinmux_config_gpio(gpioc, offset, - pinmux_type, - GPIO_CFG_DRYRUN) != 0) - goto err_unlock; - - if (pinmux_config_gpio(gpioc, offset, - pinmux_type, - GPIO_CFG_REQ) != 0) - BUG(); - } - - gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[offset].flags |= pinmux_type; - - ret = 0; - err_unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - err_out: - return ret; -} - -static void sh_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; - int pinmux_type; - - if (!gpioc) - return; - - spin_lock_irqsave(&gpio_lock, flags); - - pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE; - pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE); - gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE; - - spin_unlock_irqrestore(&gpio_lock, flags); -} - -static int pinmux_direction(struct pinmux_info *gpioc, - unsigned gpio, int new_pinmux_type) +int sh_pfc_set_direction(struct sh_pfc *pfc, unsigned gpio, + int new_pinmux_type) { int pinmux_type; int ret = -EINVAL; - if (!gpioc) + if (!pfc) goto err_out; - pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE; + pinmux_type = pfc->gpios[gpio].flags & PINMUX_FLAG_TYPE; switch (pinmux_type) { case PINMUX_TYPE_GPIO: @@ -584,156 +519,55 @@ static int pinmux_direction(struct pinmux_info *gpioc, case PINMUX_TYPE_INPUT: case PINMUX_TYPE_INPUT_PULLUP: case PINMUX_TYPE_INPUT_PULLDOWN: - pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE); + sh_pfc_config_gpio(pfc, gpio, pinmux_type, GPIO_CFG_FREE); break; default: goto err_out; } - if (pinmux_config_gpio(gpioc, gpio, + if (sh_pfc_config_gpio(pfc, gpio, new_pinmux_type, GPIO_CFG_DRYRUN) != 0) goto err_out; - if (pinmux_config_gpio(gpioc, gpio, + if (sh_pfc_config_gpio(pfc, gpio, new_pinmux_type, GPIO_CFG_REQ) != 0) BUG(); - gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; - gpioc->gpios[gpio].flags |= new_pinmux_type; + pfc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE; + pfc->gpios[gpio].flags |= new_pinmux_type; ret = 0; err_out: return ret; } +EXPORT_SYMBOL_GPL(sh_pfc_set_direction); -static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +int register_sh_pfc(struct sh_pfc *pfc) { - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; + int (*initroutine)(struct sh_pfc *) = NULL; int ret; - spin_lock_irqsave(&gpio_lock, flags); - ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT); - spin_unlock_irqrestore(&gpio_lock, flags); - - return ret; -} - -static void sh_gpio_set_value(struct pinmux_info *gpioc, - unsigned gpio, int value) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - BUG(); - else - gpio_write_bit(dr, bit, value); -} + if (sh_pfc) + return -EBUSY; -static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - unsigned long flags; - int ret; - - sh_gpio_set_value(gpioc, offset, value); - spin_lock_irqsave(&gpio_lock, flags); - ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT); - spin_unlock_irqrestore(&gpio_lock, flags); - - return ret; -} - -static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) -{ - struct pinmux_data_reg *dr = NULL; - int bit = 0; - - if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) - return -EINVAL; - - return gpio_read_bit(dr, bit); -} - -static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - return sh_gpio_get_value(chip_to_pinmux(chip), offset); -} - -static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -{ - sh_gpio_set_value(chip_to_pinmux(chip), offset, value); -} - -static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - struct pinmux_info *gpioc = chip_to_pinmux(chip); - pinmux_enum_t enum_id; - pinmux_enum_t *enum_ids; - int i, k, pos; - - pos = 0; - enum_id = 0; - while (1) { - pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id); - if (pos <= 0 || !enum_id) - break; - - for (i = 0; i < gpioc->gpio_irq_size; i++) { - enum_ids = gpioc->gpio_irq[i].enum_ids; - for (k = 0; enum_ids[k]; k++) { - if (enum_ids[k] == enum_id) - return gpioc->gpio_irq[i].irq; - } - } - } - - return -ENOSYS; -} - -int register_pinmux(struct pinmux_info *pip) -{ - struct gpio_chip *chip = &pip->chip; - int ret; - - pr_info("%s handling gpio %d -> %d\n", - pip->name, pip->first_gpio, pip->last_gpio); - - ret = pfc_ioremap(pip); - if (ret < 0) + ret = pfc_ioremap(pfc); + if (unlikely(ret < 0)) return ret; - setup_data_regs(pip); - - chip->request = sh_gpio_request; - chip->free = sh_gpio_free; - chip->direction_input = sh_gpio_direction_input; - chip->get = sh_gpio_get; - chip->direction_output = sh_gpio_direction_output; - chip->set = sh_gpio_set; - chip->to_irq = sh_gpio_to_irq; - - WARN_ON(pip->first_gpio != 0); /* needs testing */ + spin_lock_init(&pfc->lock); - chip->label = pip->name; - chip->owner = THIS_MODULE; - chip->base = pip->first_gpio; - chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; + setup_data_regs(pfc); - ret = gpiochip_add(chip); - if (ret < 0) - pfc_iounmap(pip); + sh_pfc = pfc; + pr_info("%s support registered\n", pfc->name); - return ret; -} + initroutine = symbol_request(sh_pfc_register_gpiochip); + if (initroutine) { + (*initroutine)(pfc); + symbol_put_addr(initroutine); + } -int unregister_pinmux(struct pinmux_info *pip) -{ - pr_info("%s deregistering\n", pip->name); - pfc_iounmap(pip); - return gpiochip_remove(&pip->chip); + return 0; } -- cgit v1.2.3 From 374a542dee0a10c5f81edc2af17a97b06805ecd9 Mon Sep 17 00:00:00 2001 From: Matthew Vick Date: Fri, 18 May 2012 04:54:58 +0000 Subject: igb: Streamline RSS queue and queue pairing assignment logic. Rather than spread out the complexity of the RSS queue and queue pairing assignment logic, place it all in one location for simplicity and readability. Signed-off-by: Matthew Vick Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 9 ++-- drivers/net/ethernet/intel/igb/igb_main.c | 79 ++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index ae6d3f393a54..3ced7b546f7f 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -65,13 +65,10 @@ struct igb_adapter; #define MAX_Q_VECTORS 8 /* Transmit and receive queues */ -#define IGB_MAX_RX_QUEUES ((adapter->vfs_allocated_count ? 2 : \ - (hw->mac.type > e1000_82575 ? 8 : 4))) -#define IGB_MAX_RX_QUEUES_I210 4 +#define IGB_MAX_RX_QUEUES 8 +#define IGB_MAX_RX_QUEUES_82575 4 #define IGB_MAX_RX_QUEUES_I211 2 -#define IGB_MAX_TX_QUEUES 16 -#define IGB_MAX_TX_QUEUES_I210 4 -#define IGB_MAX_TX_QUEUES_I211 2 +#define IGB_MAX_TX_QUEUES 8 #define IGB_MAX_VF_MC_ENTRIES 30 #define IGB_MAX_VF_FUNCTIONS 8 #define IGB_MAX_VFTA_ENTRIES 128 diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 64090549722d..a7c9c5d77e0b 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1048,11 +1048,6 @@ static int igb_set_interrupt_capability(struct igb_adapter *adapter) if (!(adapter->flags & IGB_FLAG_QUEUE_PAIRS)) numvecs += adapter->num_tx_queues; - /* i210 and i211 can only have 4 MSIX vectors for rx/tx queues. */ - if ((adapter->hw.mac.type == e1000_i210) - || (adapter->hw.mac.type == e1000_i211)) - numvecs = 4; - /* store the number of vectors reserved for queues */ adapter->num_q_vectors = numvecs; @@ -2338,6 +2333,7 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; + u32 max_rss_queues; pci_read_config_word(pdev, PCI_COMMAND, &hw->bus.pci_cmd_word); @@ -2370,40 +2366,69 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter) } else adapter->vfs_allocated_count = max_vfs; break; - case e1000_i210: - case e1000_i211: - adapter->vfs_allocated_count = 0; - break; default: break; } #endif /* CONFIG_PCI_IOV */ + + /* Determine the maximum number of RSS queues supported. */ switch (hw->mac.type) { + case e1000_i211: + max_rss_queues = IGB_MAX_RX_QUEUES_I211; + break; + case e1000_82575: case e1000_i210: - adapter->rss_queues = min_t(u32, IGB_MAX_RX_QUEUES_I210, - num_online_cpus()); + max_rss_queues = IGB_MAX_RX_QUEUES_82575; + break; + case e1000_i350: + /* I350 cannot do RSS and SR-IOV at the same time */ + if (!!adapter->vfs_allocated_count) { + max_rss_queues = 1; + break; + } + /* fall through */ + case e1000_82576: + if (!!adapter->vfs_allocated_count) { + max_rss_queues = 2; + break; + } + /* fall through */ + case e1000_82580: + default: + max_rss_queues = IGB_MAX_RX_QUEUES; break; + } + + adapter->rss_queues = min_t(u32, max_rss_queues, num_online_cpus()); + + /* Determine if we need to pair queues. */ + switch (hw->mac.type) { + case e1000_82575: case e1000_i211: - adapter->rss_queues = min_t(u32, IGB_MAX_RX_QUEUES_I211, - num_online_cpus()); + /* Device supports enough interrupts without queue pairing. */ break; + case e1000_82576: + /* + * If VFs are going to be allocated with RSS queues then we + * should pair the queues in order to conserve interrupts due + * to limited supply. + */ + if ((adapter->rss_queues > 1) && + (adapter->vfs_allocated_count > 6)) + adapter->flags |= IGB_FLAG_QUEUE_PAIRS; + /* fall through */ + case e1000_82580: + case e1000_i350: + case e1000_i210: default: - adapter->rss_queues = min_t(u32, IGB_MAX_RX_QUEUES, - num_online_cpus()); + /* + * If rss_queues > half of max_rss_queues, pair the queues in + * order to conserve interrupts due to limited supply. + */ + if (adapter->rss_queues > (max_rss_queues / 2)) + adapter->flags |= IGB_FLAG_QUEUE_PAIRS; break; } - /* i350 cannot do RSS and SR-IOV at the same time */ - if (hw->mac.type == e1000_i350 && adapter->vfs_allocated_count) - adapter->rss_queues = 1; - - /* - * if rss_queues > 4 or vfs are going to be allocated with rss_queues - * then we should combine the queues into a queue pair in order to - * conserve interrupts due to limited supply - */ - if ((adapter->rss_queues > 4) || - ((adapter->rss_queues > 1) && (adapter->vfs_allocated_count > 6))) - adapter->flags |= IGB_FLAG_QUEUE_PAIRS; /* Setup and initialize a copy of the hw vlan table array */ adapter->shadow_vfta = kzalloc(sizeof(u32) * -- cgit v1.2.3 From d67974f0deb1f309fc13821f45e52c63402bfb24 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Thu, 14 Jun 2012 16:04:19 +0000 Subject: igb: Update firmware info output Our NVM image creation tools have evolved over the years and there are multiple versions contained in them, depending on the tool used to create them. This patch outputs the NVM versions available in ethtool -i output. rc2: (not sure why others show in log but not in the message) Added additional call to igb_set_fw_version per Community feedback. Signed-off-by: Carolyn Wyborny Tested-by: Jeff Pieper Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb.h | 16 +++++++ drivers/net/ethernet/intel/igb/igb_ethtool.c | 17 +++---- drivers/net/ethernet/intel/igb/igb_main.c | 66 ++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 3ced7b546f7f..9e572dd29ab2 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -75,6 +75,20 @@ struct igb_adapter; #define IGB_82576_VF_DEV_ID 0x10CA #define IGB_I350_VF_DEV_ID 0x1520 +/* NVM version defines */ +#define IGB_MAJOR_MASK 0xF000 +#define IGB_MINOR_MASK 0x0FF0 +#define IGB_BUILD_MASK 0x000F +#define IGB_COMB_VER_MASK 0x00FF +#define IGB_MAJOR_SHIFT 12 +#define IGB_MINOR_SHIFT 4 +#define IGB_COMB_VER_SHFT 8 +#define IGB_NVM_VER_INVALID 0xFFFF +#define IGB_ETRACK_SHIFT 16 +#define NVM_ETRACK_WORD 0x0042 +#define NVM_COMB_VER_OFF 0x0083 +#define NVM_COMB_VER_PTR 0x003d + struct vf_data_storage { unsigned char vf_mac_addresses[ETH_ALEN]; u16 vf_mc_hashes[IGB_MAX_VF_MC_ENTRIES]; @@ -368,6 +382,7 @@ struct igb_adapter { spinlock_t tmreg_lock; struct cyclecounter cc; struct timecounter tc; + char fw_version[32]; }; #define IGB_FLAG_HAS_MSI (1 << 0) @@ -417,6 +432,7 @@ extern void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *); extern bool igb_has_link(struct igb_adapter *adapter); extern void igb_set_ethtool_ops(struct net_device *); extern void igb_power_up_link(struct igb_adapter *); +extern void igb_set_fw_version(struct igb_adapter *); #ifdef CONFIG_IGB_PTP extern void igb_ptp_init(struct igb_adapter *adapter); extern void igb_ptp_remove(struct igb_adapter *adapter); diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 59d0f04d59af..a19c84cad0e9 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -710,6 +710,7 @@ static int igb_set_eeprom(struct net_device *netdev, if ((ret_val == 0) && ((first_word <= NVM_CHECKSUM_REG))) hw->nvm.ops.update(hw); + igb_set_fw_version(adapter); kfree(eeprom_buff); return ret_val; } @@ -718,20 +719,16 @@ static void igb_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { struct igb_adapter *adapter = netdev_priv(netdev); - u16 eeprom_data; strlcpy(drvinfo->driver, igb_driver_name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, igb_driver_version, sizeof(drvinfo->version)); - /* EEPROM image version # is reported as firmware version # for - * 82575 controllers */ - adapter->hw.nvm.ops.read(&adapter->hw, 5, 1, &eeprom_data); - snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d-%d", - (eeprom_data & 0xF000) >> 12, - (eeprom_data & 0x0FF0) >> 4, - eeprom_data & 0x000F); - + /* + * EEPROM image version # is reported as firmware version # for + * 82575 controllers + */ + strlcpy(drvinfo->fw_version, adapter->fw_version, + sizeof(drvinfo->fw_version)); strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = IGB_STATS_LEN; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index a7c9c5d77e0b..1e0b94ba2ef1 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1815,6 +1815,69 @@ static const struct net_device_ops igb_netdev_ops = { .ndo_set_features = igb_set_features, }; +/** + * igb_set_fw_version - Configure version string for ethtool + * @adapter: adapter struct + * + **/ +void igb_set_fw_version(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + u16 eeprom_verh, eeprom_verl, comb_verh, comb_verl, comb_offset; + u16 major, build, patch, fw_version; + u32 etrack_id; + + hw->nvm.ops.read(hw, 5, 1, &fw_version); + if (adapter->hw.mac.type != e1000_i211) { + hw->nvm.ops.read(hw, NVM_ETRACK_WORD, 1, &eeprom_verh); + hw->nvm.ops.read(hw, (NVM_ETRACK_WORD + 1), 1, &eeprom_verl); + etrack_id = (eeprom_verh << IGB_ETRACK_SHIFT) | eeprom_verl; + + /* combo image version needs to be found */ + hw->nvm.ops.read(hw, NVM_COMB_VER_PTR, 1, &comb_offset); + if ((comb_offset != 0x0) && + (comb_offset != IGB_NVM_VER_INVALID)) { + hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset + + 1), 1, &comb_verh); + hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset), + 1, &comb_verl); + + /* Only display Option Rom if it exists and is valid */ + if ((comb_verh && comb_verl) && + ((comb_verh != IGB_NVM_VER_INVALID) && + (comb_verl != IGB_NVM_VER_INVALID))) { + major = comb_verl >> IGB_COMB_VER_SHFT; + build = (comb_verl << IGB_COMB_VER_SHFT) | + (comb_verh >> IGB_COMB_VER_SHFT); + patch = comb_verh & IGB_COMB_VER_MASK; + snprintf(adapter->fw_version, + sizeof(adapter->fw_version), + "%d.%d%d, 0x%08x, %d.%d.%d", + (fw_version & IGB_MAJOR_MASK) >> + IGB_MAJOR_SHIFT, + (fw_version & IGB_MINOR_MASK) >> + IGB_MINOR_SHIFT, + (fw_version & IGB_BUILD_MASK), + etrack_id, major, build, patch); + goto out; + } + } + snprintf(adapter->fw_version, sizeof(adapter->fw_version), + "%d.%d%d, 0x%08x", + (fw_version & IGB_MAJOR_MASK) >> IGB_MAJOR_SHIFT, + (fw_version & IGB_MINOR_MASK) >> IGB_MINOR_SHIFT, + (fw_version & IGB_BUILD_MASK), etrack_id); + } else { + snprintf(adapter->fw_version, sizeof(adapter->fw_version), + "%d.%d%d", + (fw_version & IGB_MAJOR_MASK) >> IGB_MAJOR_SHIFT, + (fw_version & IGB_MINOR_MASK) >> IGB_MINOR_SHIFT, + (fw_version & IGB_BUILD_MASK)); + } +out: + return; +} + /** * igb_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -2025,6 +2088,9 @@ static int __devinit igb_probe(struct pci_dev *pdev, goto err_eeprom; } + /* get firmware version for ethtool -i */ + igb_set_fw_version(adapter); + setup_timer(&adapter->watchdog_timer, igb_watchdog, (unsigned long) adapter); setup_timer(&adapter->phy_info_timer, igb_update_phy_info, -- cgit v1.2.3 From 200e5fd50ee80ec6ab3156bdbc46a41da0a82d10 Mon Sep 17 00:00:00 2001 From: Carolyn Wyborny Date: Thu, 31 May 2012 23:39:30 +0000 Subject: igb: Version bump This patch updates the igb version to 4.0.1. Signed-off-by: Carolyn Wyborny Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/igb/igb_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 1e0b94ba2ef1..01ced68d3aac 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -59,9 +59,9 @@ #endif #include "igb.h" -#define MAJ 3 -#define MIN 4 -#define BUILD 7 +#define MAJ 4 +#define MIN 0 +#define BUILD 1 #define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \ __stringify(BUILD) "-k" char igb_driver_name[] = "igb"; -- cgit v1.2.3 From a49fda3eaa4fe70fdd14681060a7c6c6246dc927 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 8 Jun 2012 06:59:09 +0000 Subject: ixgbe: add support for 1G SX modules This patch adds support for 1G Fiber PHY modules (SFP+ modules). This support comes along side support for 1G Copper PHY modules, but uses a different PHY type (ixgbe_sfp_type_1g_sx_core). Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c | 4 +++- drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c | 23 +++++++++++++++++++---- drivers/net/ethernet/intel/ixgbe/ixgbe_type.h | 2 ++ 3 files changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c index dee64d2703f0..e7dddfd97cb9 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c @@ -241,7 +241,9 @@ static s32 ixgbe_get_link_capabilities_82599(struct ixgbe_hw *hw, /* Determine 1G link capabilities off of SFP+ type */ if (hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0 || - hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1) { + hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1) { *speed = IXGBE_LINK_SPEED_1GB_FULL; *negotiation = true; goto out; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c index 24117709d6a2..71659edf81aa 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c @@ -907,6 +907,8 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) * 8 SFP_act_lmt_DA_CORE1 - 82599-specific * 9 SFP_1g_cu_CORE0 - 82599-specific * 10 SFP_1g_cu_CORE1 - 82599-specific + * 11 SFP_1g_sx_CORE0 - 82599-specific + * 12 SFP_1g_sx_CORE1 - 82599-specific */ if (hw->mac.type == ixgbe_mac_82598EB) { if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) @@ -957,6 +959,13 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) else hw->phy.sfp_type = ixgbe_sfp_type_1g_cu_core1; + } else if (comp_codes_1g & IXGBE_SFF_1GBASESX_CAPABLE) { + if (hw->bus.lan_id == 0) + hw->phy.sfp_type = + ixgbe_sfp_type_1g_sx_core0; + else + hw->phy.sfp_type = + ixgbe_sfp_type_1g_sx_core1; } else { hw->phy.sfp_type = ixgbe_sfp_type_unknown; } @@ -1049,7 +1058,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) /* Verify supported 1G SFP modules */ if (comp_codes_10g == 0 && !(hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1 || - hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0)) { + hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0 || + hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1)) { hw->phy.type = ixgbe_phy_sfp_unsupported; status = IXGBE_ERR_SFP_NOT_SUPPORTED; goto out; @@ -1064,7 +1075,9 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) hw->mac.ops.get_device_caps(hw, &enforce_sfp); if (!(enforce_sfp & IXGBE_DEVICE_CAPS_ALLOW_ANY_SFP) && !((hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core0) || - (hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1))) { + (hw->phy.sfp_type == ixgbe_sfp_type_1g_cu_core1) || + (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core0) || + (hw->phy.sfp_type == ixgbe_sfp_type_1g_sx_core1))) { /* Make sure we're a supported PHY type */ if (hw->phy.type == ixgbe_phy_sfp_intel) { status = 0; @@ -1128,10 +1141,12 @@ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, * SR modules */ if (sfp_type == ixgbe_sfp_type_da_act_lmt_core0 || - sfp_type == ixgbe_sfp_type_1g_cu_core0) + sfp_type == ixgbe_sfp_type_1g_cu_core0 || + sfp_type == ixgbe_sfp_type_1g_sx_core0) sfp_type = ixgbe_sfp_type_srlr_core0; else if (sfp_type == ixgbe_sfp_type_da_act_lmt_core1 || - sfp_type == ixgbe_sfp_type_1g_cu_core1) + sfp_type == ixgbe_sfp_type_1g_cu_core1 || + sfp_type == ixgbe_sfp_type_1g_sx_core1) sfp_type = ixgbe_sfp_type_srlr_core1; /* Read offset to PHY init contents */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 1085c0739a3c..7416d22ec227 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -2604,6 +2604,8 @@ enum ixgbe_sfp_type { ixgbe_sfp_type_da_act_lmt_core1 = 8, ixgbe_sfp_type_1g_cu_core0 = 9, ixgbe_sfp_type_1g_cu_core1 = 10, + ixgbe_sfp_type_1g_sx_core0 = 11, + ixgbe_sfp_type_1g_sx_core1 = 12, ixgbe_sfp_type_not_present = 0xFFFE, ixgbe_sfp_type_unknown = 0xFFFF }; -- cgit v1.2.3 From db01896398ae6beba41fc14f1a90d55fd21e6738 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 8 Jun 2012 06:59:17 +0000 Subject: ixgbe: clean up ixgbe_get_settings ethtool function This patch cleans up the method used for determining the link speed of devices. The old method re-wrote some logic already existing in a mac.ops function which should be used instead. The result is much simpler to understand and removes a strange double-check of logic, as well as reducing code redundancy. Signed-off-by: Jacob Keller Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 147 ++++++++++------------- 1 file changed, 62 insertions(+), 85 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index 3178f1ec3711..bbc7da5cdb4d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -154,100 +154,60 @@ static int ixgbe_get_settings(struct net_device *netdev, { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_hw *hw = &adapter->hw; + ixgbe_link_speed supported_link; u32 link_speed = 0; + bool autoneg; bool link_up; - ecmd->supported = SUPPORTED_10000baseT_Full; - ecmd->autoneg = AUTONEG_ENABLE; - ecmd->transceiver = XCVR_EXTERNAL; - if ((hw->phy.media_type == ixgbe_media_type_copper) || - (hw->phy.multispeed_fiber)) { - ecmd->supported |= (SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg); - - switch (hw->mac.type) { - case ixgbe_mac_X540: - ecmd->supported |= SUPPORTED_100baseT_Full; - break; - default: - break; - } - - ecmd->advertising = ADVERTISED_Autoneg; - if (hw->phy.autoneg_advertised) { - if (hw->phy.autoneg_advertised & - IXGBE_LINK_SPEED_100_FULL) - ecmd->advertising |= ADVERTISED_100baseT_Full; - if (hw->phy.autoneg_advertised & - IXGBE_LINK_SPEED_10GB_FULL) - ecmd->advertising |= ADVERTISED_10000baseT_Full; - if (hw->phy.autoneg_advertised & - IXGBE_LINK_SPEED_1GB_FULL) - ecmd->advertising |= ADVERTISED_1000baseT_Full; - } else { - /* - * Default advertised modes in case - * phy.autoneg_advertised isn't set. - */ - ecmd->advertising |= (ADVERTISED_10000baseT_Full | - ADVERTISED_1000baseT_Full); - if (hw->mac.type == ixgbe_mac_X540) - ecmd->advertising |= ADVERTISED_100baseT_Full; - } - - if (hw->phy.media_type == ixgbe_media_type_copper) { - ecmd->supported |= SUPPORTED_TP; - ecmd->advertising |= ADVERTISED_TP; - ecmd->port = PORT_TP; - } else { - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising |= ADVERTISED_FIBRE; - ecmd->port = PORT_FIBRE; - } - } else if (hw->phy.media_type == ixgbe_media_type_backplane) { - /* Set as FIBRE until SERDES defined in kernel */ - if (hw->device_id == IXGBE_DEV_ID_82598_BX) { - ecmd->supported = (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE); - ecmd->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE); - ecmd->port = PORT_FIBRE; - ecmd->autoneg = AUTONEG_DISABLE; - } else if ((hw->device_id == IXGBE_DEV_ID_82599_COMBO_BACKPLANE) || - (hw->device_id == IXGBE_DEV_ID_82599_KX4_MEZZ)) { - ecmd->supported |= (SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_FIBRE); - ecmd->advertising = (ADVERTISED_10000baseT_Full | - ADVERTISED_1000baseT_Full | - ADVERTISED_Autoneg | - ADVERTISED_FIBRE); - ecmd->port = PORT_FIBRE; - } else { - ecmd->supported |= (SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE); - ecmd->advertising = (ADVERTISED_10000baseT_Full | - ADVERTISED_1000baseT_Full | - ADVERTISED_FIBRE); - ecmd->port = PORT_FIBRE; - } + hw->mac.ops.get_link_capabilities(hw, &supported_link, &autoneg); + + /* set the supported link speeds */ + if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) + ecmd->supported |= SUPPORTED_10000baseT_Full; + if (supported_link & IXGBE_LINK_SPEED_1GB_FULL) + ecmd->supported |= SUPPORTED_1000baseT_Full; + if (supported_link & IXGBE_LINK_SPEED_100_FULL) + ecmd->supported |= SUPPORTED_100baseT_Full; + + /* set the advertised speeds */ + if (hw->phy.autoneg_advertised) { + if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) + ecmd->advertising |= ADVERTISED_100baseT_Full; + if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) + ecmd->advertising |= ADVERTISED_10000baseT_Full; + if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) + ecmd->advertising |= ADVERTISED_1000baseT_Full; } else { - ecmd->supported |= SUPPORTED_FIBRE; - ecmd->advertising = (ADVERTISED_10000baseT_Full | - ADVERTISED_FIBRE); - ecmd->port = PORT_FIBRE; - ecmd->autoneg = AUTONEG_DISABLE; + /* default modes in case phy.autoneg_advertised isn't set */ + if (supported_link & IXGBE_LINK_SPEED_10GB_FULL) + ecmd->advertising |= ADVERTISED_10000baseT_Full; + if (supported_link & IXGBE_LINK_SPEED_1GB_FULL) + ecmd->advertising |= ADVERTISED_1000baseT_Full; + if (supported_link & IXGBE_LINK_SPEED_100_FULL) + ecmd->advertising |= ADVERTISED_100baseT_Full; } - /* Get PHY type */ + if (autoneg) { + ecmd->supported |= SUPPORTED_Autoneg; + ecmd->advertising |= ADVERTISED_Autoneg; + ecmd->autoneg = AUTONEG_ENABLE; + } else + ecmd->autoneg = AUTONEG_DISABLE; + + ecmd->transceiver = XCVR_EXTERNAL; + + /* Determine the remaining settings based on the PHY type. */ switch (adapter->hw.phy.type) { case ixgbe_phy_tn: case ixgbe_phy_aq: case ixgbe_phy_cu_unknown: - /* Copper 10G-BASET */ + ecmd->supported |= SUPPORTED_TP; + ecmd->advertising |= ADVERTISED_TP; ecmd->port = PORT_TP; break; case ixgbe_phy_qt: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_FIBRE; break; case ixgbe_phy_nl: @@ -257,42 +217,59 @@ static int ixgbe_get_settings(struct net_device *netdev, case ixgbe_phy_sfp_avago: case ixgbe_phy_sfp_intel: case ixgbe_phy_sfp_unknown: - switch (adapter->hw.phy.sfp_type) { /* SFP+ devices, further checking needed */ + switch (adapter->hw.phy.sfp_type) { case ixgbe_sfp_type_da_cu: case ixgbe_sfp_type_da_cu_core0: case ixgbe_sfp_type_da_cu_core1: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_DA; break; case ixgbe_sfp_type_sr: case ixgbe_sfp_type_lr: case ixgbe_sfp_type_srlr_core0: case ixgbe_sfp_type_srlr_core1: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_FIBRE; break; case ixgbe_sfp_type_not_present: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_NONE; break; case ixgbe_sfp_type_1g_cu_core0: case ixgbe_sfp_type_1g_cu_core1: + ecmd->supported |= SUPPORTED_TP; + ecmd->advertising |= ADVERTISED_TP; ecmd->port = PORT_TP; - ecmd->supported = SUPPORTED_TP; - ecmd->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_TP); + break; + case ixgbe_sfp_type_1g_sx_core0: + case ixgbe_sfp_type_1g_sx_core1: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; + ecmd->port = PORT_FIBRE; break; case ixgbe_sfp_type_unknown: default: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_OTHER; break; } break; case ixgbe_phy_xaui: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_NONE; break; case ixgbe_phy_unknown: case ixgbe_phy_generic: case ixgbe_phy_sfp_unsupported: default: + ecmd->supported |= SUPPORTED_FIBRE; + ecmd->advertising |= ADVERTISED_FIBRE; ecmd->port = PORT_OTHER; break; } -- cgit v1.2.3 From b16b2a262c234d6abd8899d41769278337fff5b5 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 20 Jun 2012 18:17:56 +0900 Subject: sh: pfc: Make gpio chip support optional where possible. This implements some Kconfig knobs for ensuring that the PFC gpio chip can be disabled or built as a module in the cases where it's optional, or forcibly enabled in cases where it's not. Signed-off-by: Paul Mundt --- drivers/sh/Kconfig | 15 +++++++++++++++ drivers/sh/Makefile | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/sh/Kconfig b/drivers/sh/Kconfig index f168a6159961..d7dbfee1bc70 100644 --- a/drivers/sh/Kconfig +++ b/drivers/sh/Kconfig @@ -2,4 +2,19 @@ menu "SuperH / SH-Mobile Driver Options" source "drivers/sh/intc/Kconfig" +comment "Pin function controller options" + +config SH_PFC + # XXX move off the gpio dependency + depends on GENERIC_GPIO + select GPIO_SH_PFC if ARCH_REQUIRE_GPIOLIB + def_bool y + +config GPIO_SH_PFC + tristate "SuperH PFC GPIO support" + depends on SH_PFC && GPIOLIB + help + This enables support for GPIOs within the SoC's pin function + controller. + endmenu diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index be5b2934f067..f5d93e8de090 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -6,5 +6,6 @@ obj-y := intc/ obj-$(CONFIG_HAVE_CLK) += clk/ obj-$(CONFIG_MAPLE) += maple/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ -obj-$(CONFIG_GENERIC_GPIO) += pfc.o pfc-gpio.o +obj-$(CONFIG_SH_PFC) += pfc.o +obj-$(CONFIG_GPIO_SH_PFC) += pfc-gpio.o obj-y += pm_runtime.o -- cgit v1.2.3 From 809d310c7e69289c94a4475d287ba1c512885478 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 20 Jun 2012 11:08:43 +0100 Subject: regulator: lp872x: Don't allow modular build Fixes link failure due to devm_gpio_request_one() Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 870f7300b156..434462ada7c0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -20,6 +20,7 @@ menuconfig REGULATOR If unsure, say no. + if REGULATOR config REGULATOR_DEBUG @@ -225,7 +226,7 @@ config REGULATOR_LP3972 on National Semiconductors LP3972 PMIC config REGULATOR_LP872X - tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators" + bool "TI/National Semiconductor LP8720/LP8725 voltage regulators" depends on I2C select REGMAP_I2C help -- cgit v1.2.3 From c59d2a6b20aef4660a9ed89224bd17eec6a54751 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 20 Jun 2012 15:01:25 +0800 Subject: regulator: max77686: Initialize regulator_config Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index a29eee33dd7b..877d4bff1120 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -217,7 +217,7 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) struct max77686_data *max77686; int i, size; int ret = 0; - struct regulator_config config; + struct regulator_config config = { }; dev_dbg(&pdev->dev, "%s\n", __func__); -- cgit v1.2.3 From f503071b03dcac6908fb7e051cfc9c45bea084c0 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Wed, 20 Jun 2012 10:50:51 +0530 Subject: regulator: max77686: Implement .set_ramp_delay() callback This patch implements the .set_ramp_delay callback to set the ramp_delay on hardware for BUCK2/3/4 if ramp_delay is set in regulator constraints. This patch also do some cleaning work for unrequired members of struct max77686_data. Signed-off-by: Yadwinder Singh Brar Acked-by: MyungJoo Ham Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 877d4bff1120..23f956a6c5f2 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -65,11 +65,34 @@ enum max77686_ramp_rate { }; struct max77686_data { - struct device *dev; - struct max77686_dev *iodev; struct regulator_dev **rdev; }; +static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) +{ + unsigned int ramp_value = RAMP_RATE_NO_CTRL; + + switch (ramp_delay) { + case 1 ... 13750: + ramp_value = RAMP_RATE_13P75MV; + break; + case 13751 ... 27500: + ramp_value = RAMP_RATE_27P5MV; + break; + case 27501 ... 55000: + ramp_value = RAMP_RATE_55MV; + break; + case 55001 ... 100000: + break; + default: + pr_warn("%s: ramp_delay: %d not supported, setting 100000\n", + rdev->desc->name, ramp_delay); + } + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX77686_RAMP_RATE_MASK, ramp_value << 6); +} + static struct regulator_ops max77686_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -90,6 +113,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = max77686_set_ramp_delay, }; #define regulator_desc_ldo(num) { \ @@ -238,20 +262,17 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) return -ENOMEM; rdev = max77686->rdev; - max77686->dev = &pdev->dev; - max77686->iodev = iodev; + config.dev = &pdev->dev; + config.regmap = iodev->regmap; platform_set_drvdata(pdev, max77686); for (i = 0; i < MAX77686_REGULATORS; i++) { - config.dev = max77686->dev; - config.regmap = iodev->regmap; - config.driver_data = max77686; config.init_data = pdata->regulators[i].initdata; rdev[i] = regulator_register(®ulators[i], &config); if (IS_ERR(rdev[i])) { ret = PTR_ERR(rdev[i]); - dev_err(max77686->dev, + dev_err(&pdev->dev, "regulator init failed for %d\n", i); rdev[i] = NULL; goto err; -- cgit v1.2.3 From baf73e2c4e25aa57a7bd5df90a93e65be0542c74 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 20 Jun 2012 16:13:15 +0800 Subject: regulator: lp872x: Return -EINVAL if pdata is NULL Return -EINVAL if pdata is NULL, otherwise we have NULL dereference bug. This patch also moves the code checking pdata earlier in lp872x_probe. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/lp872x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index d51d09852041..e8f54efb1985 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -785,11 +785,6 @@ static int lp872x_config(struct lp872x *lp) struct lp872x_platform_data *pdata = lp->pdata; int ret; - if (!pdata) { - dev_warn(lp->dev, "no platform data\n"); - return 0; - } - if (!pdata->update_config) return 0; @@ -889,6 +884,11 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) [LP8725] = LP8725_NUM_REGULATORS, }; + if (!pdata) { + dev_warn(&cl->dev, "no platform data\n"); + return -EINVAL; + } + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); if (!lp) goto err_mem; -- cgit v1.2.3 From b667a45d9f8ed98d4da2bbbd1c9083aade0f3237 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 14 Jun 2012 18:14:00 +0100 Subject: regulator: arizona: Add support for microphone supplies on Arizona devices The Wolfson Arizona platform is used for a range of low power audio hub CODECs. Many of these devices feature an integrated power supply for the microphone which is supported by this driver. Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 ++ drivers/regulator/Makefile | 1 + drivers/regulator/arizona-micsupp.c | 184 ++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/regulator/arizona-micsupp.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 434462ada7c0..e056c09661a2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -89,6 +89,13 @@ config REGULATOR_AAT2870 If you have a AnalogicTech AAT2870 say Y to enable the regulator driver. +config REGULATOR_ARIZONA + tristate "Wolfson Arizona class devices" + depends on MFD_ARIZONA + help + Support for the regulators found on Wolfson Arizona class + devices. + config REGULATOR_DA903X tristate "Dialog Semiconductor DA9030/DA9034 regulators" depends on PMIC_DA903X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e6f700748a3b..ec4a0cfb3ca7 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o +obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c new file mode 100644 index 000000000000..97d85b09a940 --- /dev/null +++ b/drivers/regulator/arizona-micsupp.c @@ -0,0 +1,184 @@ +/* + * arizona-micsupp.c -- Microphone supply for Arizona devices + * + * Copyright 2012 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define ARIZONA_MICSUPP_MAX_SELECTOR 0x1f + +struct arizona_micsupp { + struct regulator_dev *regulator; + struct arizona *arizona; + + struct regulator_consumer_supply supply; + struct regulator_init_data init_data; +}; + +static int arizona_micsupp_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector > ARIZONA_MICSUPP_MAX_SELECTOR) + return -EINVAL; + + if (selector == ARIZONA_MICSUPP_MAX_SELECTOR) + return 3300000; + else + return (selector * 50000) + 1700000; +} + +static int arizona_micsupp_map_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + unsigned int voltage; + int selector; + + if (min_uV < 1700000) + min_uV = 1700000; + + if (min_uV >= 3300000) + selector = ARIZONA_MICSUPP_MAX_SELECTOR; + else + selector = DIV_ROUND_UP(min_uV - 1700000, 50000); + + if (selector < 0) + return -EINVAL; + + voltage = arizona_micsupp_list_voltage(rdev, selector); + if (voltage < min_uV || voltage > max_uV) + return -EINVAL; + + return selector; +} + +static struct regulator_ops arizona_micsupp_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + + .list_voltage = arizona_micsupp_list_voltage, + .map_voltage = arizona_micsupp_map_voltage, + + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_desc arizona_micsupp = { + .name = "MICVDD", + .supply_name = "CPVDD", + .type = REGULATOR_VOLTAGE, + .n_voltages = ARIZONA_MICSUPP_MAX_SELECTOR + 1, + .ops = &arizona_micsupp_ops, + + .vsel_reg = ARIZONA_LDO2_CONTROL_1, + .vsel_mask = ARIZONA_LDO2_VSEL_MASK, + .enable_reg = ARIZONA_MIC_CHARGE_PUMP_1, + .enable_mask = ARIZONA_CPMIC_ENA, + + .owner = THIS_MODULE, +}; + +static const struct regulator_init_data arizona_micsupp_default = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_VOLTAGE, + .min_uV = 1700000, + .max_uV = 3300000, + }, + + .num_consumer_supplies = 1, +}; + +static __devinit int arizona_micsupp_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct arizona_micsupp *micsupp; + int ret; + + micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL); + if (micsupp == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + micsupp->arizona = arizona; + + /* + * Since the chip usually supplies itself we provide some + * default init_data for it. This will be overridden with + * platform data if provided. + */ + micsupp->init_data = arizona_micsupp_default; + micsupp->init_data.consumer_supplies = &micsupp->supply; + micsupp->supply.supply = "MICVDD"; + micsupp->supply.dev_name = dev_name(arizona->dev); + + config.dev = arizona->dev; + config.driver_data = micsupp; + config.regmap = arizona->regmap; + + if (arizona->pdata.micvdd) + config.init_data = arizona->pdata.micvdd; + else + config.init_data = &micsupp->init_data; + + micsupp->regulator = regulator_register(&arizona_micsupp, &config); + if (IS_ERR(micsupp->regulator)) { + ret = PTR_ERR(micsupp->regulator); + dev_err(arizona->dev, "Failed to register mic supply: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, micsupp); + + return 0; +} + +static __devexit int arizona_micsupp_remove(struct platform_device *pdev) +{ + struct arizona_micsupp *micsupp = platform_get_drvdata(pdev); + + regulator_unregister(micsupp->regulator); + + return 0; +} + +static struct platform_driver arizona_micsupp_driver = { + .probe = arizona_micsupp_probe, + .remove = __devexit_p(arizona_micsupp_remove), + .driver = { + .name = "arizona-micsupp", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(arizona_micsupp_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Arizona microphone supply driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:arizona-micsupp"); -- cgit v1.2.3 From c6da0ba8dfc826d476b75710abdf787ff17542b5 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Fri, 15 Jun 2012 11:04:08 +0800 Subject: dmaengine: mmp_tdma: add mmp tdma support Add support for two-channel dma under dmaengine support: mmp-adma and pxa910-squ Signed-off-by: Zhangfei Gao Signed-off-by: Leo Yan Signed-off-by: Qiao Zhou Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 10 + drivers/dma/Makefile | 1 + drivers/dma/mmp_tdma.c | 610 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 621 insertions(+) create mode 100644 drivers/dma/mmp_tdma.c (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index b3761153649d..90fec0e0e8f5 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -274,6 +274,16 @@ config DMA_SA11X0 SA-1110 SoCs. This DMA engine can only be used with on-chip devices. +config MMP_TDMA + bool "MMP Two-Channel DMA support" + default ARCH_MMP + select DMA_ENGINE + help + Support the MMP Two-Channel DMA engine. + This engine used for MMP Audio DMA and pxa910 SQU. + + Say Y here if you enabled MMP ADMA, otherwise say N. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index d679ea3f6ab0..370a65562242 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_PCH_DMA) += pch_dma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o +obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c new file mode 100644 index 000000000000..8a15cf2163dc --- /dev/null +++ b/drivers/dma/mmp_tdma.c @@ -0,0 +1,610 @@ +/* + * Driver For Marvell Two-channel DMA Engine + * + * Copyright: Marvell International Ltd. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" + +/* + * Two-Channel DMA registers + */ +#define TDBCR 0x00 /* Byte Count */ +#define TDSAR 0x10 /* Src Addr */ +#define TDDAR 0x20 /* Dst Addr */ +#define TDNDPR 0x30 /* Next Desc */ +#define TDCR 0x40 /* Control */ +#define TDCP 0x60 /* Priority*/ +#define TDCDPR 0x70 /* Current Desc */ +#define TDIMR 0x80 /* Int Mask */ +#define TDISR 0xa0 /* Int Status */ + +/* Two-Channel DMA Control Register */ +#define TDCR_SSZ_8_BITS (0x0 << 22) /* Sample Size */ +#define TDCR_SSZ_12_BITS (0x1 << 22) +#define TDCR_SSZ_16_BITS (0x2 << 22) +#define TDCR_SSZ_20_BITS (0x3 << 22) +#define TDCR_SSZ_24_BITS (0x4 << 22) +#define TDCR_SSZ_32_BITS (0x5 << 22) +#define TDCR_SSZ_SHIFT (0x1 << 22) +#define TDCR_SSZ_MASK (0x7 << 22) +#define TDCR_SSPMOD (0x1 << 21) /* SSP MOD */ +#define TDCR_ABR (0x1 << 20) /* Channel Abort */ +#define TDCR_CDE (0x1 << 17) /* Close Desc Enable */ +#define TDCR_PACKMOD (0x1 << 16) /* Pack Mode (ADMA Only) */ +#define TDCR_CHANACT (0x1 << 14) /* Channel Active */ +#define TDCR_FETCHND (0x1 << 13) /* Fetch Next Desc */ +#define TDCR_CHANEN (0x1 << 12) /* Channel Enable */ +#define TDCR_INTMODE (0x1 << 10) /* Interrupt Mode */ +#define TDCR_CHAINMOD (0x1 << 9) /* Chain Mode */ +#define TDCR_BURSTSZ_MSK (0x7 << 6) /* Burst Size */ +#define TDCR_BURSTSZ_4B (0x0 << 6) +#define TDCR_BURSTSZ_8B (0x1 << 6) +#define TDCR_BURSTSZ_16B (0x3 << 6) +#define TDCR_BURSTSZ_32B (0x6 << 6) +#define TDCR_BURSTSZ_64B (0x7 << 6) +#define TDCR_BURSTSZ_SQU_32B (0x7 << 6) +#define TDCR_BURSTSZ_128B (0x5 << 6) +#define TDCR_DSTDIR_MSK (0x3 << 4) /* Dst Direction */ +#define TDCR_DSTDIR_ADDR_HOLD (0x2 << 4) /* Dst Addr Hold */ +#define TDCR_DSTDIR_ADDR_INC (0x0 << 4) /* Dst Addr Increment */ +#define TDCR_SRCDIR_MSK (0x3 << 2) /* Src Direction */ +#define TDCR_SRCDIR_ADDR_HOLD (0x2 << 2) /* Src Addr Hold */ +#define TDCR_SRCDIR_ADDR_INC (0x0 << 2) /* Src Addr Increment */ +#define TDCR_DSTDESCCONT (0x1 << 1) +#define TDCR_SRCDESTCONT (0x1 << 0) + +/* Two-Channel DMA Int Mask Register */ +#define TDIMR_COMP (0x1 << 0) + +/* Two-Channel DMA Int Status Register */ +#define TDISR_COMP (0x1 << 0) + +/* + * Two-Channel DMA Descriptor Struct + * NOTE: desc's buf must be aligned to 16 bytes. + */ +struct mmp_tdma_desc { + u32 byte_cnt; + u32 src_addr; + u32 dst_addr; + u32 nxt_desc; +}; + +enum mmp_tdma_type { + MMP_AUD_TDMA = 0, + PXA910_SQU, +}; + +#define TDMA_ALIGNMENT 3 +#define TDMA_MAX_XFER_BYTES SZ_64K + +struct mmp_tdma_chan { + struct device *dev; + struct dma_chan chan; + struct dma_async_tx_descriptor desc; + struct tasklet_struct tasklet; + + struct mmp_tdma_desc *desc_arr; + phys_addr_t desc_arr_phys; + int desc_num; + enum dma_transfer_direction dir; + dma_addr_t dev_addr; + u32 burst_sz; + enum dma_slave_buswidth buswidth; + enum dma_status status; + + int idx; + enum mmp_tdma_type type; + int irq; + unsigned long reg_base; + + size_t buf_len; + size_t period_len; + size_t pos; +}; + +#define TDMA_CHANNEL_NUM 2 +struct mmp_tdma_device { + struct device *dev; + void __iomem *base; + struct dma_device device; + struct mmp_tdma_chan *tdmac[TDMA_CHANNEL_NUM]; + int irq; +}; + +#define to_mmp_tdma_chan(dchan) container_of(dchan, struct mmp_tdma_chan, chan) + +static void mmp_tdma_chan_set_desc(struct mmp_tdma_chan *tdmac, dma_addr_t phys) +{ + writel(phys, tdmac->reg_base + TDNDPR); + writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND, + tdmac->reg_base + TDCR); +} + +static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac) +{ + /* enable irq */ + writel(TDIMR_COMP, tdmac->reg_base + TDIMR); + /* enable dma chan */ + writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN, + tdmac->reg_base + TDCR); + tdmac->status = DMA_IN_PROGRESS; +} + +static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac) +{ + writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, + tdmac->reg_base + TDCR); + tdmac->status = DMA_SUCCESS; +} + +static void mmp_tdma_resume_chan(struct mmp_tdma_chan *tdmac) +{ + writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN, + tdmac->reg_base + TDCR); + tdmac->status = DMA_IN_PROGRESS; +} + +static void mmp_tdma_pause_chan(struct mmp_tdma_chan *tdmac) +{ + writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN, + tdmac->reg_base + TDCR); + tdmac->status = DMA_PAUSED; +} + +static int mmp_tdma_config_chan(struct mmp_tdma_chan *tdmac) +{ + unsigned int tdcr; + + mmp_tdma_disable_chan(tdmac); + + if (tdmac->dir == DMA_MEM_TO_DEV) + tdcr = TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC; + else if (tdmac->dir == DMA_DEV_TO_MEM) + tdcr = TDCR_SRCDIR_ADDR_HOLD | TDCR_DSTDIR_ADDR_INC; + + if (tdmac->type == MMP_AUD_TDMA) { + tdcr |= TDCR_PACKMOD; + + switch (tdmac->burst_sz) { + case 4: + tdcr |= TDCR_BURSTSZ_4B; + break; + case 8: + tdcr |= TDCR_BURSTSZ_8B; + break; + case 16: + tdcr |= TDCR_BURSTSZ_16B; + break; + case 32: + tdcr |= TDCR_BURSTSZ_32B; + break; + case 64: + tdcr |= TDCR_BURSTSZ_64B; + break; + case 128: + tdcr |= TDCR_BURSTSZ_128B; + break; + default: + dev_err(tdmac->dev, "mmp_tdma: unknown burst size.\n"); + return -EINVAL; + } + + switch (tdmac->buswidth) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + tdcr |= TDCR_SSZ_8_BITS; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + tdcr |= TDCR_SSZ_16_BITS; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + tdcr |= TDCR_SSZ_32_BITS; + break; + default: + dev_err(tdmac->dev, "mmp_tdma: unknown bus size.\n"); + return -EINVAL; + } + } else if (tdmac->type == PXA910_SQU) { + tdcr |= TDCR_BURSTSZ_SQU_32B; + tdcr |= TDCR_SSPMOD; + } + + writel(tdcr, tdmac->reg_base + TDCR); + return 0; +} + +static int mmp_tdma_clear_chan_irq(struct mmp_tdma_chan *tdmac) +{ + u32 reg = readl(tdmac->reg_base + TDISR); + + if (reg & TDISR_COMP) { + /* clear irq */ + reg &= ~TDISR_COMP; + writel(reg, tdmac->reg_base + TDISR); + + return 0; + } + return -EAGAIN; +} + +static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id) +{ + struct mmp_tdma_chan *tdmac = dev_id; + + if (mmp_tdma_clear_chan_irq(tdmac) == 0) { + tdmac->pos = (tdmac->pos + tdmac->period_len) % tdmac->buf_len; + tasklet_schedule(&tdmac->tasklet); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} + +static irqreturn_t mmp_tdma_int_handler(int irq, void *dev_id) +{ + struct mmp_tdma_device *tdev = dev_id; + int i, ret; + int irq_num = 0; + + for (i = 0; i < TDMA_CHANNEL_NUM; i++) { + struct mmp_tdma_chan *tdmac = tdev->tdmac[i]; + + ret = mmp_tdma_chan_handler(irq, tdmac); + if (ret == IRQ_HANDLED) + irq_num++; + } + + if (irq_num) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static void dma_do_tasklet(unsigned long data) +{ + struct mmp_tdma_chan *tdmac = (struct mmp_tdma_chan *)data; + + if (tdmac->desc.callback) + tdmac->desc.callback(tdmac->desc.callback_param); + +} + +static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac) +{ + struct gen_pool *gpool; + int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc); + + gpool = sram_get_gpool("asram"); + if (tdmac->desc_arr) + gen_pool_free(gpool, (unsigned long)tdmac->desc_arr, + size); + tdmac->desc_arr = NULL; + + return; +} + +static dma_cookie_t mmp_tdma_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(tx->chan); + + mmp_tdma_chan_set_desc(tdmac, tdmac->desc_arr_phys); + + return 0; +} + +static int mmp_tdma_alloc_chan_resources(struct dma_chan *chan) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + int ret; + + dma_async_tx_descriptor_init(&tdmac->desc, chan); + tdmac->desc.tx_submit = mmp_tdma_tx_submit; + + if (tdmac->irq) { + ret = devm_request_irq(tdmac->dev, tdmac->irq, + mmp_tdma_chan_handler, IRQF_DISABLED, "tdma", tdmac); + if (ret) + return ret; + } + return 1; +} + +static void mmp_tdma_free_chan_resources(struct dma_chan *chan) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + + if (tdmac->irq) + devm_free_irq(tdmac->dev, tdmac->irq, tdmac); + mmp_tdma_free_descriptor(tdmac); + return; +} + +struct mmp_tdma_desc *mmp_tdma_alloc_descriptor(struct mmp_tdma_chan *tdmac) +{ + struct gen_pool *gpool; + int size = tdmac->desc_num * sizeof(struct mmp_tdma_desc); + + gpool = sram_get_gpool("asram"); + if (!gpool) + return NULL; + + tdmac->desc_arr = (void *)gen_pool_alloc(gpool, size); + if (!tdmac->desc_arr) + return NULL; + + tdmac->desc_arr_phys = gen_pool_virt_to_phys(gpool, + (unsigned long)tdmac->desc_arr); + + return tdmac->desc_arr; +} + +static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + void *context) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + struct mmp_tdma_desc *desc; + int num_periods = buf_len / period_len; + int i = 0, buf = 0; + + if (tdmac->status != DMA_SUCCESS) + return NULL; + + if (period_len > TDMA_MAX_XFER_BYTES) { + dev_err(tdmac->dev, + "maximum period size exceeded: %d > %d\n", + period_len, TDMA_MAX_XFER_BYTES); + goto err_out; + } + + tdmac->status = DMA_IN_PROGRESS; + tdmac->desc_num = num_periods; + desc = mmp_tdma_alloc_descriptor(tdmac); + if (!desc) + goto err_out; + + while (buf < buf_len) { + desc = &tdmac->desc_arr[i]; + + if (i + 1 == num_periods) + desc->nxt_desc = tdmac->desc_arr_phys; + else + desc->nxt_desc = tdmac->desc_arr_phys + + sizeof(*desc) * (i + 1); + + if (direction == DMA_MEM_TO_DEV) { + desc->src_addr = dma_addr; + desc->dst_addr = tdmac->dev_addr; + } else { + desc->src_addr = tdmac->dev_addr; + desc->dst_addr = dma_addr; + } + desc->byte_cnt = period_len; + dma_addr += period_len; + buf += period_len; + i++; + } + + tdmac->buf_len = buf_len; + tdmac->period_len = period_len; + tdmac->pos = 0; + + return &tdmac->desc; + +err_out: + tdmac->status = DMA_ERROR; + return NULL; +} + +static int mmp_tdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + struct dma_slave_config *dmaengine_cfg = (void *)arg; + int ret = 0; + + switch (cmd) { + case DMA_TERMINATE_ALL: + mmp_tdma_disable_chan(tdmac); + break; + case DMA_PAUSE: + mmp_tdma_pause_chan(tdmac); + break; + case DMA_RESUME: + mmp_tdma_resume_chan(tdmac); + break; + case DMA_SLAVE_CONFIG: + if (dmaengine_cfg->direction == DMA_DEV_TO_MEM) { + tdmac->dev_addr = dmaengine_cfg->src_addr; + tdmac->burst_sz = dmaengine_cfg->src_maxburst; + tdmac->buswidth = dmaengine_cfg->src_addr_width; + } else { + tdmac->dev_addr = dmaengine_cfg->dst_addr; + tdmac->burst_sz = dmaengine_cfg->dst_maxburst; + tdmac->buswidth = dmaengine_cfg->dst_addr_width; + } + tdmac->dir = dmaengine_cfg->direction; + return mmp_tdma_config_chan(tdmac); + default: + ret = -ENOSYS; + } + + return ret; +} + +static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + + dma_set_residue(txstate, tdmac->buf_len - tdmac->pos); + + return tdmac->status; +} + +static void mmp_tdma_issue_pending(struct dma_chan *chan) +{ + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + + mmp_tdma_enable_chan(tdmac); +} + +static int __devexit mmp_tdma_remove(struct platform_device *pdev) +{ + struct mmp_tdma_device *tdev = platform_get_drvdata(pdev); + + dma_async_device_unregister(&tdev->device); + return 0; +} + +static int __devinit mmp_tdma_chan_init(struct mmp_tdma_device *tdev, + int idx, int irq, int type) +{ + struct mmp_tdma_chan *tdmac; + + if (idx >= TDMA_CHANNEL_NUM) { + dev_err(tdev->dev, "too many channels for device!\n"); + return -EINVAL; + } + + /* alloc channel */ + tdmac = devm_kzalloc(tdev->dev, sizeof(*tdmac), GFP_KERNEL); + if (!tdmac) { + dev_err(tdev->dev, "no free memory for DMA channels!\n"); + return -ENOMEM; + } + if (irq) + tdmac->irq = irq + idx; + tdmac->dev = tdev->dev; + tdmac->chan.device = &tdev->device; + tdmac->idx = idx; + tdmac->type = type; + tdmac->reg_base = (unsigned long)tdev->base + idx * 4; + tdmac->status = DMA_SUCCESS; + tdev->tdmac[tdmac->idx] = tdmac; + tasklet_init(&tdmac->tasklet, dma_do_tasklet, (unsigned long)tdmac); + + /* add the channel to tdma_chan list */ + list_add_tail(&tdmac->chan.device_node, + &tdev->device.channels); + + return 0; +} + +static int __devinit mmp_tdma_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + enum mmp_tdma_type type = id->driver_data; + struct mmp_tdma_device *tdev; + struct resource *iores; + int i, ret; + int irq = 0; + int chan_num = TDMA_CHANNEL_NUM; + + /* always have couple channels */ + tdev = devm_kzalloc(&pdev->dev, sizeof(*tdev), GFP_KERNEL); + if (!tdev) + return -ENOMEM; + + tdev->dev = &pdev->dev; + iores = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!iores) + return -EINVAL; + + if (resource_size(iores) != chan_num) + tdev->irq = iores->start; + else + irq = iores->start; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -EINVAL; + + tdev->base = devm_request_and_ioremap(&pdev->dev, iores); + if (!tdev->base) + return -EADDRNOTAVAIL; + + if (tdev->irq) { + ret = devm_request_irq(&pdev->dev, tdev->irq, + mmp_tdma_int_handler, IRQF_DISABLED, "tdma", tdev); + if (ret) + return ret; + } + + dma_cap_set(DMA_SLAVE, tdev->device.cap_mask); + dma_cap_set(DMA_CYCLIC, tdev->device.cap_mask); + + INIT_LIST_HEAD(&tdev->device.channels); + + /* initialize channel parameters */ + for (i = 0; i < chan_num; i++) { + ret = mmp_tdma_chan_init(tdev, i, irq, type); + if (ret) + return ret; + } + + tdev->device.dev = &pdev->dev; + tdev->device.device_alloc_chan_resources = + mmp_tdma_alloc_chan_resources; + tdev->device.device_free_chan_resources = + mmp_tdma_free_chan_resources; + tdev->device.device_prep_dma_cyclic = mmp_tdma_prep_dma_cyclic; + tdev->device.device_tx_status = mmp_tdma_tx_status; + tdev->device.device_issue_pending = mmp_tdma_issue_pending; + tdev->device.device_control = mmp_tdma_control; + tdev->device.copy_align = TDMA_ALIGNMENT; + + dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); + platform_set_drvdata(pdev, tdev); + + ret = dma_async_device_register(&tdev->device); + if (ret) { + dev_err(tdev->device.dev, "unable to register\n"); + return ret; + } + + dev_info(tdev->device.dev, "initialized\n"); + return 0; +} + +static const struct platform_device_id mmp_tdma_id_table[] = { + { "mmp-adma", MMP_AUD_TDMA }, + { "pxa910-squ", PXA910_SQU }, + { }, +}; + +static struct platform_driver mmp_tdma_driver = { + .driver = { + .name = "mmp-tdma", + .owner = THIS_MODULE, + }, + .id_table = mmp_tdma_id_table, + .probe = mmp_tdma_probe, + .remove = __devexit_p(mmp_tdma_remove), +}; + +module_platform_driver(mmp_tdma_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MMP Two-Channel DMA Driver"); +MODULE_ALIAS("platform:mmp-tdma"); +MODULE_AUTHOR("Leo Yan "); +MODULE_AUTHOR("Zhangfei Gao "); -- cgit v1.2.3 From 06d5631f56460917af3d9417ef63811cf0cad9ce Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 21 Jun 2012 00:03:41 +0900 Subject: sh: pfc: Verify pin type encoding size at build time. The encoding is tightly packed, and future changes (such as pinconf-generic support) can easily lead to a situation where we violate the encoding constraints and trample data bit/reg bits. This plugs in some sanity checks by way of a BUILD_BUG_ON() to blow up if we fail to fit. Signed-off-by: Paul Mundt --- drivers/sh/pfc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c index 8a9ae09603d6..ce4579ebd602 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -549,6 +549,11 @@ int register_sh_pfc(struct sh_pfc *pfc) int (*initroutine)(struct sh_pfc *) = NULL; int ret; + /* + * Ensure that the type encoding fits + */ + BUILD_BUG_ON(PINMUX_FLAG_TYPE > ((1 << PINMUX_FLAG_DBIT_SHIFT) - 1)); + if (sh_pfc) return -EBUSY; -- cgit v1.2.3 From 8356aad43005ecb771a67efb4182dc038b4187e3 Mon Sep 17 00:00:00 2001 From: Wanpeng Li Date: Fri, 15 Jun 2012 21:15:49 +0800 Subject: PCI: cleanup assign_requested_resources_sorted() kernel-doc warning Warning(drivers/pci/setup-bus.c:277): No description found for parameter 'fail_head' Warning(drivers/pci/setup-bus.c:277): Excess function parameter 'failed_list' description in 'assign_requested_resources_sorted' Signed-off-by: Wanpeng Li Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 8fa2d4be88de..9165d25cc792 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -265,7 +265,7 @@ out: * assign_requested_resources_sorted() - satisfy resource requests * * @head : head of the list tracking requests for resources - * @failed_list : head of the list tracking requests that could + * @fail_head : head of the list tracking requests that could * not be allocated * * Satisfy resource requests of each element in the list. Add -- cgit v1.2.3 From d6d88c832eaea6c6947ddf7b664601930a9f8a14 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 19 Jun 2012 06:54:49 -0600 Subject: PCI: use __weak consistently Use "__weak" instead of the gcc-specific "__attribute__ ((weak))" Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/pci.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 86c63fe45d11..a0b435f20bd6 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1112,7 +1112,7 @@ static struct bin_attribute pcie_config_attr = { .write = pci_write_config, }; -int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) +int __weak pcibios_add_platform_entries(struct pci_dev *dev) { return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..8f4a5ea543fc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1349,7 +1349,7 @@ void pcim_pin_device(struct pci_dev *pdev) * is the default implementation. Architecture implementations can * override this. */ -void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} +void __weak pcibios_disable_device (struct pci_dev *dev) {} static void do_pci_disable_device(struct pci_dev *dev) { @@ -1413,8 +1413,8 @@ pci_disable_device(struct pci_dev *dev) * Sets the PCIe reset state for the device. This is the default * implementation. Architecture implementations can override this. */ -int __attribute__ ((weak)) pcibios_set_pcie_reset_state(struct pci_dev *dev, - enum pcie_reset_state state) +int __weak pcibios_set_pcie_reset_state(struct pci_dev *dev, + enum pcie_reset_state state) { return -EINVAL; } @@ -3851,7 +3851,7 @@ static void __devinit pci_no_domains(void) * greater than 0xff). This is the default implementation. Architecture * implementations can override this. */ -int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) +int __weak pci_ext_cfg_avail(struct pci_dev *dev) { return 1; } -- cgit v1.2.3 From a89534edaaa7008992b878680490e9b02a665563 Mon Sep 17 00:00:00 2001 From: Woody Hung Date: Wed, 13 Jun 2012 15:01:16 +0800 Subject: rt2x00 : RT3290 chip support v4 This patch support the new chipset rt3290 wifi implementation in rt2x00. It initailize the related mac, bbp and rf register in startup phase. And this patch modify the efuse read/write method for the different efuse data offset of rt3290. Signed-off-by: Woody Hung Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/Kconfig | 8 + drivers/net/wireless/rt2x00/rt2800.h | 173 +++++++++++++++- drivers/net/wireless/rt2x00/rt2800lib.c | 350 +++++++++++++++++++++++++++----- drivers/net/wireless/rt2x00/rt2800pci.c | 82 +++++++- drivers/net/wireless/rt2x00/rt2800pci.h | 1 + drivers/net/wireless/rt2x00/rt2x00.h | 1 + drivers/net/wireless/rt2x00/rt2x00pci.c | 9 + 7 files changed, 567 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 299c3879582d..c7548da6573d 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -99,6 +99,14 @@ config RT2800PCI_RT53XX rt2800pci driver. Supported chips: RT5390 +config RT2800PCI_RT3290 + bool "rt2800pci - Include support for rt3290 devices (EXPERIMENTAL)" + depends on EXPERIMENTAL + default y + ---help--- + This adds support for rt3290 wireless chipset family to the + rt2800pci driver. + Supported chips: RT3290 endif config RT2500USB diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index 1ca88cdc6ece..e252e9bafd0e 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -68,6 +68,7 @@ #define RF3320 0x000b #define RF3322 0x000c #define RF3053 0x000d +#define RF3290 0x3290 #define RF5360 0x5360 #define RF5370 0x5370 #define RF5372 0x5372 @@ -117,6 +118,12 @@ * Registers. */ + +/* + * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. + */ +#define MAC_CSR0_3290 0x0000 + /* * E2PROM_CSR: PCI EEPROM control register. * RELOAD: Write 1 to reload eeprom content. @@ -132,6 +139,150 @@ #define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) #define E2PROM_CSR_RELOAD FIELD32(0x00000080) +/* + * CMB_CTRL_CFG + */ +#define CMB_CTRL 0x0020 +#define AUX_OPT_BIT0 FIELD32(0x00000001) +#define AUX_OPT_BIT1 FIELD32(0x00000002) +#define AUX_OPT_BIT2 FIELD32(0x00000004) +#define AUX_OPT_BIT3 FIELD32(0x00000008) +#define AUX_OPT_BIT4 FIELD32(0x00000010) +#define AUX_OPT_BIT5 FIELD32(0x00000020) +#define AUX_OPT_BIT6 FIELD32(0x00000040) +#define AUX_OPT_BIT7 FIELD32(0x00000080) +#define AUX_OPT_BIT8 FIELD32(0x00000100) +#define AUX_OPT_BIT9 FIELD32(0x00000200) +#define AUX_OPT_BIT10 FIELD32(0x00000400) +#define AUX_OPT_BIT11 FIELD32(0x00000800) +#define AUX_OPT_BIT12 FIELD32(0x00001000) +#define AUX_OPT_BIT13 FIELD32(0x00002000) +#define AUX_OPT_BIT14 FIELD32(0x00004000) +#define AUX_OPT_BIT15 FIELD32(0x00008000) +#define LDO25_LEVEL FIELD32(0x00030000) +#define LDO25_LARGEA FIELD32(0x00040000) +#define LDO25_FRC_ON FIELD32(0x00080000) +#define CMB_RSV FIELD32(0x00300000) +#define XTAL_RDY FIELD32(0x00400000) +#define PLL_LD FIELD32(0x00800000) +#define LDO_CORE_LEVEL FIELD32(0x0F000000) +#define LDO_BGSEL FIELD32(0x30000000) +#define LDO3_EN FIELD32(0x40000000) +#define LDO0_EN FIELD32(0x80000000) + +/* + * EFUSE_CSR_3290: RT3290 EEPROM + */ +#define EFUSE_CTRL_3290 0x0024 + +/* + * EFUSE_DATA3 of 3290 + */ +#define EFUSE_DATA3_3290 0x0028 + +/* + * EFUSE_DATA2 of 3290 + */ +#define EFUSE_DATA2_3290 0x002c + +/* + * EFUSE_DATA1 of 3290 + */ +#define EFUSE_DATA1_3290 0x0030 + +/* + * EFUSE_DATA0 of 3290 + */ +#define EFUSE_DATA0_3290 0x0034 + +/* + * OSC_CTRL_CFG + * Ring oscillator configuration + */ +#define OSC_CTRL 0x0038 +#define OSC_REF_CYCLE FIELD32(0x00001fff) +#define OSC_RSV FIELD32(0x0000e000) +#define OSC_CAL_CNT FIELD32(0x0fff0000) +#define OSC_CAL_ACK FIELD32(0x10000000) +#define OSC_CLK_32K_VLD FIELD32(0x20000000) +#define OSC_CAL_REQ FIELD32(0x40000000) +#define OSC_ROSC_EN FIELD32(0x80000000) + +/* + * COEX_CFG_0 + */ +#define COEX_CFG0 0x0040 +#define COEX_CFG_ANT FIELD32(0xff000000) +/* + * COEX_CFG_1 + */ +#define COEX_CFG1 0x0044 + +/* + * COEX_CFG_2 + */ +#define COEX_CFG2 0x0048 +#define BT_COEX_CFG1 FIELD32(0xff000000) +#define BT_COEX_CFG0 FIELD32(0x00ff0000) +#define WL_COEX_CFG1 FIELD32(0x0000ff00) +#define WL_COEX_CFG0 FIELD32(0x000000ff) +/* + * PLL_CTRL_CFG + * PLL configuration register + */ +#define PLL_CTRL 0x0050 +#define PLL_RESERVED_INPUT1 FIELD32(0x000000ff) +#define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) +#define PLL_CONTROL FIELD32(0x00070000) +#define PLL_LPF_R1 FIELD32(0x00080000) +#define PLL_LPF_C1_CTRL FIELD32(0x00300000) +#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) +#define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) +#define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) +#define PLL_LOCK_CTRL FIELD32(0x70000000) +#define PLL_VBGBK_EN FIELD32(0x80000000) + + +/* + * WLAN_CTRL_CFG + * RT3290 wlan configuration + */ +#define WLAN_FUN_CTRL 0x0080 +#define WLAN_EN FIELD32(0x00000001) +#define WLAN_CLK_EN FIELD32(0x00000002) +#define WLAN_RSV1 FIELD32(0x00000004) +#define WLAN_RESET FIELD32(0x00000008) +#define PCIE_APP0_CLK_REQ FIELD32(0x00000010) +#define FRC_WL_ANT_SET FIELD32(0x00000020) +#define INV_TR_SW0 FIELD32(0x00000040) +#define WLAN_GPIO_IN_BIT0 FIELD32(0x00000100) +#define WLAN_GPIO_IN_BIT1 FIELD32(0x00000200) +#define WLAN_GPIO_IN_BIT2 FIELD32(0x00000400) +#define WLAN_GPIO_IN_BIT3 FIELD32(0x00000800) +#define WLAN_GPIO_IN_BIT4 FIELD32(0x00001000) +#define WLAN_GPIO_IN_BIT5 FIELD32(0x00002000) +#define WLAN_GPIO_IN_BIT6 FIELD32(0x00004000) +#define WLAN_GPIO_IN_BIT7 FIELD32(0x00008000) +#define WLAN_GPIO_IN_BIT_ALL FIELD32(0x0000ff00) +#define WLAN_GPIO_OUT_BIT0 FIELD32(0x00010000) +#define WLAN_GPIO_OUT_BIT1 FIELD32(0x00020000) +#define WLAN_GPIO_OUT_BIT2 FIELD32(0x00040000) +#define WLAN_GPIO_OUT_BIT3 FIELD32(0x00050000) +#define WLAN_GPIO_OUT_BIT4 FIELD32(0x00100000) +#define WLAN_GPIO_OUT_BIT5 FIELD32(0x00200000) +#define WLAN_GPIO_OUT_BIT6 FIELD32(0x00400000) +#define WLAN_GPIO_OUT_BIT7 FIELD32(0x00800000) +#define WLAN_GPIO_OUT_BIT_ALL FIELD32(0x00ff0000) +#define WLAN_GPIO_OUT_OE_BIT0 FIELD32(0x01000000) +#define WLAN_GPIO_OUT_OE_BIT1 FIELD32(0x02000000) +#define WLAN_GPIO_OUT_OE_BIT2 FIELD32(0x04000000) +#define WLAN_GPIO_OUT_OE_BIT3 FIELD32(0x08000000) +#define WLAN_GPIO_OUT_OE_BIT4 FIELD32(0x10000000) +#define WLAN_GPIO_OUT_OE_BIT5 FIELD32(0x20000000) +#define WLAN_GPIO_OUT_OE_BIT6 FIELD32(0x40000000) +#define WLAN_GPIO_OUT_OE_BIT7 FIELD32(0x80000000) +#define WLAN_GPIO_OUT_OE_BIT_ALL FIELD32(0xff000000) + /* * AUX_CTRL: Aux/PCI-E related configuration */ @@ -1763,9 +1914,11 @@ struct mac_iveiv_entry { /* * BBP 3: RX Antenna */ -#define BBP3_RX_ADC FIELD8(0x03) +#define BBP3_RX_ADC FIELD8(0x03) #define BBP3_RX_ANTENNA FIELD8(0x18) #define BBP3_HT40_MINUS FIELD8(0x20) +#define BBP3_ADC_MODE_SWITCH FIELD8(0x40) +#define BBP3_ADC_INIT_MODE FIELD8(0x80) /* * BBP 4: Bandwidth @@ -1774,6 +1927,14 @@ struct mac_iveiv_entry { #define BBP4_BANDWIDTH FIELD8(0x18) #define BBP4_MAC_IF_CTRL FIELD8(0x40) +/* + * BBP 47: Bandwidth + */ +#define BBP47_TSSI_REPORT_SEL FIELD8(0x03) +#define BBP47_TSSI_UPDATE_REQ FIELD8(0x04) +#define BBP47_TSSI_TSSI_MODE FIELD8(0x18) +#define BBP47_TSSI_ADC6 FIELD8(0x80) + /* * BBP 109 */ @@ -1916,6 +2077,16 @@ struct mac_iveiv_entry { #define RFCSR27_R3 FIELD8(0x30) #define RFCSR27_R4 FIELD8(0x40) +/* + * RFCSR 29: + */ +#define RFCSR29_ADC6_TEST FIELD8(0x01) +#define RFCSR29_ADC6_INT_TEST FIELD8(0x02) +#define RFCSR29_RSSI_RESET FIELD8(0x04) +#define RFCSR29_RSSI_ON FIELD8(0x08) +#define RFCSR29_RSSI_RIP_CTRL FIELD8(0x30) +#define RFCSR29_RSSI_GAIN FIELD8(0xc0) + /* * RFCSR 30: */ diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 4d3747c3010b..068276ee8aff 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -354,16 +354,15 @@ int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev, * of 4kb. Certain USB chipsets however require different firmware, * which Ralink only provides attached to the original firmware * file. Thus for USB devices, firmware files have a length - * which is a multiple of 4kb. + * which is a multiple of 4kb. The firmware for rt3290 chip also + * have a length which is a multiple of 4kb. */ - if (rt2x00_is_usb(rt2x00dev)) { + if (rt2x00_is_usb(rt2x00dev) || rt2x00_rt(rt2x00dev, RT3290)) fw_len = 4096; - multiple = true; - } else { + else fw_len = 8192; - multiple = true; - } + multiple = true; /* * Validate the firmware length */ @@ -415,7 +414,8 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev, return -EBUSY; if (rt2x00_is_pci(rt2x00dev)) { - if (rt2x00_rt(rt2x00dev, RT3572) || + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_register_read(rt2x00dev, AUX_CTRL, ®); @@ -851,8 +851,13 @@ int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; - rt2800_register_read(rt2x00dev, GPIO_CTRL_CFG, ®); - return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2); + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + return rt2x00_get_field32(reg, WLAN_GPIO_IN_BIT0); + } else { + rt2800_register_read(rt2x00dev, GPIO_CTRL_CFG, ®); + return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2); + } } EXPORT_SYMBOL_GPL(rt2800_rfkill_poll); @@ -1935,9 +1940,54 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); } +#define RT3290_POWER_BOUND 0x27 +#define RT3290_FREQ_OFFSET_BOUND 0x5f #define RT5390_POWER_BOUND 0x27 #define RT5390_FREQ_OFFSET_BOUND 0x5f +static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev, + struct ieee80211_conf *conf, + struct rf_channel *rf, + struct channel_info *info) +{ + u8 rfcsr; + + rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1); + rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3); + rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR11_R, rf->rf2); + rt2800_rfcsr_write(rt2x00dev, 11, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr); + if (info->default_power1 > RT3290_POWER_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR49_TX, RT3290_POWER_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR49_TX, info->default_power1); + rt2800_rfcsr_write(rt2x00dev, 49, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr); + if (rt2x00dev->freq_offset > RT3290_FREQ_OFFSET_BOUND) + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, + RT3290_FREQ_OFFSET_BOUND); + else + rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset); + rt2800_rfcsr_write(rt2x00dev, 17, rfcsr); + + if (rf->channel <= 14) { + if (rf->channel == 6) + rt2800_bbp_write(rt2x00dev, 68, 0x0c); + else + rt2800_bbp_write(rt2x00dev, 68, 0x0b); + + if (rf->channel >= 1 && rf->channel <= 6) + rt2800_bbp_write(rt2x00dev, 59, 0x0f); + else if (rf->channel >= 7 && rf->channel <= 11) + rt2800_bbp_write(rt2x00dev, 59, 0x0e); + else if (rf->channel >= 12 && rf->channel <= 14) + rt2800_bbp_write(rt2x00dev, 59, 0x0d); + } +} + static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, struct rf_channel *rf, @@ -2036,15 +2086,6 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, } } } - - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, 0); - rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, 0); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - - rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); - rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); } static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, @@ -2054,7 +2095,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, { u32 reg; unsigned int tx_pin; - u8 bbp; + u8 bbp, rfcsr; if (rf->channel <= 14) { info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1); @@ -2075,6 +2116,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF3052: rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info); break; + case RF3290: + rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info); + break; case RF5360: case RF5370: case RF5372: @@ -2086,6 +2130,22 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); } + if (rt2x00_rf(rt2x00dev, RF3290) || + rt2x00_rf(rt2x00dev, RF5360) || + rt2x00_rf(rt2x00dev, RF5370) || + rt2x00_rf(rt2x00dev, RF5372) || + rt2x00_rf(rt2x00dev, RF5390) || + rt2x00_rf(rt2x00dev, RF5392)) { + rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, 0); + rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, 0); + rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); + + rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); + rt2800_rfcsr_write(rt2x00dev, 3, rfcsr); + } + /* * Change BBP settings */ @@ -2566,6 +2626,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1); rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); break; + case RF3290: case RF5360: case RF5370: case RF5372: @@ -2701,6 +2762,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) @@ -2797,10 +2859,54 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00_set_field32(®, BKOFF_SLOT_CFG_CC_DELAY_TIME, 2); rt2800_register_write(rt2x00dev, BKOFF_SLOT_CFG, reg); + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + if (rt2x00_get_field32(reg, WLAN_EN) == 1) { + rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + } + + rt2800_register_read(rt2x00dev, CMB_CTRL, ®); + if (!(rt2x00_get_field32(reg, LDO0_EN) == 1)) { + rt2x00_set_field32(®, LDO0_EN, 1); + rt2x00_set_field32(®, LDO_BGSEL, 3); + rt2800_register_write(rt2x00dev, CMB_CTRL, reg); + } + + rt2800_register_read(rt2x00dev, OSC_CTRL, ®); + rt2x00_set_field32(®, OSC_ROSC_EN, 1); + rt2x00_set_field32(®, OSC_CAL_REQ, 1); + rt2x00_set_field32(®, OSC_REF_CYCLE, 0x27); + rt2800_register_write(rt2x00dev, OSC_CTRL, reg); + + rt2800_register_read(rt2x00dev, COEX_CFG0, ®); + rt2x00_set_field32(®, COEX_CFG_ANT, 0x5e); + rt2800_register_write(rt2x00dev, COEX_CFG0, reg); + + rt2800_register_read(rt2x00dev, COEX_CFG2, ®); + rt2x00_set_field32(®, BT_COEX_CFG1, 0x00); + rt2x00_set_field32(®, BT_COEX_CFG0, 0x17); + rt2x00_set_field32(®, WL_COEX_CFG1, 0x93); + rt2x00_set_field32(®, WL_COEX_CFG0, 0x7f); + rt2800_register_write(rt2x00dev, COEX_CFG2, reg); + + rt2800_register_read(rt2x00dev, PLL_CTRL, ®); + rt2x00_set_field32(®, PLL_CONTROL, 1); + rt2800_register_write(rt2x00dev, PLL_CTRL, reg); + } + if (rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390)) { - rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); + + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_register_write(rt2x00dev, TX_SW_CFG0, + 0x00000404); + else + rt2800_register_write(rt2x00dev, TX_SW_CFG0, + 0x00000400); + rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000); if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || @@ -3209,14 +3315,16 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_wait_bbp_ready(rt2x00dev))) return -EACCES; - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_read(rt2x00dev, 4, &value); rt2x00_set_field8(&value, BBP4_MAC_IF_CTRL, 1); rt2800_bbp_write(rt2x00dev, 4, value); } if (rt2800_is_305x_soc(rt2x00dev) || + rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) @@ -3225,20 +3333,26 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 65, 0x2c); rt2800_bbp_write(rt2x00dev, 66, 0x38); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 68, 0x0b); if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) { rt2800_bbp_write(rt2x00dev, 69, 0x16); rt2800_bbp_write(rt2x00dev, 73, 0x12); - } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { + } else if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x13); rt2800_bbp_write(rt2x00dev, 75, 0x46); rt2800_bbp_write(rt2x00dev, 76, 0x28); - rt2800_bbp_write(rt2x00dev, 77, 0x59); + + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_bbp_write(rt2x00dev, 77, 0x58); + else + rt2800_bbp_write(rt2x00dev, 77, 0x59); } else { rt2800_bbp_write(rt2x00dev, 69, 0x12); rt2800_bbp_write(rt2x00dev, 73, 0x10); @@ -3263,23 +3377,33 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x37); } + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_bbp_write(rt2x00dev, 74, 0x0b); + rt2800_bbp_write(rt2x00dev, 79, 0x18); + rt2800_bbp_write(rt2x00dev, 80, 0x09); + rt2800_bbp_write(rt2x00dev, 81, 0x33); + } + rt2800_bbp_write(rt2x00dev, 82, 0x62); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 83, 0x7a); else rt2800_bbp_write(rt2x00dev, 83, 0x6a); if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860D)) rt2800_bbp_write(rt2x00dev, 84, 0x19); - else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + else if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 84, 0x9a); else rt2800_bbp_write(rt2x00dev, 84, 0x99); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 86, 0x38); else rt2800_bbp_write(rt2x00dev, 86, 0x00); @@ -3289,8 +3413,9 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 91, 0x04); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 92, 0x02); else rt2800_bbp_write(rt2x00dev, 92, 0x00); @@ -3304,6 +3429,7 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2x00_rt_rev_gte(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_gte(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_gte(rt2x00dev, RT3390, REV_RT3390E) || + rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || @@ -3312,27 +3438,32 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) else rt2800_bbp_write(rt2x00dev, 103, 0x00); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 104, 0x92); if (rt2800_is_305x_soc(rt2x00dev)) rt2800_bbp_write(rt2x00dev, 105, 0x01); + else if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_bbp_write(rt2x00dev, 105, 0x1c); else if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 105, 0x3c); else rt2800_bbp_write(rt2x00dev, 105, 0x05); - if (rt2x00_rt(rt2x00dev, RT5390)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390)) rt2800_bbp_write(rt2x00dev, 106, 0x03); else if (rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 106, 0x12); else rt2800_bbp_write(rt2x00dev, 106, 0x35); - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) rt2800_bbp_write(rt2x00dev, 128, 0x12); if (rt2x00_rt(rt2x00dev, RT5392)) { @@ -3357,6 +3488,29 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 138, value); } + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_bbp_write(rt2x00dev, 67, 0x24); + rt2800_bbp_write(rt2x00dev, 143, 0x04); + rt2800_bbp_write(rt2x00dev, 142, 0x99); + rt2800_bbp_write(rt2x00dev, 150, 0x30); + rt2800_bbp_write(rt2x00dev, 151, 0x2e); + rt2800_bbp_write(rt2x00dev, 152, 0x20); + rt2800_bbp_write(rt2x00dev, 153, 0x34); + rt2800_bbp_write(rt2x00dev, 154, 0x40); + rt2800_bbp_write(rt2x00dev, 155, 0x3b); + rt2800_bbp_write(rt2x00dev, 253, 0x04); + + rt2800_bbp_read(rt2x00dev, 47, &value); + rt2x00_set_field8(&value, BBP47_TSSI_ADC6, 1); + rt2800_bbp_write(rt2x00dev, 47, value); + + /* Use 5-bit ADC for Acquisition and 8-bit ADC for data */ + rt2800_bbp_read(rt2x00dev, 3, &value); + rt2x00_set_field8(&value, BBP3_ADC_MODE_SWITCH, 1); + rt2x00_set_field8(&value, BBP3_ADC_INIT_MODE, 1); + rt2800_bbp_write(rt2x00dev, 3, value); + } + if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { int ant, div_mode; @@ -3489,6 +3643,7 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) if (!rt2x00_rt(rt2x00dev, RT3070) && !rt2x00_rt(rt2x00dev, RT3071) && !rt2x00_rt(rt2x00dev, RT3090) && + !rt2x00_rt(rt2x00dev, RT3290) && !rt2x00_rt(rt2x00dev, RT3390) && !rt2x00_rt(rt2x00dev, RT3572) && !rt2x00_rt(rt2x00dev, RT5390) && @@ -3499,8 +3654,9 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) /* * Init RF calibration. */ - if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { + if (rt2x00_rt(rt2x00dev, RT3290) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1); rt2800_rfcsr_write(rt2x00dev, 2, rfcsr); @@ -3538,6 +3694,53 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 24, 0x16); rt2800_rfcsr_write(rt2x00dev, 25, 0x01); rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); + } else if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_rfcsr_write(rt2x00dev, 1, 0x0f); + rt2800_rfcsr_write(rt2x00dev, 2, 0x80); + rt2800_rfcsr_write(rt2x00dev, 3, 0x08); + rt2800_rfcsr_write(rt2x00dev, 4, 0x00); + rt2800_rfcsr_write(rt2x00dev, 6, 0xa0); + rt2800_rfcsr_write(rt2x00dev, 8, 0xf3); + rt2800_rfcsr_write(rt2x00dev, 9, 0x02); + rt2800_rfcsr_write(rt2x00dev, 10, 0x53); + rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); + rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); + rt2800_rfcsr_write(rt2x00dev, 18, 0x02); + rt2800_rfcsr_write(rt2x00dev, 22, 0x20); + rt2800_rfcsr_write(rt2x00dev, 25, 0x83); + rt2800_rfcsr_write(rt2x00dev, 26, 0x82); + rt2800_rfcsr_write(rt2x00dev, 27, 0x09); + rt2800_rfcsr_write(rt2x00dev, 29, 0x10); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); + rt2800_rfcsr_write(rt2x00dev, 31, 0x80); + rt2800_rfcsr_write(rt2x00dev, 32, 0x80); + rt2800_rfcsr_write(rt2x00dev, 33, 0x00); + rt2800_rfcsr_write(rt2x00dev, 34, 0x05); + rt2800_rfcsr_write(rt2x00dev, 35, 0x12); + rt2800_rfcsr_write(rt2x00dev, 36, 0x00); + rt2800_rfcsr_write(rt2x00dev, 38, 0x85); + rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); + rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); + rt2800_rfcsr_write(rt2x00dev, 42, 0xd5); + rt2800_rfcsr_write(rt2x00dev, 43, 0x7b); + rt2800_rfcsr_write(rt2x00dev, 44, 0x0e); + rt2800_rfcsr_write(rt2x00dev, 45, 0xa2); + rt2800_rfcsr_write(rt2x00dev, 46, 0x73); + rt2800_rfcsr_write(rt2x00dev, 47, 0x00); + rt2800_rfcsr_write(rt2x00dev, 48, 0x10); + rt2800_rfcsr_write(rt2x00dev, 49, 0x98); + rt2800_rfcsr_write(rt2x00dev, 52, 0x38); + rt2800_rfcsr_write(rt2x00dev, 53, 0x00); + rt2800_rfcsr_write(rt2x00dev, 54, 0x78); + rt2800_rfcsr_write(rt2x00dev, 55, 0x43); + rt2800_rfcsr_write(rt2x00dev, 56, 0x02); + rt2800_rfcsr_write(rt2x00dev, 57, 0x80); + rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); + rt2800_rfcsr_write(rt2x00dev, 59, 0x09); + rt2800_rfcsr_write(rt2x00dev, 60, 0x45); + rt2800_rfcsr_write(rt2x00dev, 61, 0xc1); } else if (rt2x00_rt(rt2x00dev, RT3390)) { rt2800_rfcsr_write(rt2x00dev, 0, 0xa0); rt2800_rfcsr_write(rt2x00dev, 1, 0xe1); @@ -3946,6 +4149,12 @@ static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 27, rfcsr); } + if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_rfcsr_read(rt2x00dev, 29, &rfcsr); + rt2x00_set_field8(&rfcsr, RFCSR29_RSSI_GAIN, 3); + rt2800_rfcsr_write(rt2x00dev, 29, rfcsr); + } + if (rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr); @@ -4052,9 +4261,14 @@ EXPORT_SYMBOL_GPL(rt2800_disable_radio); int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev) { u32 reg; + u16 efuse_ctrl_reg; - rt2800_register_read(rt2x00dev, EFUSE_CTRL, ®); + if (rt2x00_rt(rt2x00dev, RT3290)) + efuse_ctrl_reg = EFUSE_CTRL_3290; + else + efuse_ctrl_reg = EFUSE_CTRL; + rt2800_register_read(rt2x00dev, efuse_ctrl_reg, ®); return rt2x00_get_field32(reg, EFUSE_CTRL_PRESENT); } EXPORT_SYMBOL_GPL(rt2800_efuse_detect); @@ -4062,27 +4276,44 @@ EXPORT_SYMBOL_GPL(rt2800_efuse_detect); static void rt2800_efuse_read(struct rt2x00_dev *rt2x00dev, unsigned int i) { u32 reg; - + u16 efuse_ctrl_reg; + u16 efuse_data0_reg; + u16 efuse_data1_reg; + u16 efuse_data2_reg; + u16 efuse_data3_reg; + + if (rt2x00_rt(rt2x00dev, RT3290)) { + efuse_ctrl_reg = EFUSE_CTRL_3290; + efuse_data0_reg = EFUSE_DATA0_3290; + efuse_data1_reg = EFUSE_DATA1_3290; + efuse_data2_reg = EFUSE_DATA2_3290; + efuse_data3_reg = EFUSE_DATA3_3290; + } else { + efuse_ctrl_reg = EFUSE_CTRL; + efuse_data0_reg = EFUSE_DATA0; + efuse_data1_reg = EFUSE_DATA1; + efuse_data2_reg = EFUSE_DATA2; + efuse_data3_reg = EFUSE_DATA3; + } mutex_lock(&rt2x00dev->csr_mutex); - rt2800_register_read_lock(rt2x00dev, EFUSE_CTRL, ®); + rt2800_register_read_lock(rt2x00dev, efuse_ctrl_reg, ®); rt2x00_set_field32(®, EFUSE_CTRL_ADDRESS_IN, i); rt2x00_set_field32(®, EFUSE_CTRL_MODE, 0); rt2x00_set_field32(®, EFUSE_CTRL_KICK, 1); - rt2800_register_write_lock(rt2x00dev, EFUSE_CTRL, reg); + rt2800_register_write_lock(rt2x00dev, efuse_ctrl_reg, reg); /* Wait until the EEPROM has been loaded */ - rt2800_regbusy_read(rt2x00dev, EFUSE_CTRL, EFUSE_CTRL_KICK, ®); - + rt2800_regbusy_read(rt2x00dev, efuse_ctrl_reg, EFUSE_CTRL_KICK, ®); /* Apparently the data is read from end to start */ - rt2800_register_read_lock(rt2x00dev, EFUSE_DATA3, ®); + rt2800_register_read_lock(rt2x00dev, efuse_data3_reg, ®); /* The returned value is in CPU order, but eeprom is le */ *(u32 *)&rt2x00dev->eeprom[i] = cpu_to_le32(reg); - rt2800_register_read_lock(rt2x00dev, EFUSE_DATA2, ®); + rt2800_register_read_lock(rt2x00dev, efuse_data2_reg, ®); *(u32 *)&rt2x00dev->eeprom[i + 2] = cpu_to_le32(reg); - rt2800_register_read_lock(rt2x00dev, EFUSE_DATA1, ®); + rt2800_register_read_lock(rt2x00dev, efuse_data1_reg, ®); *(u32 *)&rt2x00dev->eeprom[i + 4] = cpu_to_le32(reg); - rt2800_register_read_lock(rt2x00dev, EFUSE_DATA0, ®); + rt2800_register_read_lock(rt2x00dev, efuse_data0_reg, ®); *(u32 *)&rt2x00dev->eeprom[i + 6] = cpu_to_le32(reg); mutex_unlock(&rt2x00dev->csr_mutex); @@ -4244,9 +4475,14 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) * RT28xx/RT30xx: defined in "EEPROM_NIC_CONF0_RF_TYPE" field * RT53xx: defined in "EEPROM_CHIP_ID" field */ - rt2800_register_read(rt2x00dev, MAC_CSR0, ®); - if (rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5390 || - rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5392) + if (rt2x00_rt(rt2x00dev, RT3290)) + rt2800_register_read(rt2x00dev, MAC_CSR0_3290, ®); + else + rt2800_register_read(rt2x00dev, MAC_CSR0, ®); + + if (rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT3290 || + rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5390 || + rt2x00_get_field32(reg, MAC_CSR0_CHIPSET) == RT5392) rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &value); else value = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE); @@ -4261,6 +4497,7 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RT3070: case RT3071: case RT3090: + case RT3290: case RT3390: case RT3572: case RT5390: @@ -4281,6 +4518,7 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF3021: case RF3022: case RF3052: + case RF3290: case RF3320: case RF5360: case RF5370: @@ -4597,6 +4835,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00_rf(rt2x00dev, RF2020) || rt2x00_rf(rt2x00dev, RF3021) || rt2x00_rf(rt2x00dev, RF3022) || + rt2x00_rf(rt2x00dev, RF3290) || rt2x00_rf(rt2x00dev, RF3320) || rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || @@ -4685,6 +4924,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3022: case RF3320: case RF3052: + case RF3290: case RF5360: case RF5370: case RF5372: diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 206158b67426..dd436125fe3d 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -280,7 +280,13 @@ static void rt2800pci_stop_queue(struct data_queue *queue) */ static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev) { - return FIRMWARE_RT2860; + /* + * Chip rt3290 use specific 4KB firmware named rt3290.bin. + */ + if (rt2x00_rt(rt2x00dev, RT3290)) + return FIRMWARE_RT3290; + else + return FIRMWARE_RT2860; } static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, @@ -974,6 +980,66 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev) return rt2800_validate_eeprom(rt2x00dev); } +static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + int i, count; + + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + if ((rt2x00_get_field32(reg, WLAN_EN) == 1)) + return 0; + + rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff); + rt2x00_set_field32(®, FRC_WL_ANT_SET, 1); + rt2x00_set_field32(®, WLAN_CLK_EN, 0); + rt2x00_set_field32(®, WLAN_EN, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + + udelay(REGISTER_BUSY_DELAY); + + count = 0; + do { + /* + * Check PLL_LD & XTAL_RDY. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, CMB_CTRL, ®); + if ((rt2x00_get_field32(reg, PLL_LD) == 1) && + (rt2x00_get_field32(reg, XTAL_RDY) == 1)) + break; + udelay(REGISTER_BUSY_DELAY); + } + + if (i >= REGISTER_BUSY_COUNT) { + + if (count >= 10) + return -EIO; + + rt2800_register_write(rt2x00dev, 0x58, 0x018); + udelay(REGISTER_BUSY_DELAY); + rt2800_register_write(rt2x00dev, 0x58, 0x418); + udelay(REGISTER_BUSY_DELAY); + rt2800_register_write(rt2x00dev, 0x58, 0x618); + udelay(REGISTER_BUSY_DELAY); + count++; + } else { + count = 0; + } + + rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®); + rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0); + rt2x00_set_field32(®, WLAN_CLK_EN, 1); + rt2x00_set_field32(®, WLAN_RESET, 1); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + udelay(10); + rt2x00_set_field32(®, WLAN_RESET, 0); + rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg); + udelay(10); + rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff); + } while (count != 0); + + return 0; +} static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; @@ -996,6 +1062,17 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) if (retval) return retval; + /* + * In probe phase call rt2800_enable_wlan_rt3290 to enable wlan + * clk for rt3290. That avoid the MCU fail in start phase. + */ + if (rt2x00_rt(rt2x00dev, RT3290)) { + retval = rt2800_enable_wlan_rt3290(rt2x00dev); + + if (retval) + return retval; + } + /* * This device has multiple filters for control frames * and has a separate filter for PS Poll frames. @@ -1175,6 +1252,9 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { { PCI_DEVICE(0x1432, 0x7768) }, { PCI_DEVICE(0x1462, 0x891a) }, { PCI_DEVICE(0x1a3b, 0x1059) }, +#ifdef CONFIG_RT2800PCI_RT3290 + { PCI_DEVICE(0x1814, 0x3290) }, +#endif #ifdef CONFIG_RT2800PCI_RT33XX { PCI_DEVICE(0x1814, 0x3390) }, #endif diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h index 70e050d904c8..ab22a087c50d 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.h +++ b/drivers/net/wireless/rt2x00/rt2800pci.h @@ -47,6 +47,7 @@ * 8051 firmware image. */ #define FIRMWARE_RT2860 "rt2860.bin" +#define FIRMWARE_RT3290 "rt3290.bin" #define FIRMWARE_IMAGE_BASE 0x2000 /* diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 8f754025b06e..8afb546c2b2d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -187,6 +187,7 @@ struct rt2x00_chip { #define RT3070 0x3070 #define RT3071 0x3071 #define RT3090 0x3090 /* 2.4GHz PCIe */ +#define RT3290 0x3290 #define RT3390 0x3390 #define RT3572 0x3572 #define RT3593 0x3593 diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index 0a4653a92cab..a0c8caef3b0a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -256,6 +256,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) struct ieee80211_hw *hw; struct rt2x00_dev *rt2x00dev; int retval; + u16 chip; retval = pci_enable_device(pci_dev); if (retval) { @@ -305,6 +306,14 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) if (retval) goto exit_free_device; + /* + * Because rt3290 chip use different efuse offset to read efuse data. + * So before read efuse it need to indicate it is the + * rt3290 or not. + */ + pci_read_config_word(pci_dev, PCI_DEVICE_ID, &chip); + rt2x00dev->chip.rt = chip; + retval = rt2x00lib_probe_dev(rt2x00dev); if (retval) goto exit_free_reg; -- cgit v1.2.3 From 4fc0d0160d59689a90cebcdc9c269ac1d8c6d161 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 14 Jun 2012 12:16:57 +0200 Subject: brcmfmac: introduce checkdied debugfs functionality The checkdied functionality provides useful information for analyzing firmware crashes. By exposing this information to a debugfs file users can easily provide its content in bug reports. The functionality is available only when CONFIG_BRCMDBG is selected. Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 332 ++++++++++++++++++++- 1 file changed, 331 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a07fb01bc36b..9971e130476d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,9 @@ #define CBUF_LEN (128) +/* Device console log buffer state */ +#define CONSOLE_BUFFER_MAX 2024 + struct rte_log_le { __le32 buf; /* Can't be pointer on (64-bit) hosts */ __le32 buf_size; @@ -281,7 +285,7 @@ struct rte_console { * Shared structure between dongle and the host. * The structure contains pointers to trap or assert information. */ -#define SDPCM_SHARED_VERSION 0x0002 +#define SDPCM_SHARED_VERSION 0x0003 #define SDPCM_SHARED_VERSION_MASK 0x00FF #define SDPCM_SHARED_ASSERT_BUILT 0x0100 #define SDPCM_SHARED_ASSERT 0x0200 @@ -428,6 +432,29 @@ struct brcmf_console { u8 *buf; /* Log buffer (host copy) */ uint last; /* Last buffer read index */ }; + +struct brcmf_trap_info { + __le32 type; + __le32 epc; + __le32 cpsr; + __le32 spsr; + __le32 r0; /* a1 */ + __le32 r1; /* a2 */ + __le32 r2; /* a3 */ + __le32 r3; /* a4 */ + __le32 r4; /* v1 */ + __le32 r5; /* v2 */ + __le32 r6; /* v3 */ + __le32 r7; /* v4 */ + __le32 r8; /* v5 */ + __le32 r9; /* sb/v6 */ + __le32 r10; /* sl/v7 */ + __le32 r11; /* fp/v8 */ + __le32 r12; /* ip */ + __le32 r13; /* sp */ + __le32 r14; /* lr */ + __le32 pc; /* r15 */ +}; #endif /* DEBUG */ struct sdpcm_shared { @@ -439,6 +466,7 @@ struct sdpcm_shared { u32 console_addr; /* Address of struct rte_console */ u32 msgtrace_addr; u8 tag[32]; + u32 brpt_addr; }; struct sdpcm_shared_le { @@ -450,6 +478,7 @@ struct sdpcm_shared_le { __le32 console_addr; /* Address of struct rte_console */ __le32 msgtrace_addr; u8 tag[32]; + __le32 brpt_addr; }; @@ -2953,13 +2982,311 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ + return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + shaddr = bus->ramsize - 4; + + /* + * Read last word in socram to determine + * address of sdpcm_shared structure + */ + rv = brcmf_sdbrcm_membytes(bus, false, shaddr, + (u8 *)&addr_le, 4); + if (rv < 0) + return rv; + + addr = le32_to_cpu(addr_le); + + brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_dbg(ERROR, "invalid sdpcm_shared address 0x%08X\n", + addr); + return -EINVAL; + } + + /* Read hndrte_shared structure */ + rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + return rv; + + /* Endianness */ + sh->flags = le32_to_cpu(sh_le.flags); + sh->trap_addr = le32_to_cpu(sh_le.trap_addr); + sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); + sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); + sh->assert_line = le32_to_cpu(sh_le.assert_line); + sh->console_addr = le32_to_cpu(sh_le.console_addr); + sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { + brcmf_dbg(ERROR, + "sdpcm_shared version mismatch: dhd %d dongle %d\n", + SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK); + return -EPROTO; + } + + return 0; +} + +static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, + struct sdpcm_shared *sh, char __user *data, + size_t count) +{ + u32 addr, console_ptr, console_size, console_index; + char *conbuf = NULL; + __le32 sh_val; + int rv; + loff_t pos = 0; + int nbytes = 0; + + /* obtain console information from device memory */ + addr = sh->console_addr + offsetof(struct rte_console, log_le); + rv = brcmf_sdbrcm_membytes(bus, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_ptr = le32_to_cpu(sh_val); + + addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); + rv = brcmf_sdbrcm_membytes(bus, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_size = le32_to_cpu(sh_val); + + addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); + rv = brcmf_sdbrcm_membytes(bus, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_index = le32_to_cpu(sh_val); + + /* allocate buffer for console data */ + if (console_size <= CONSOLE_BUFFER_MAX) + conbuf = vzalloc(console_size+1); + + if (!conbuf) + return -ENOMEM; + + /* obtain the console data from device */ + conbuf[console_size] = '\0'; + rv = brcmf_sdbrcm_membytes(bus, false, console_ptr, (u8 *)conbuf, + console_size); + if (rv < 0) + goto done; + + rv = simple_read_from_buffer(data, count, &pos, + conbuf + console_index, + console_size - console_index); + if (rv < 0) + goto done; + + nbytes = rv; + if (console_index > 0) { + pos = 0; + rv = simple_read_from_buffer(data+nbytes, count, &pos, + conbuf, console_index - 1); + if (rv < 0) + goto done; + rv += nbytes; + } +done: + vfree(conbuf); + return rv; +} + +static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, + char __user *data, size_t count) +{ + int error, res; + char buf[350]; + struct brcmf_trap_info tr; + int nbytes; + loff_t pos = 0; + + if ((sh->flags & SDPCM_SHARED_TRAP) == 0) + return 0; + + error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr, + sizeof(struct brcmf_trap_info)); + if (error < 0) + return error; + + nbytes = brcmf_sdio_dump_console(bus, sh, data, count); + if (nbytes < 0) + return nbytes; + + res = scnprintf(buf, sizeof(buf), + "dongle trap info: type 0x%x @ epc 0x%08x\n" + " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" + " lr 0x%08x pc 0x%08x offset 0x%x\n" + " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", + le32_to_cpu(tr.type), le32_to_cpu(tr.epc), + le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), + le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), + le32_to_cpu(tr.pc), le32_to_cpu(sh->trap_addr), + le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), + le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), + le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), + le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); + + error = simple_read_from_buffer(data+nbytes, count, &pos, buf, res); + if (error < 0) + return error; + + nbytes += error; + return nbytes; +} + +static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, + struct sdpcm_shared *sh, char __user *data, + size_t count) +{ + int error = 0; + char buf[200]; + char file[80] = "?"; + char expr[80] = ""; + int res; + loff_t pos = 0; + + if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { + brcmf_dbg(INFO, "firmware not built with -assert\n"); + return 0; + } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { + brcmf_dbg(INFO, "no assert in dongle\n"); + return 0; + } + + if (sh->assert_file_addr != 0) { + error = brcmf_sdbrcm_membytes(bus, false, sh->assert_file_addr, + (u8 *)file, 80); + if (error < 0) + return error; + } + if (sh->assert_exp_addr != 0) { + error = brcmf_sdbrcm_membytes(bus, false, sh->assert_exp_addr, + (u8 *)expr, 80); + if (error < 0) + return error; + } + + res = scnprintf(buf, sizeof(buf), + "dongle assert: %s:%d: assert(%s)\n", + file, sh->assert_line, expr); + return simple_read_from_buffer(data, count, &pos, buf, res); +} + +static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +{ + int error; + struct sdpcm_shared sh; + + down(&bus->sdsem); + error = brcmf_sdio_readshared(bus, &sh); + up(&bus->sdsem); + + if (error < 0) + return error; + + if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) + brcmf_dbg(INFO, "firmware not built with -assert\n"); + else if (sh.flags & SDPCM_SHARED_ASSERT) + brcmf_dbg(ERROR, "assertion in dongle\n"); + + if (sh.flags & SDPCM_SHARED_TRAP) + brcmf_dbg(ERROR, "firmware trap in dongle\n"); + + return 0; +} + +static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, + size_t count, loff_t *ppos) +{ + int error = 0; + struct sdpcm_shared sh; + int nbytes = 0; + loff_t pos = *ppos; + + if (pos != 0) + return 0; + + down(&bus->sdsem); + error = brcmf_sdio_readshared(bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_assert_info(bus, &sh, data, count); + if (error < 0) + goto done; + + nbytes = error; + error = brcmf_sdio_trap_info(bus, &sh, data, count); + if (error < 0) + goto done; + + error += nbytes; + *ppos += error; +done: + up(&bus->sdsem); + return error; +} + +static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, + size_t count, loff_t *ppos) +{ + struct brcmf_sdio *bus = f->private_data; + int res; + + res = brcmf_sdbrcm_died_dump(bus, data, count, ppos); + if (res > 0) + *ppos += res; + return (ssize_t)res; +} + +static const struct file_operations brcmf_sdio_forensic_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = brcmf_sdio_forensic_read +}; + static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) { struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + if (IS_ERR_OR_NULL(dentry)) + return; + + debugfs_create_file("forensics", S_IRUGO, dentry, bus, + &brcmf_sdio_forensic_ops); brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); } #else +static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +{ + return 0; +} + static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) { } @@ -2991,11 +3318,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) rxlen, msglen); } else if (timeleft == 0) { brcmf_dbg(ERROR, "resumed on timeout\n"); + brcmf_sdbrcm_checkdied(bus); } else if (pending) { brcmf_dbg(CTL, "cancelled\n"); return -ERESTARTSYS; } else { brcmf_dbg(CTL, "resumed for unknown reason?\n"); + brcmf_sdbrcm_checkdied(bus); } if (rxlen) @@ -3817,6 +4146,7 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); + if (bus) { /* De-register interrupt handler */ brcmf_sdio_intr_unregister(bus->sdiodev); -- cgit v1.2.3 From a769f9577232afe2c754606a83aad85127e7052a Mon Sep 17 00:00:00 2001 From: Jeongdo Son Date: Fri, 15 Jun 2012 02:28:01 +0900 Subject: rt2x00: Add support for BUFFALO WLI-UC-GNM2 to rt2800usb. This is a RT3070 based device. Signed-off-by: Jeongdo Son Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 20a504072895..6cf336595e25 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -971,6 +971,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0411, 0x015d) }, { USB_DEVICE(0x0411, 0x016f) }, { USB_DEVICE(0x0411, 0x01a2) }, + { USB_DEVICE(0x0411, 0x01ee) }, /* Corega */ { USB_DEVICE(0x07aa, 0x002f) }, { USB_DEVICE(0x07aa, 0x003c) }, -- cgit v1.2.3 From 35e808b7e4e9ddac1086716867fe686f52fb78ff Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:19 +0200 Subject: ath9k_hw: remove aniState->noiseFloor I don't know why somebody decided to keep a cached copy of beacon rssi in a variable called 'noiseFloor', but the caching is unnecessary and the variable name is confusing, so let's just get rid of it entirely. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 11 ++++------- drivers/net/wireless/ath/ath9k/ani.h | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index b4c77f9d7470..18ce69d7feac 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -266,11 +266,9 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; - aniState->noiseFloor = BEACON_RSSI(ah); - ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->ofdmNoiseImmunityLevel, - immunityLevel, aniState->noiseFloor, + immunityLevel, BEACON_RSSI(ah), aniState->rssiThrLow, aniState->rssiThrHigh); if (aniState->update_ani) @@ -292,7 +290,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) ATH9K_ANI_FIRSTEP_LEVEL, entry_ofdm->fir_step_level); - if ((aniState->noiseFloor >= aniState->rssiThrHigh) && + if (BEACON_RSSI(ah) >= aniState->rssiThrHigh && (!aniState->ofdmWeakSigDetectOff != entry_ofdm->ofdm_weak_signal_on)) { ath9k_hw_ani_control(ah, @@ -329,15 +327,14 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; - aniState->noiseFloor = BEACON_RSSI(ah); ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->cckNoiseImmunityLevel, immunityLevel, - aniState->noiseFloor, aniState->rssiThrLow, + BEACON_RSSI(ah), aniState->rssiThrLow, aniState->rssiThrHigh); if ((ah->opmode == NL80211_IFTYPE_STATION || ah->opmode == NL80211_IFTYPE_ADHOC) && - aniState->noiseFloor <= aniState->rssiThrLow && + BEACON_RSSI(ah) <= aniState->rssiThrLow && immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 72e2b874e179..c04d167725f5 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -128,7 +128,6 @@ struct ar5416AniState { u32 listenTime; int32_t rssiThrLow; int32_t rssiThrHigh; - u32 noiseFloor; u32 ofdmPhyErrCount; u32 cckPhyErrCount; int16_t pktRssi[2]; -- cgit v1.2.3 From 0b81cc3922bce79d0f21189648bf4f9992e9a533 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:20 +0200 Subject: ath9k_hw: fix OFDM weak signal detection handling Commit "ath9k_hw: improve ANI processing and rx desensitizing parameters" was unifying some code related to overriding OFDM weak signal detection, but seems to have gotten some of the original intent wrong, probably because of a misnamed variable. The beacon RSSI is only valid in station mode, and the main reason to check it in ath9k_hw_set_ofdm_nil is to make sure that OFDM weak signal detection stays enabled if the RSSI is low, even when the OFDM noise immunity entry is supposed to disable it. The above commit removed the mode checks and changed the code so that OFDM weak signal detection would only be changed if the rssi is high, which is wrong for everything but client mode. This patch restores the old behavior in a simplified form. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 18ce69d7feac..cf310508cac5 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -265,6 +265,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) struct ath_common *common = ath9k_hw_common(ah); const struct ani_ofdm_level_entry *entry_ofdm; const struct ani_cck_level_entry *entry_cck; + bool weak_sig; ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", aniState->ofdmNoiseImmunityLevel, @@ -290,13 +291,15 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) ATH9K_ANI_FIRSTEP_LEVEL, entry_ofdm->fir_step_level); - if (BEACON_RSSI(ah) >= aniState->rssiThrHigh && - (!aniState->ofdmWeakSigDetectOff != - entry_ofdm->ofdm_weak_signal_on)) { + weak_sig = entry_ofdm->ofdm_weak_signal_on; + if (ah->opmode == NL80211_IFTYPE_STATION && + BEACON_RSSI(ah) <= aniState->rssiThrHigh) + weak_sig = true; + + if (!aniState->ofdmWeakSigDetectOff != weak_sig) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, entry_ofdm->ofdm_weak_signal_on); - } } static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) -- cgit v1.2.3 From 7067e7014d7013645f7e866b74814f1e59e6a7cd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:21 +0200 Subject: ath9k_hw: remove confusing logic inversion in an ANI variable Code using this had already triggered smatch complaints, so remove it before it gets fixed the wrong way. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 17 ++++++++--------- drivers/net/wireless/ath/ath9k/ani.h | 2 +- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 18 +++++++++--------- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 10 +++++----- 4 files changed, 23 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index cf310508cac5..aef81707cf88 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -185,7 +185,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) } rssi = BEACON_RSSI(ah); if (rssi > aniState->rssiThrHigh) { - if (!aniState->ofdmWeakSigDetectOff) { + if (aniState->ofdmWeakSigDetect) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, false)) { @@ -200,7 +200,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) return; } } else if (rssi > aniState->rssiThrLow) { - if (aniState->ofdmWeakSigDetectOff) + if (!aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, true); @@ -211,7 +211,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) } else { if ((conf->channel->band == IEEE80211_BAND_2GHZ) && !conf_is_ht(conf)) { - if (!aniState->ofdmWeakSigDetectOff) + if (aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, false); @@ -296,7 +296,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) BEACON_RSSI(ah) <= aniState->rssiThrHigh) weak_sig = true; - if (!aniState->ofdmWeakSigDetectOff != weak_sig) + if (aniState->ofdmWeakSigDetect != weak_sig) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, entry_ofdm->ofdm_weak_signal_on); @@ -401,7 +401,7 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) if (rssi > aniState->rssiThrHigh) { /* XXX: Handle me */ } else if (rssi > aniState->rssiThrLow) { - if (aniState->ofdmWeakSigDetectOff) { + if (!aniState->ofdmWeakSigDetect) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, true)) @@ -509,9 +509,9 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) if (aniState->spurImmunityLevel != 0) ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel); - if (aniState->ofdmWeakSigDetectOff) + if (!aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - !aniState->ofdmWeakSigDetectOff); + aniState->ofdmWeakSigDetect); if (aniState->cckWeakSigThreshold) ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, aniState->cckWeakSigThreshold); @@ -887,8 +887,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; - ani->ofdmWeakSigDetectOff = - !ATH9K_ANI_USE_OFDM_WEAK_SIG; + ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; ani->update_ani = false; diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index c04d167725f5..2ed848fdf08b 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -122,7 +122,7 @@ struct ar5416AniState { u8 mrcCCKOff; u8 spurImmunityLevel; u8 firstepLevel; - u8 ofdmWeakSigDetectOff; + u8 ofdmWeakSigDetect; u8 cckWeakSigThreshold; bool update_ani; u32 listenTime; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index c7492c6a2519..d2fb78b4318f 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1042,12 +1042,12 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1114,10 +1114,10 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, ath_dbg(common, ANI, "ANI parameters:\n"); ath_dbg(common, ANI, - "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetectOff=%d\n", + "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetect=%d\n", aniState->noiseImmunityLevel, aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff); + aniState->ofdmWeakSigDetect); ath_dbg(common, ANI, "cckWeakSigThreshold=%d, firstepLevel=%d, listenTime=%d\n", aniState->cckWeakSigThreshold, @@ -1206,18 +1206,18 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { ath_dbg(common, ANI, "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, - !aniState->ofdmWeakSigDetectOff ? + aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1367,7 +1367,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, ath_dbg(common, ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff ? "on" : "off", + aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, @@ -1456,7 +1456,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = true; /* not available on pre AR9003 */ } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index d6baf69cdc14..bb186d084eb8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -825,18 +825,18 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { ath_dbg(common, ANI, "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, - !aniState->ofdmWeakSigDetectOff ? + aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1002,7 +1002,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, ath_dbg(common, ANI, "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff ? "on" : "off", + aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, @@ -1113,7 +1113,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; } -- cgit v1.2.3 From 5330df7b178f4f050c67451413020bdd4ec75262 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:22 +0200 Subject: ath9k_hw: clean up / fix ANI mode checks related to beacon RSSI Beacon RSSI is only meaningful in station mode - in ad-hoc mode it fluctuates, depending on which peer last sent a beacon, and in other modes it is not set at all. Fix places in ANI where the beacon RSSI is used to limit their use to station mode only. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index aef81707cf88..4949d8b99d2e 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -176,7 +176,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) } } - if (ah->opmode == NL80211_IFTYPE_AP) { + if (ah->opmode != NL80211_IFTYPE_STATION) { if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); @@ -236,7 +236,7 @@ static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah) return; } } - if (ah->opmode == NL80211_IFTYPE_AP) { + if (ah->opmode != NL80211_IFTYPE_STATION) { if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, aniState->firstepLevel + 1); @@ -335,8 +335,7 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) BEACON_RSSI(ah), aniState->rssiThrLow, aniState->rssiThrHigh); - if ((ah->opmode == NL80211_IFTYPE_STATION || - ah->opmode == NL80211_IFTYPE_ADHOC) && + if (ah->opmode == NL80211_IFTYPE_STATION && BEACON_RSSI(ah) <= aniState->rssiThrLow && immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; @@ -390,7 +389,7 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) aniState = &ah->curchan->ani; - if (ah->opmode == NL80211_IFTYPE_AP) { + if (ah->opmode != NL80211_IFTYPE_STATION) { if (aniState->firstepLevel > 0) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, aniState->firstepLevel - 1)) @@ -474,8 +473,7 @@ static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) aniState = &ah->curchan->ani; - if (ah->opmode != NL80211_IFTYPE_STATION - && ah->opmode != NL80211_IFTYPE_ADHOC) { + if (ah->opmode != NL80211_IFTYPE_STATION) { ath_dbg(common, ANI, "Reset ANI state opmode %u\n", ah->opmode); ah->stats.ast_ani_reset++; -- cgit v1.2.3 From 6790ae7a1383db07842b3fd03208dd86f55da57e Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:23 +0200 Subject: ath9k_hw: remove the old ANI implementation It was found to be buggy on a variety of chipsets from AR913x to AR928x. The new version (which was introduced along with AR93xx support) is more reliable in preventing connectivity dropouts and also fixes MIB interrupt storm issues. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 332 ++-------------------------- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 142 +----------- drivers/net/wireless/ath/ath9k/ar9002_hw.c | 4 - drivers/net/wireless/ath/ath9k/hw.h | 9 +- 4 files changed, 19 insertions(+), 468 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 4949d8b99d2e..175c3f5590e9 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -104,11 +104,6 @@ static const struct ani_cck_level_entry cck_level_table[] = { #define ATH9K_ANI_CCK_DEF_LEVEL \ 2 /* default level - matches the INI settings */ -static bool use_new_ani(struct ath_hw *ah) -{ - return AR_SREV_9300_20_OR_LATER(ah) || modparam_force_new_ani; -} - static void ath9k_hw_update_mibstats(struct ath_hw *ah, struct ath9k_mib_stats *stats) { @@ -131,11 +126,6 @@ static void ath9k_ani_restart(struct ath_hw *ah) aniState = &ah->curchan->ani; aniState->listenTime = 0; - if (!use_new_ani(ah)) { - ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; - cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; - } - ath_dbg(common, ANI, "Writing ofdmbase=%u cckbase=%u\n", ofdm_base, cck_base); @@ -154,110 +144,6 @@ static void ath9k_ani_restart(struct ath_hw *ah) aniState->cckPhyErrCount = 0; } -static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - struct ar5416AniState *aniState; - int32_t rssi; - - aniState = &ah->curchan->ani; - - if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { - if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, - aniState->noiseImmunityLevel + 1)) { - return; - } - } - - if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) { - if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, - aniState->spurImmunityLevel + 1)) { - return; - } - } - - if (ah->opmode != NL80211_IFTYPE_STATION) { - if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - } - return; - } - rssi = BEACON_RSSI(ah); - if (rssi > aniState->rssiThrHigh) { - if (aniState->ofdmWeakSigDetect) { - if (ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - false)) { - ath9k_hw_ani_control(ah, - ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); - return; - } - } - if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - return; - } - } else if (rssi > aniState->rssiThrLow) { - if (!aniState->ofdmWeakSigDetect) - ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - true); - if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - return; - } else { - if ((conf->channel->band == IEEE80211_BAND_2GHZ) && - !conf_is_ht(conf)) { - if (aniState->ofdmWeakSigDetect) - ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - false); - if (aniState->firstepLevel > 0) - ath9k_hw_ani_control(ah, - ATH9K_ANI_FIRSTEP_LEVEL, 0); - return; - } - } -} - -static void ath9k_hw_ani_cck_err_trigger_old(struct ath_hw *ah) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - struct ar5416AniState *aniState; - int32_t rssi; - - aniState = &ah->curchan->ani; - if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { - if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, - aniState->noiseImmunityLevel + 1)) { - return; - } - } - if (ah->opmode != NL80211_IFTYPE_STATION) { - if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - } - return; - } - rssi = BEACON_RSSI(ah); - if (rssi > aniState->rssiThrLow) { - if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel + 1); - } else { - if ((conf->channel->band == IEEE80211_BAND_2GHZ) && - !conf_is_ht(conf)) { - if (aniState->firstepLevel > 0) - ath9k_hw_ani_control(ah, - ATH9K_ANI_FIRSTEP_LEVEL, 0); - } - } -} - /* Adjust the OFDM Noise Immunity Level */ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) { @@ -309,11 +195,6 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) if (!DO_ANI(ah)) return; - if (!use_new_ani(ah)) { - ath9k_hw_ani_ofdm_err_trigger_old(ah); - return; - } - aniState = &ah->curchan->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) @@ -371,70 +252,12 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) if (!DO_ANI(ah)) return; - if (!use_new_ani(ah)) { - ath9k_hw_ani_cck_err_trigger_old(ah); - return; - } - aniState = &ah->curchan->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1); } -static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) -{ - struct ar5416AniState *aniState; - int32_t rssi; - - aniState = &ah->curchan->ani; - - if (ah->opmode != NL80211_IFTYPE_STATION) { - if (aniState->firstepLevel > 0) { - if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1)) - return; - } - } else { - rssi = BEACON_RSSI(ah); - if (rssi > aniState->rssiThrHigh) { - /* XXX: Handle me */ - } else if (rssi > aniState->rssiThrLow) { - if (!aniState->ofdmWeakSigDetect) { - if (ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - true)) - return; - } - if (aniState->firstepLevel > 0) { - if (ath9k_hw_ani_control(ah, - ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1)) - return; - } - } else { - if (aniState->firstepLevel > 0) { - if (ath9k_hw_ani_control(ah, - ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel - 1)) - return; - } - } - } - - if (aniState->spurImmunityLevel > 0) { - if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, - aniState->spurImmunityLevel - 1)) - return; - } - - if (aniState->noiseImmunityLevel > 0) { - ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, - aniState->noiseImmunityLevel - 1); - return; - } -} - /* * only lower either OFDM or CCK errors per turn * we lower the other one next time @@ -445,11 +268,6 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) aniState = &ah->curchan->ani; - if (!use_new_ani(ah)) { - ath9k_hw_ani_lower_immunity_old(ah); - return; - } - /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) { @@ -462,71 +280,6 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1); } -static void ath9k_ani_reset_old(struct ath_hw *ah, bool is_scanning) -{ - struct ar5416AniState *aniState; - struct ath9k_channel *chan = ah->curchan; - struct ath_common *common = ath9k_hw_common(ah); - - if (!DO_ANI(ah)) - return; - - aniState = &ah->curchan->ani; - - if (ah->opmode != NL80211_IFTYPE_STATION) { - ath_dbg(common, ANI, "Reset ANI state opmode %u\n", ah->opmode); - ah->stats.ast_ani_reset++; - - if (ah->opmode == NL80211_IFTYPE_AP) { - /* - * ath9k_hw_ani_control() will only process items set on - * ah->ani_function - */ - if (IS_CHAN_2GHZ(chan)) - ah->ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | - ATH9K_ANI_FIRSTEP_LEVEL); - else - ah->ani_function = 0; - } - - ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0); - ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); - ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - !ATH9K_ANI_USE_OFDM_WEAK_SIG); - ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, - ATH9K_ANI_CCK_WEAK_SIG_THR); - - ath9k_ani_restart(ah); - return; - } - - if (aniState->noiseImmunityLevel != 0) - ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, - aniState->noiseImmunityLevel); - if (aniState->spurImmunityLevel != 0) - ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, - aniState->spurImmunityLevel); - if (!aniState->ofdmWeakSigDetect) - ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - aniState->ofdmWeakSigDetect); - if (aniState->cckWeakSigThreshold) - ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, - aniState->cckWeakSigThreshold); - if (aniState->firstepLevel != 0) - ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, - aniState->firstepLevel); - - ath9k_ani_restart(ah); - - ENABLE_REGWRITE_BUFFER(ah); - - REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); - REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); - - REGWRITE_BUFFER_FLUSH(ah); -} - /* * Restore the ANI parameters in the HAL and reset the statistics. * This routine should be called for every hardware reset and for @@ -541,9 +294,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) if (!DO_ANI(ah)) return; - if (!use_new_ani(ah)) - return ath9k_ani_reset_old(ah, is_scanning); - BUG_ON(aniState == NULL); ah->stats.ast_ani_reset++; @@ -640,11 +390,6 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) return false; } - if (!use_new_ani(ah)) { - ofdm_base = AR_PHY_COUNTMAX - ah->config.ofdm_trig_high; - cck_base = AR_PHY_COUNTMAX - ah->config.cck_trig_high; - } - aniState->listenTime += listenTime; ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); @@ -652,26 +397,6 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - if (!use_new_ani(ah) && (phyCnt1 < ofdm_base || phyCnt2 < cck_base)) { - if (phyCnt1 < ofdm_base) { - ath_dbg(common, ANI, - "phyCnt1 0x%x, resetting counter value to 0x%x\n", - phyCnt1, ofdm_base); - REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base); - REG_WRITE(ah, AR_PHY_ERR_MASK_1, - AR_PHY_ERR_OFDM_TIMING); - } - if (phyCnt2 < cck_base) { - ath_dbg(common, ANI, - "phyCnt2 0x%x, resetting counter value to 0x%x\n", - phyCnt2, cck_base); - REG_WRITE(ah, AR_PHY_ERR_2, cck_base); - REG_WRITE(ah, AR_PHY_ERR_MASK_2, - AR_PHY_ERR_CCK_TIMING); - } - return false; - } - ofdmPhyErrCnt = phyCnt1 - ofdm_base; ah->stats.ast_ani_ofdmerrs += ofdmPhyErrCnt - aniState->ofdmPhyErrCount; @@ -810,9 +535,6 @@ void ath9k_hw_proc_mib_event(struct ath_hw *ah) if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { - if (!use_new_ani(ah)) - ath9k_hw_ani_read_counters(ah); - /* NB: always restart to insure the h/w counters are reset */ ath9k_ani_restart(ah); } @@ -843,45 +565,28 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ath_dbg(common, ANI, "Initialize ANI\n"); - if (use_new_ani(ah)) { - ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; - ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_NEW; - - ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_NEW; - ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_NEW; - } else { - ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD; - ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD; + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_NEW; - ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD; - ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD; - } + ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_NEW; + ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_NEW; for (i = 0; i < ARRAY_SIZE(ah->channels); i++) { struct ath9k_channel *chan = &ah->channels[i]; struct ar5416AniState *ani = &chan->ani; - if (use_new_ani(ah)) { - ani->spurImmunityLevel = - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; + ani->spurImmunityLevel = + ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; - ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; + ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - if (AR_SREV_9300_20_OR_LATER(ah)) - ani->mrcCCKOff = - !ATH9K_ANI_ENABLE_MRC_CCK; - else - ani->mrcCCKOff = true; - - ani->ofdmsTurn = true; - } else { - ani->spurImmunityLevel = - ATH9K_ANI_SPUR_IMMUNE_LVL_OLD; - ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_OLD; - - ani->cckWeakSigThreshold = - ATH9K_ANI_CCK_WEAK_SIG_THR; - } + if (AR_SREV_9300_20_OR_LATER(ah)) + ani->mrcCCKOff = + !ATH9K_ANI_ENABLE_MRC_CCK; + else + ani->mrcCCKOff = true; + + ani->ofdmsTurn = true; ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; @@ -895,13 +600,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah) * since we expect some ongoing maintenance on the tables, let's sanity * check here default level should not modify INI setting. */ - if (use_new_ani(ah)) { - ah->aniperiod = ATH9K_ANI_PERIOD_NEW; - ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_NEW; - } else { - ah->aniperiod = ATH9K_ANI_PERIOD_OLD; - ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_OLD; - } + ah->aniperiod = ATH9K_ANI_PERIOD_NEW; + ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_NEW; if (ah->config.enable_ani) ah->proc_phyerr |= HAL_PROCESS_ANI; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index d2fb78b4318f..736747cb38c0 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -995,141 +995,6 @@ static u32 ar5008_hw_compute_pll_control(struct ath_hw *ah, return pll; } -static bool ar5008_hw_ani_control_old(struct ath_hw *ah, - enum ath9k_ani_cmd cmd, - int param) -{ - struct ar5416AniState *aniState = &ah->curchan->ani; - struct ath_common *common = ath9k_hw_common(ah); - - switch (cmd & ah->ani_function) { - case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{ - u32 level = param; - - if (level >= ARRAY_SIZE(ah->totalSizeDesired)) { - ath_dbg(common, ANI, "level out of range (%u > %zu)\n", - level, ARRAY_SIZE(ah->totalSizeDesired)); - return false; - } - - REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, - AR_PHY_DESIRED_SZ_TOT_DES, - ah->totalSizeDesired[level]); - REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, - AR_PHY_AGC_CTL1_COARSE_LOW, - ah->coarse_low[level]); - REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, - AR_PHY_AGC_CTL1_COARSE_HIGH, - ah->coarse_high[level]); - REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, - AR_PHY_FIND_SIG_FIRPWR, - ah->firpwr[level]); - - if (level > aniState->noiseImmunityLevel) - ah->stats.ast_ani_niup++; - else if (level < aniState->noiseImmunityLevel) - ah->stats.ast_ani_nidown++; - aniState->noiseImmunityLevel = level; - break; - } - case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ - u32 on = param ? 1 : 0; - - if (on) - REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - else - REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - - if (on != aniState->ofdmWeakSigDetect) { - if (on) - ah->stats.ast_ani_ofdmon++; - else - ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetect = on; - } - break; - } - case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{ - static const int weakSigThrCck[] = { 8, 6 }; - u32 high = param ? 1 : 0; - - REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, - AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, - weakSigThrCck[high]); - if (high != aniState->cckWeakSigThreshold) { - if (high) - ah->stats.ast_ani_cckhigh++; - else - ah->stats.ast_ani_ccklow++; - aniState->cckWeakSigThreshold = high; - } - break; - } - case ATH9K_ANI_FIRSTEP_LEVEL:{ - static const int firstep[] = { 0, 4, 8 }; - u32 level = param; - - if (level >= ARRAY_SIZE(firstep)) { - ath_dbg(common, ANI, "level out of range (%u > %zu)\n", - level, ARRAY_SIZE(firstep)); - return false; - } - REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, - AR_PHY_FIND_SIG_FIRSTEP, - firstep[level]); - if (level > aniState->firstepLevel) - ah->stats.ast_ani_stepup++; - else if (level < aniState->firstepLevel) - ah->stats.ast_ani_stepdown++; - aniState->firstepLevel = level; - break; - } - case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{ - static const int cycpwrThr1[] = { 2, 4, 6, 8, 10, 12, 14, 16 }; - u32 level = param; - - if (level >= ARRAY_SIZE(cycpwrThr1)) { - ath_dbg(common, ANI, "level out of range (%u > %zu)\n", - level, ARRAY_SIZE(cycpwrThr1)); - return false; - } - REG_RMW_FIELD(ah, AR_PHY_TIMING5, - AR_PHY_TIMING5_CYCPWR_THR1, - cycpwrThr1[level]); - if (level > aniState->spurImmunityLevel) - ah->stats.ast_ani_spurup++; - else if (level < aniState->spurImmunityLevel) - ah->stats.ast_ani_spurdown++; - aniState->spurImmunityLevel = level; - break; - } - case ATH9K_ANI_PRESENT: - break; - default: - ath_dbg(common, ANI, "invalid cmd %u\n", cmd); - return false; - } - - ath_dbg(common, ANI, "ANI parameters:\n"); - ath_dbg(common, ANI, - "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetect=%d\n", - aniState->noiseImmunityLevel, - aniState->spurImmunityLevel, - aniState->ofdmWeakSigDetect); - ath_dbg(common, ANI, - "cckWeakSigThreshold=%d, firstepLevel=%d, listenTime=%d\n", - aniState->cckWeakSigThreshold, - aniState->firstepLevel, - aniState->listenTime); - ath_dbg(common, ANI, "ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", - aniState->ofdmPhyErrCount, - aniState->cckPhyErrCount); - - return true; -} - static bool ar5008_hw_ani_control_new(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) @@ -1545,11 +1410,8 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah) priv_ops->do_getnf = ar5008_hw_do_getnf; priv_ops->set_radar_params = ar5008_hw_set_radar_params; - if (modparam_force_new_ani) { - priv_ops->ani_control = ar5008_hw_ani_control_new; - priv_ops->ani_cache_ini_regs = ar5008_hw_ani_cache_ini_regs; - } else - priv_ops->ani_control = ar5008_hw_ani_control_old; + priv_ops->ani_control = ar5008_hw_ani_control_new; + priv_ops->ani_cache_ini_regs = ar5008_hw_ani_cache_ini_regs; if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) priv_ops->compute_pll_control = ar9160_hw_compute_pll_control; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index d9a69fc470cd..edf21ea4fe93 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -21,10 +21,6 @@ #include "ar9002_initvals.h" #include "ar9002_phy.h" -int modparam_force_new_ani; -module_param_named(force_new_ani, modparam_force_new_ani, int, 0444); -MODULE_PARM_DESC(force_new_ani, "Force new ANI for AR5008, AR9001, AR9002"); - /* General hardware code for the A5008/AR9001/AR9002 hadware families */ static void ar9002_hw_init_mode_regs(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 03d590924c64..111a046b44c6 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1019,14 +1019,7 @@ void ar9002_hw_attach_ops(struct ath_hw *ah); void ar9003_hw_attach_ops(struct ath_hw *ah); void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); -/* - * ANI work can be shared between all families but a next - * generation implementation of ANI will be used only for AR9003 only - * for now as the other families still need to be tested with the same - * next generation ANI. Feel free to start testing it though for the - * older families (AR5008, AR9001, AR9002) by using modparam_force_new_ani. - */ -extern int modparam_force_new_ani; + void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); void ath9k_hw_proc_mib_event(struct ath_hw *ah); void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan); -- cgit v1.2.3 From 465dce62cdb35f49bc0e859fb23787e1229b62a3 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:24 +0200 Subject: ath9k_hw: clean up defines and variables from the ANI implementation split Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 42 +++++++++++------------------ drivers/net/wireless/ath/ath9k/ani.h | 24 ++++++----------- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 20 +++++++------- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 20 +++++++------- 4 files changed, 43 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 175c3f5590e9..7d5367676d4e 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -117,8 +117,6 @@ static void ath9k_hw_update_mibstats(struct ath_hw *ah, static void ath9k_ani_restart(struct ath_hw *ah) { struct ar5416AniState *aniState; - struct ath_common *common = ath9k_hw_common(ah); - u32 ofdm_base = 0, cck_base = 0; if (!DO_ANI(ah)) return; @@ -126,13 +124,10 @@ static void ath9k_ani_restart(struct ath_hw *ah) aniState = &ah->curchan->ani; aniState->listenTime = 0; - ath_dbg(common, ANI, "Writing ofdmbase=%u cckbase=%u\n", - ofdm_base, cck_base); - ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_PHY_ERR_1, ofdm_base); - REG_WRITE(ah, AR_PHY_ERR_2, cck_base); + REG_WRITE(ah, AR_PHY_ERR_1, 0); + REG_WRITE(ah, AR_PHY_ERR_2, 0); REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); @@ -375,9 +370,6 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); struct ar5416AniState *aniState = &ah->curchan->ani; - u32 ofdm_base = 0; - u32 cck_base = 0; - u32 ofdmPhyErrCnt, cckPhyErrCnt; u32 phyCnt1, phyCnt2; int32_t listenTime; @@ -397,15 +389,12 @@ static bool ath9k_hw_ani_read_counters(struct ath_hw *ah) phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - ofdmPhyErrCnt = phyCnt1 - ofdm_base; - ah->stats.ast_ani_ofdmerrs += - ofdmPhyErrCnt - aniState->ofdmPhyErrCount; - aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + ah->stats.ast_ani_ofdmerrs += phyCnt1 - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = phyCnt1; + + ah->stats.ast_ani_cckerrs += phyCnt2 - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = phyCnt2; - cckPhyErrCnt = phyCnt2 - cck_base; - ah->stats.ast_ani_cckerrs += - cckPhyErrCnt - aniState->cckPhyErrCount; - aniState->cckPhyErrCount = cckPhyErrCnt; return true; } @@ -565,20 +554,19 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ath_dbg(common, ANI, "Initialize ANI\n"); - ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_NEW; - ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_NEW; + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; - ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_NEW; - ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_NEW; + ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH; + ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW; for (i = 0; i < ARRAY_SIZE(ah->channels); i++) { struct ath9k_channel *chan = &ah->channels[i]; struct ar5416AniState *ani = &chan->ani; - ani->spurImmunityLevel = - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; + ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; - ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; + ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; if (AR_SREV_9300_20_OR_LATER(ah)) ani->mrcCCKOff = @@ -600,8 +588,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah) * since we expect some ongoing maintenance on the tables, let's sanity * check here default level should not modify INI setting. */ - ah->aniperiod = ATH9K_ANI_PERIOD_NEW; - ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL_NEW; + ah->aniperiod = ATH9K_ANI_PERIOD; + ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL; if (ah->config.enable_ani) ah->proc_phyerr |= HAL_PROCESS_ANI; diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 2ed848fdf08b..79c85fc6c32c 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -24,42 +24,34 @@ #define BEACON_RSSI(ahp) (ahp->stats.avgbrssi) /* units are errors per second */ -#define ATH9K_ANI_OFDM_TRIG_HIGH_OLD 500 -#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 3500 +#define ATH9K_ANI_OFDM_TRIG_HIGH 3500 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000 /* units are errors per second */ -#define ATH9K_ANI_OFDM_TRIG_LOW_OLD 200 -#define ATH9K_ANI_OFDM_TRIG_LOW_NEW 400 +#define ATH9K_ANI_OFDM_TRIG_LOW 400 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900 /* units are errors per second */ -#define ATH9K_ANI_CCK_TRIG_HIGH_OLD 200 -#define ATH9K_ANI_CCK_TRIG_HIGH_NEW 600 +#define ATH9K_ANI_CCK_TRIG_HIGH 600 /* units are errors per second */ -#define ATH9K_ANI_CCK_TRIG_LOW_OLD 100 -#define ATH9K_ANI_CCK_TRIG_LOW_NEW 300 +#define ATH9K_ANI_CCK_TRIG_LOW 300 #define ATH9K_ANI_NOISE_IMMUNE_LVL 4 #define ATH9K_ANI_USE_OFDM_WEAK_SIG true #define ATH9K_ANI_CCK_WEAK_SIG_THR false -#define ATH9K_ANI_SPUR_IMMUNE_LVL_OLD 7 -#define ATH9K_ANI_SPUR_IMMUNE_LVL_NEW 3 +#define ATH9K_ANI_SPUR_IMMUNE_LVL 3 -#define ATH9K_ANI_FIRSTEP_LVL_OLD 0 -#define ATH9K_ANI_FIRSTEP_LVL_NEW 2 +#define ATH9K_ANI_FIRSTEP_LVL 2 #define ATH9K_ANI_RSSI_THR_HIGH 40 #define ATH9K_ANI_RSSI_THR_LOW 7 -#define ATH9K_ANI_PERIOD_OLD 100 -#define ATH9K_ANI_PERIOD_NEW 300 +#define ATH9K_ANI_PERIOD 300 /* in ms */ -#define ATH9K_ANI_POLLINTERVAL_OLD 100 -#define ATH9K_ANI_POLLINTERVAL_NEW 1000 +#define ATH9K_ANI_POLLINTERVAL 1000 #define HAL_NOISE_IMMUNE_MAX 4 #define HAL_SPUR_IMMUNE_MAX 7 diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 736747cb38c0..b6efcd9b3129 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1101,7 +1101,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, * from INI file & cap value */ value = firstep_table[level] - - firstep_table[ATH9K_ANI_FIRSTEP_LVL_NEW] + + firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstep; if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN) value = ATH9K_SIG_FIRSTEP_SETTING_MIN; @@ -1116,7 +1116,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, * from INI file & cap value */ value2 = firstep_table[level] - - firstep_table[ATH9K_ANI_FIRSTEP_LVL_NEW] + + firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstepLow; if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN) value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN; @@ -1132,7 +1132,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, chan->channel, aniState->firstepLevel, level, - ATH9K_ANI_FIRSTEP_LVL_NEW, + ATH9K_ANI_FIRSTEP_LVL, value, aniState->iniDef.firstep); ath_dbg(common, ANI, @@ -1140,7 +1140,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, chan->channel, aniState->firstepLevel, level, - ATH9K_ANI_FIRSTEP_LVL_NEW, + ATH9K_ANI_FIRSTEP_LVL, value2, aniState->iniDef.firstepLow); if (level > aniState->firstepLevel) @@ -1165,7 +1165,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, * from INI file & cap value */ value = cycpwrThr1_table[level] - - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL_NEW] + + cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1; if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value = ATH9K_SIG_SPUR_IMM_SETTING_MIN; @@ -1181,7 +1181,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, * from INI file & cap value */ value2 = cycpwrThr1_table[level] - - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL_NEW] + + cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1Ext; if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN; @@ -1196,7 +1196,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, chan->channel, aniState->spurImmunityLevel, level, - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW, + ATH9K_ANI_SPUR_IMMUNE_LVL, value, aniState->iniDef.cycpwrThr1); ath_dbg(common, ANI, @@ -1204,7 +1204,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, chan->channel, aniState->spurImmunityLevel, level, - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW, + ATH9K_ANI_SPUR_IMMUNE_LVL, value2, aniState->iniDef.cycpwrThr1Ext); if (level > aniState->spurImmunityLevel) @@ -1319,8 +1319,8 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) AR_PHY_EXT_TIMING5_CYCPWR_THR1); /* these levels just got reset to defaults by the INI */ - aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; - aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; + aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; + aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = true; /* not available on pre AR9003 */ } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index bb186d084eb8..ec8a8d5c6db5 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -855,7 +855,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, * from INI file & cap value */ value = firstep_table[level] - - firstep_table[ATH9K_ANI_FIRSTEP_LVL_NEW] + + firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstep; if (value < ATH9K_SIG_FIRSTEP_SETTING_MIN) value = ATH9K_SIG_FIRSTEP_SETTING_MIN; @@ -870,7 +870,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, * from INI file & cap value */ value2 = firstep_table[level] - - firstep_table[ATH9K_ANI_FIRSTEP_LVL_NEW] + + firstep_table[ATH9K_ANI_FIRSTEP_LVL] + aniState->iniDef.firstepLow; if (value2 < ATH9K_SIG_FIRSTEP_SETTING_MIN) value2 = ATH9K_SIG_FIRSTEP_SETTING_MIN; @@ -886,7 +886,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, chan->channel, aniState->firstepLevel, level, - ATH9K_ANI_FIRSTEP_LVL_NEW, + ATH9K_ANI_FIRSTEP_LVL, value, aniState->iniDef.firstep); ath_dbg(common, ANI, @@ -894,7 +894,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, chan->channel, aniState->firstepLevel, level, - ATH9K_ANI_FIRSTEP_LVL_NEW, + ATH9K_ANI_FIRSTEP_LVL, value2, aniState->iniDef.firstepLow); if (level > aniState->firstepLevel) @@ -919,7 +919,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, * from INI file & cap value */ value = cycpwrThr1_table[level] - - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL_NEW] + + cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1; if (value < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value = ATH9K_SIG_SPUR_IMM_SETTING_MIN; @@ -935,7 +935,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, * from INI file & cap value */ value2 = cycpwrThr1_table[level] - - cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL_NEW] + + cycpwrThr1_table[ATH9K_ANI_SPUR_IMMUNE_LVL] + aniState->iniDef.cycpwrThr1Ext; if (value2 < ATH9K_SIG_SPUR_IMM_SETTING_MIN) value2 = ATH9K_SIG_SPUR_IMM_SETTING_MIN; @@ -950,7 +950,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, chan->channel, aniState->spurImmunityLevel, level, - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW, + ATH9K_ANI_SPUR_IMMUNE_LVL, value, aniState->iniDef.cycpwrThr1); ath_dbg(common, ANI, @@ -958,7 +958,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, chan->channel, aniState->spurImmunityLevel, level, - ATH9K_ANI_SPUR_IMMUNE_LVL_NEW, + ATH9K_ANI_SPUR_IMMUNE_LVL, value2, aniState->iniDef.cycpwrThr1Ext); if (level > aniState->spurImmunityLevel) @@ -1111,8 +1111,8 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) AR_PHY_EXT_CYCPWR_THR1); /* these levels just got reset to defaults by the INI */ - aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; - aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; + aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; + aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; } -- cgit v1.2.3 From 198823fd002a7f9784648c24c72b7e8c9e73fd14 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:25 +0200 Subject: ath9k: remove MIB interrupt support The new ANI implementation does not need it Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 40 ---------------------------------- drivers/net/wireless/ath/ath9k/debug.c | 2 -- drivers/net/wireless/ath/ath9k/hw.h | 1 - drivers/net/wireless/ath/ath9k/main.c | 26 ++-------------------- 4 files changed, 2 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 7d5367676d4e..da73fa2a77d6 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -490,46 +490,6 @@ void ath9k_hw_disable_mib_counters(struct ath_hw *ah) } EXPORT_SYMBOL(ath9k_hw_disable_mib_counters); -/* - * Process a MIB interrupt. We may potentially be invoked because - * any of the MIB counters overflow/trigger so don't assume we're - * here because a PHY error counter triggered. - */ -void ath9k_hw_proc_mib_event(struct ath_hw *ah) -{ - u32 phyCnt1, phyCnt2; - - /* Reset these counters regardless */ - REG_WRITE(ah, AR_FILT_OFDM, 0); - REG_WRITE(ah, AR_FILT_CCK, 0); - if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) - REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); - - /* Clear the mib counters and save them in the stats */ - ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - - if (!DO_ANI(ah)) { - /* - * We must always clear the interrupt cause by - * resetting the phy error regs. - */ - REG_WRITE(ah, AR_PHY_ERR_1, 0); - REG_WRITE(ah, AR_PHY_ERR_2, 0); - return; - } - - /* NB: these are not reset-on-read */ - phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); - phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); - if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || - ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { - - /* NB: always restart to insure the h/w counters are reset */ - ath9k_ani_restart(ah); - } -} -EXPORT_SYMBOL(ath9k_hw_proc_mib_event); - void ath9k_hw_ani_setup(struct ath_hw *ah) { int i; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 2831258d9507..5c3192ffc196 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -348,8 +348,6 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) sc->debug.stats.istats.txok++; if (status & ATH9K_INT_TXURN) sc->debug.stats.istats.txurn++; - if (status & ATH9K_INT_MIB) - sc->debug.stats.istats.mib++; if (status & ATH9K_INT_RXPHY) sc->debug.stats.istats.rxphyerr++; if (status & ATH9K_INT_RXKCM) diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 111a046b44c6..b49e4b48f459 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1021,7 +1021,6 @@ void ar9003_hw_attach_ops(struct ath_hw *ah); void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning); -void ath9k_hw_proc_mib_event(struct ath_hw *ah); void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan); #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 52561b341d68..85f9ab4fa26e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -516,24 +516,6 @@ irqreturn_t ath_isr(int irq, void *dev) ath9k_hw_set_interrupts(ah); } - if (status & ATH9K_INT_MIB) { - /* - * Disable interrupts until we service the MIB - * interrupt; otherwise it will continue to - * fire. - */ - ath9k_hw_disable_interrupts(ah); - /* - * Let the hal handle the event. We assume - * it will clear whatever condition caused - * the interrupt. - */ - spin_lock(&common->cc_lock); - ath9k_hw_proc_mib_event(ah); - spin_unlock(&common->cc_lock); - ath9k_hw_enable_interrupts(ah); - } - if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) if (status & ATH9K_INT_TIM_TIMER) { if (ATH_DBG_WARN_ON_ONCE(sc->ps_idle)) @@ -959,14 +941,10 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, /* * Enable MIB interrupts when there are hardware phy counters. */ - if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) { - if (ah->config.enable_ani) - ah->imask |= ATH9K_INT_MIB; + if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0) ah->imask |= ATH9K_INT_TSFOOR; - } else { - ah->imask &= ~ATH9K_INT_MIB; + else ah->imask &= ~ATH9K_INT_TSFOOR; - } ath9k_hw_set_interrupts(ah); -- cgit v1.2.3 From 1e8f0a317b5774b2856cd8824991324efb353963 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:26 +0200 Subject: ath9k_hw: fix setting lower noise immunity values Commit af1e8a6f "ath9k: reset noiseimmunity level to default" was supposed to ensure that the default noise immunity level is above the INI values, however it prevents setting lower noise immunity values altogether. Fix this by moving the checks to the right function. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index da73fa2a77d6..6ee0875df469 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -154,9 +154,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) aniState->rssiThrLow, aniState->rssiThrHigh); if (aniState->update_ani) - aniState->ofdmNoiseImmunityLevel = - (immunityLevel > ATH9K_ANI_OFDM_DEF_LEVEL) ? - immunityLevel : ATH9K_ANI_OFDM_DEF_LEVEL; + aniState->ofdmNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; @@ -217,9 +215,7 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; if (aniState->update_ani) - aniState->cckNoiseImmunityLevel = - (immunityLevel > ATH9K_ANI_CCK_DEF_LEVEL) ? - immunityLevel : ATH9K_ANI_CCK_DEF_LEVEL; + aniState->cckNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; @@ -285,6 +281,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) struct ar5416AniState *aniState = &ah->curchan->ani; struct ath9k_channel *chan = ah->curchan; struct ath_common *common = ath9k_hw_common(ah); + int ofdm_nil, cck_nil; if (!DO_ANI(ah)) return; @@ -306,6 +303,11 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) /* always allow mode (on/off) to be controlled */ ah->ani_function |= ATH9K_ANI_MODE; + ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL, + aniState->ofdmNoiseImmunityLevel); + cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL, + aniState->cckNoiseImmunityLevel); + if (is_scanning || (ah->opmode != NL80211_IFTYPE_STATION && ah->opmode != NL80211_IFTYPE_ADHOC)) { @@ -329,8 +331,8 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->cckNoiseImmunityLevel); aniState->update_ani = false; - ath9k_hw_set_ofdm_nil(ah, ATH9K_ANI_OFDM_DEF_LEVEL); - ath9k_hw_set_cck_nil(ah, ATH9K_ANI_CCK_DEF_LEVEL); + ofdm_nil = ATH9K_ANI_OFDM_DEF_LEVEL; + cck_nil = ATH9K_ANI_CCK_DEF_LEVEL; } } else { /* @@ -346,11 +348,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->cckNoiseImmunityLevel); aniState->update_ani = true; - ath9k_hw_set_ofdm_nil(ah, - aniState->ofdmNoiseImmunityLevel); - ath9k_hw_set_cck_nil(ah, - aniState->cckNoiseImmunityLevel); } + ath9k_hw_set_ofdm_nil(ah, ofdm_nil); + ath9k_hw_set_cck_nil(ah, cck_nil); /* * enable phy counters if hw supports or if not, enable phy -- cgit v1.2.3 From 058a6385cb876d4b1b97610ee4e73a845e7bea71 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 15 Jun 2012 15:25:27 +0200 Subject: ath9k_hw: clean up ANI OFDM trigger handling Adjust ah->config.ofdm_trig_{high,low} when setting noise immunity values to simplify threshold checks in ath9k_hw_ani_monitor Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 6ee0875df469..a90aa0b4c8f1 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -179,6 +179,14 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, entry_ofdm->ofdm_weak_signal_on); + + if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) { + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI; + } else { + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; + } } static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) @@ -428,21 +436,10 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) if (aniState->listenTime > ah->aniperiod) { if (cckPhyErrRate < ah->config.cck_trig_low && - ((ofdmPhyErrRate < ah->config.ofdm_trig_low && - aniState->ofdmNoiseImmunityLevel < - ATH9K_ANI_OFDM_DEF_LEVEL) || - (ofdmPhyErrRate < ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI && - aniState->ofdmNoiseImmunityLevel >= - ATH9K_ANI_OFDM_DEF_LEVEL))) { + ofdmPhyErrRate < ah->config.ofdm_trig_low) { ath9k_hw_ani_lower_immunity(ah); aniState->ofdmsTurn = !aniState->ofdmsTurn; - } else if ((ofdmPhyErrRate > ah->config.ofdm_trig_high && - aniState->ofdmNoiseImmunityLevel >= - ATH9K_ANI_OFDM_DEF_LEVEL) || - (ofdmPhyErrRate > - ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI && - aniState->ofdmNoiseImmunityLevel < - ATH9K_ANI_OFDM_DEF_LEVEL)) { + } else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) { ath9k_hw_ani_ofdm_err_trigger(ah); aniState->ofdmsTurn = false; } else if (cckPhyErrRate > ah->config.cck_trig_high) { -- cgit v1.2.3 From 29a6b508569cc1d04884d3140ccb5973dd7d91fa Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Fri, 15 Jun 2012 17:27:05 +0200 Subject: wireless: rtl818x: rtl8180 add devices ids from windows driver: 0x1186, 0x3301 D-Link Air DWL-510 Wireless PCI Adapter 0x1432, 0x7106 LevelOne WPC-0101 11Mbps Wireless PCMCIA CardBus Adapter Signed-off-by: Xose Vazquez Perez Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8180/dev.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 2bebcb71a1e9..3b505395d869 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -47,6 +47,8 @@ static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { { PCI_DEVICE(0x1799, 0x6001) }, { PCI_DEVICE(0x1799, 0x6020) }, { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x3300) }, + { PCI_DEVICE(0x1186, 0x3301) }, + { PCI_DEVICE(0x1432, 0x7106) }, { } }; -- cgit v1.2.3 From 0abd79e5a87319f0710c8db2ef2a2c5c6479c32b Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 15 Jun 2012 12:21:51 -0700 Subject: mwifiex: set channel via start_ap handler for AP interface This patch adds functionality to set channel info received from cfg80211_ap_settings in start_ap handler. Since set_channel cfg80211 handler has been removed and we need not explicitely call mwifiex_uap_set_channel(); hence this function definition is also removed. Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 23 +++++++++++++++++------ drivers/net/wireless/mwifiex/main.h | 1 - drivers/net/wireless/mwifiex/uap_cmd.c | 30 ------------------------------ 3 files changed, 17 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 015fec3371a0..7b4eaf41bfc0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -384,13 +384,13 @@ mwifiex_set_rf_channel(struct mwifiex_private *priv, cfp.freq = chan->center_freq; cfp.channel = ieee80211_frequency_to_channel(chan->center_freq); - if (mwifiex_bss_set_channel(priv, &cfp)) - return -EFAULT; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { + if (mwifiex_bss_set_channel(priv, &cfp)) + return -EFAULT; return mwifiex_drv_change_adhoc_chan(priv, cfp.channel); - else - return mwifiex_uap_set_channel(priv, cfp.channel); + } + + return 0; } /* @@ -961,6 +961,17 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } + bss_cfg->channel = + (u8)ieee80211_frequency_to_channel(params->channel->center_freq); + bss_cfg->band_cfg = BAND_CONFIG_MANUAL; + + if (mwifiex_set_rf_channel(priv, params->channel, + params->channel_type)) { + kfree(bss_cfg); + wiphy_err(wiphy, "Failed to set band config information!\n"); + return -1; + } + if (mwifiex_set_secure_params(priv, bss_cfg, params)) { kfree(bss_cfg); wiphy_err(wiphy, "Failed to parse secuirty parameters!\n"); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 5b32221077c4..512481ea664e 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -990,7 +990,6 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv, int mwifiex_main_process(struct mwifiex_adapter *); -int mwifiex_uap_set_channel(struct mwifiex_private *priv, int channel); int mwifiex_bss_set_channel(struct mwifiex_private *, struct mwifiex_chan_freq_power *cfp); int mwifiex_get_bss_info(struct mwifiex_private *, diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 89f9a2a45de3..fb0c6cbfc277 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -421,33 +421,3 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, return 0; } - -/* This function sets the RF channel for AP. - * - * This function populates channel information in AP config structure - * and sends command to configure channel information in AP. - */ -int mwifiex_uap_set_channel(struct mwifiex_private *priv, int channel) -{ - struct mwifiex_uap_bss_param *bss_cfg; - struct wiphy *wiphy = priv->wdev->wiphy; - - bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); - if (!bss_cfg) - return -ENOMEM; - - mwifiex_set_sys_config_invalid_data(bss_cfg); - bss_cfg->band_cfg = BAND_CONFIG_MANUAL; - bss_cfg->channel = channel; - - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg)) { - wiphy_err(wiphy, "Failed to set the uAP channel\n"); - kfree(bss_cfg); - return -1; - } - - kfree(bss_cfg); - return 0; -} -- cgit v1.2.3 From 2228125600b3031657b825502d1a1999e4c02566 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 15 Jun 2012 12:21:53 -0700 Subject: mwifiex: set HT capability based on cfg80211_ap_settings Parse HT IE from cfg80211 and set HT capabilities accordingly to FW. If HT IE is missing, 11n would be disabled in FW. Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 ++ drivers/net/wireless/mwifiex/fw.h | 6 +++++ drivers/net/wireless/mwifiex/ioctl.h | 1 + drivers/net/wireless/mwifiex/main.h | 3 +++ drivers/net/wireless/mwifiex/uap_cmd.c | 47 +++++++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 7b4eaf41bfc0..5af3f97936e4 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -978,6 +978,8 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, return -1; } + mwifiex_set_ht_params(priv, bss_cfg, params); + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL)) { wiphy_err(wiphy, "Failed to stop the BSS\n"); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 561452a5c818..9686f6fec19a 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -162,6 +162,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) +#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ + IEEE80211_HT_CAP_SM_PS) + +#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR + /* dev_cap bitmap * BIT * 0-16 reserved diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index e6be6ee75951..8c8e47c792a0 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -90,6 +90,7 @@ struct mwifiex_uap_bss_param { u16 key_mgmt; u16 key_mgmt_operation; struct wpa_param wpa_cfg; + struct ieee80211_ht_cap ht_cap; }; enum { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 512481ea664e..0b3b5aa9830d 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -840,6 +840,9 @@ void mwifiex_init_priv_params(struct mwifiex_private *priv, int mwifiex_set_secure_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_config, struct cfg80211_ap_settings *params); +void mwifiex_set_ht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); /* * This function checks if the queuing is RA based or not. diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index fb0c6cbfc277..59e5ba4df02b 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -118,6 +118,33 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, return 0; } +/* This function updates 11n related parameters from IE and sets them into + * bss_config structure. + */ +void +mwifiex_set_ht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *ht_ie; + + if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) + return; + + ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (ht_ie) { + memcpy(&bss_cfg->ht_cap, ht_ie + 2, + sizeof(struct ieee80211_ht_cap)); + } else { + memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); + bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); + bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; + } + + return; +} + /* This function initializes some of mwifiex_uap_bss_param variables. * This helps FW in ignoring invalid values. These values may or may not * be get updated to valid ones at later stage. @@ -154,6 +181,7 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) struct host_cmd_tlv_auth_type *auth_type; struct host_cmd_tlv_passphrase *passphrase; struct host_cmd_tlv_akmp *tlv_akmp; + struct mwifiex_ie_types_htcap *htcap; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; u16 cmd_size = *param_size; @@ -330,6 +358,25 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); } + if (bss_cfg->ht_cap.cap_info) { + htcap = (struct mwifiex_ie_types_htcap *)tlv; + htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + htcap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; + htcap->ht_cap.ampdu_params_info = + bss_cfg->ht_cap.ampdu_params_info; + memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, + sizeof(struct ieee80211_mcs_info)); + htcap->ht_cap.extended_ht_cap_info = + bss_cfg->ht_cap.extended_ht_cap_info; + htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; + htcap->ht_cap.antenna_selection_info = + bss_cfg->ht_cap.antenna_selection_info; + cmd_size += sizeof(struct mwifiex_ie_types_htcap); + tlv += sizeof(struct mwifiex_ie_types_htcap); + } + *param_size = cmd_size; return 0; -- cgit v1.2.3 From 5d66cb62954106d4abfb67bbb5a14cec57903cbb Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 15 Jun 2012 12:21:54 -0700 Subject: mwifiex: separate uAP WPA/WPA2 parsing from other BSS parameters To enhance readability, create a separate function for parsing WPA/WPA2 related parameters from cfg80211_ap_settings. There is no functional change in this patch. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/uap_cmd.c | 147 ++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 68 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 59e5ba4df02b..86a4432df7a8 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -161,6 +161,82 @@ void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) config->retry_limit = 0x7F; } +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WPA/WPA2 security. + * These TLVs are appended to command buffer. + */ +static void +mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_pwk_cipher *pwk_cipher; + struct host_cmd_tlv_gwk_cipher *gwk_cipher; + struct host_cmd_tlv_passphrase *passphrase; + struct host_cmd_tlv_akmp *tlv_akmp; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + u16 cmd_size = *param_size; + u8 *tlv = *tlv_buf; + + tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; + tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - + sizeof(struct host_cmd_tlv)); + tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); + tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); + cmd_size += sizeof(struct host_cmd_tlv_akmp); + tlv += sizeof(struct host_cmd_tlv_akmp); + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->tlv.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct host_cmd_tlv)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->tlv.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct host_cmd_tlv)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { + gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; + gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); + gwk_cipher->tlv.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - + sizeof(struct host_cmd_tlv)); + gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; + cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); + tlv += sizeof(struct host_cmd_tlv_gwk_cipher); + } + + if (bss_cfg->wpa_cfg.length) { + passphrase = (struct host_cmd_tlv_passphrase *)tlv; + passphrase->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); + passphrase->tlv.len = cpu_to_le16(bss_cfg->wpa_cfg.length); + memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, + bss_cfg->wpa_cfg.length); + cmd_size += sizeof(struct host_cmd_tlv) + + bss_cfg->wpa_cfg.length; + tlv += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length; + } + + *param_size = cmd_size; + *tlv_buf = tlv; + + return; +} + /* This function parses BSS related parameters from structure * and prepares TLVs. These TLVs are appended to command buffer. */ @@ -175,12 +251,8 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) struct host_cmd_tlv_frag_threshold *frag_threshold; struct host_cmd_tlv_rts_threshold *rts_threshold; struct host_cmd_tlv_retry_limit *retry_limit; - struct host_cmd_tlv_pwk_cipher *pwk_cipher; - struct host_cmd_tlv_gwk_cipher *gwk_cipher; struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; struct host_cmd_tlv_auth_type *auth_type; - struct host_cmd_tlv_passphrase *passphrase; - struct host_cmd_tlv_akmp *tlv_akmp; struct mwifiex_ie_types_htcap *htcap; struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; u16 cmd_size = *param_size; @@ -271,70 +343,9 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) } if ((bss_cfg->protocol & PROTOCOL_WPA) || (bss_cfg->protocol & PROTOCOL_WPA2) || - (bss_cfg->protocol & PROTOCOL_EAP)) { - tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; - tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); - tlv_akmp->tlv.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - - sizeof(struct host_cmd_tlv)); - tlv_akmp->key_mgmt_operation = - cpu_to_le16(bss_cfg->key_mgmt_operation); - tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); - cmd_size += sizeof(struct host_cmd_tlv_akmp); - tlv += sizeof(struct host_cmd_tlv_akmp); - - if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & - VALID_CIPHER_BITMAP) { - pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->tlv.type = - cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->tlv.len = cpu_to_le16( - sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct host_cmd_tlv)); - pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); - pwk_cipher->cipher = - bss_cfg->wpa_cfg.pairwise_cipher_wpa; - cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); - tlv += sizeof(struct host_cmd_tlv_pwk_cipher); - } - if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & - VALID_CIPHER_BITMAP) { - pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->tlv.len = cpu_to_le16( - sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct host_cmd_tlv)); - pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); - pwk_cipher->cipher = - bss_cfg->wpa_cfg.pairwise_cipher_wpa2; - cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); - tlv += sizeof(struct host_cmd_tlv_pwk_cipher); - } - if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { - gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; - gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); - gwk_cipher->tlv.len = cpu_to_le16( - sizeof(struct host_cmd_tlv_gwk_cipher) - - sizeof(struct host_cmd_tlv)); - gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; - cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); - tlv += sizeof(struct host_cmd_tlv_gwk_cipher); - } - if (bss_cfg->wpa_cfg.length) { - passphrase = (struct host_cmd_tlv_passphrase *)tlv; - passphrase->tlv.type = - cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); - passphrase->tlv.len = - cpu_to_le16(bss_cfg->wpa_cfg.length); - memcpy(passphrase->passphrase, - bss_cfg->wpa_cfg.passphrase, - bss_cfg->wpa_cfg.length); - cmd_size += sizeof(struct host_cmd_tlv) + - bss_cfg->wpa_cfg.length; - tlv += sizeof(struct host_cmd_tlv) + - bss_cfg->wpa_cfg.length; - } - } + (bss_cfg->protocol & PROTOCOL_EAP)) + mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); + if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { auth_type = (struct host_cmd_tlv_auth_type *)tlv; -- cgit v1.2.3 From 96893538564c43276dffe5e294b56935197cf1c8 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 15 Jun 2012 12:21:55 -0700 Subject: mwifiex: support for WEP in AP mode This patch adds support for WEP open/shared encryption in AP mode. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 30 +++++++++++++++- drivers/net/wireless/mwifiex/fw.h | 8 +++++ drivers/net/wireless/mwifiex/ioctl.h | 9 +++++ drivers/net/wireless/mwifiex/uap_cmd.c | 62 +++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 5af3f97936e4..9c2e08e4b093 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -170,7 +170,9 @@ mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, if (!priv->sec_info.wep_enabled) return 0; - if (mwifiex_set_encode(priv, NULL, 0, key_index, NULL, 0)) { + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { + priv->wep_key_curr_index = key_index; + } else if (mwifiex_set_encode(priv, NULL, 0, key_index, NULL, 0)) { wiphy_err(wiphy, "set default Tx key index\n"); return -EFAULT; } @@ -187,9 +189,25 @@ mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, struct key_params *params) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + struct mwifiex_wep_key *wep_key; const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && + (params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104)) { + if (params->key && params->key_len) { + wep_key = &priv->wep_key[key_index]; + memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); + memcpy(wep_key->key_material, params->key, + params->key_len); + wep_key->key_index = key_index; + wep_key->key_length = params->key_len; + priv->sec_info.wep_enabled = 1; + } + return 0; + } + if (mwifiex_set_encode(priv, params->key, params->key_len, key_index, peer_mac, 0)) { wiphy_err(wiphy, "crypto keys added\n"); @@ -1003,6 +1021,16 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, return -1; } + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter)) + return -1; + return 0; } diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 9686f6fec19a..1184141839ae 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -124,6 +124,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) #define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) #define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) +#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) #define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) #define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) #define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) @@ -1201,6 +1202,13 @@ struct host_cmd_tlv_passphrase { u8 passphrase[0]; } __packed; +struct host_cmd_tlv_wep_key { + struct host_cmd_tlv tlv; + u8 key_index; + u8 is_default; + u8 key[1]; +}; + struct host_cmd_tlv_auth_type { struct host_cmd_tlv tlv; u8 auth_type; diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 8c8e47c792a0..9f088fb88cb7 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -21,6 +21,7 @@ #define _MWIFIEX_IOCTL_H_ #include +#include enum { MWIFIEX_SCAN_TYPE_UNCHANGED = 0, @@ -71,6 +72,13 @@ struct wpa_param { u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN]; }; +struct wep_key { + u8 key_index; + u8 is_default; + u16 length; + u8 key[WLAN_KEY_LEN_WEP104]; +}; + #define KEY_MGMT_ON_HOST 0x03 #define MWIFIEX_AUTH_MODE_AUTO 0xFF #define BAND_CONFIG_MANUAL 0x00 @@ -90,6 +98,7 @@ struct mwifiex_uap_bss_param { u16 key_mgmt; u16 key_mgmt_operation; struct wpa_param wpa_cfg; + struct wep_key wep_cfg[NUM_WEP_KEYS]; struct ieee80211_ht_cap ht_cap; }; diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 86a4432df7a8..abb1322a8ceb 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -26,6 +26,7 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_config, struct cfg80211_ap_settings *params) { int i; + struct mwifiex_wep_key wep_key; if (!params->privacy) { bss_config->protocol = PROTOCOL_NO_SECURITY; @@ -104,6 +105,27 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, switch (params->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: + if (priv->sec_info.wep_enabled) { + bss_config->protocol = PROTOCOL_STATIC_WEP; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + wep_key = priv->wep_key[i]; + bss_config->wep_cfg[i].key_index = i; + + if (priv->wep_key_curr_index == i) + bss_config->wep_cfg[i].is_default = 1; + else + bss_config->wep_cfg[i].is_default = 0; + + bss_config->wep_cfg[i].length = + wep_key.key_length; + memcpy(&bss_config->wep_cfg[i].key, + &wep_key.key_material, + wep_key.key_length); + } + } break; case WLAN_CIPHER_SUITE_TKIP: bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; @@ -237,6 +259,44 @@ mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) return; } +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WEP encryption. + * These TLVs are appended to command buffer. + */ +static void +mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_wep_key *wep_key; + u16 cmd_size = *param_size; + int i; + u8 *tlv = *tlv_buf; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss_cfg->wep_cfg[i].length && + (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || + bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { + wep_key = (struct host_cmd_tlv_wep_key *)tlv; + wep_key->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + wep_key->tlv.len = + cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); + wep_key->key_index = bss_cfg->wep_cfg[i].key_index; + wep_key->is_default = bss_cfg->wep_cfg[i].is_default; + memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, + bss_cfg->wep_cfg[i].length); + cmd_size += sizeof(struct host_cmd_tlv) + 2 + + bss_cfg->wep_cfg[i].length; + tlv += sizeof(struct host_cmd_tlv) + 2 + + bss_cfg->wep_cfg[i].length; + } + } + + *param_size = cmd_size; + *tlv_buf = tlv; + + return; +} + /* This function parses BSS related parameters from structure * and prepares TLVs. These TLVs are appended to command buffer. */ @@ -345,6 +405,8 @@ mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) (bss_cfg->protocol & PROTOCOL_WPA2) || (bss_cfg->protocol & PROTOCOL_EAP)) mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); + else + mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size); if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { -- cgit v1.2.3 From 0bd8b79fe928b88ebaf36f2621f921a2fc09ea9c Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:48 -0500 Subject: brcm80211: smac: don't set up tx power limits during initialization This code is unnecessary, and in fact it's never executed because the interface is never up when brcms_c_channels_commit() is called. Removing it helps simplify the implementation of proper regulatory support. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Brett Rudley Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index eb77ac3cfb6b..049d78fce103 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -908,7 +908,6 @@ static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm) { struct brcms_c_info *wlc = wlc_cm->wlc; uint chan; - struct txpwr_limits txpwr; /* search for the existence of any valid channel */ for (chan = 0; chan < MAXCHANNEL; chan++) { @@ -949,13 +948,6 @@ static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm) wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, brcms_c_japan(wlc) ? true : false); - - if (wlc->pub->up && chan != INVCHANNEL) { - brcms_c_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr); - brcms_c_channel_min_txpower_limits_with_local_constraint(wlc_cm, - &txpwr, BRCMS_TXPWR_MAX); - wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec); - } } static int -- cgit v1.2.3 From 91691295527d3fd63f12079222df0fd8048acccb Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:49 -0500 Subject: brcm80211: smac: always set channel specified by mac80211 In some situations brcmsmac is choosing a channel internally. This makes it difficult at times to know what channel to use for enforcing regulatory constraints, so instead always use the channel from the mac80211 configuration. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/main.c | 30 ++++++-------------------- 1 file changed, 6 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 19db4052c44c..93c865db7729 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -3139,20 +3139,6 @@ void brcms_c_reset(struct brcms_c_info *wlc) brcms_b_reset(wlc->hw); } -/* Return the channel the driver should initialize during brcms_c_init. - * the channel may have to be changed from the currently configured channel - * if other configurations are in conflict (bandlocked, 11n mode disabled, - * invalid channel for current country, etc.) - */ -static u16 brcms_c_init_chanspec(struct brcms_c_info *wlc) -{ - u16 chanspec = - 1 | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE | - WL_CHANSPEC_BAND_2G; - - return chanspec; -} - void brcms_c_init_scb(struct scb *scb) { int i; @@ -5129,6 +5115,8 @@ static void brcms_c_wme_retries_write(struct brcms_c_info *wlc) /* make interface operational */ int brcms_c_up(struct brcms_c_info *wlc) { + struct ieee80211_channel *ch; + BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* HW is turned off so don't try to access it */ @@ -5195,8 +5183,9 @@ int brcms_c_up(struct brcms_c_info *wlc) wlc->pub->up = true; if (wlc->bandinit_pending) { + ch = wlc->pub->ieee_hw->conf.channel; brcms_c_suspend_mac_and_wait(wlc); - brcms_c_set_chanspec(wlc, wlc->default_bss->chanspec); + brcms_c_set_chanspec(wlc, ch20mhz_chspec(ch->hw_value)); wlc->bandinit_pending = false; brcms_c_enable_mac(wlc); } @@ -8201,19 +8190,12 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) { struct bcma_device *core = wlc->hw->d11core; + struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; u16 chanspec; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); - /* - * This will happen if a big-hammer was executed. In - * that case, we want to go back to the channel that - * we were on and not new channel - */ - if (wlc->pub->associated) - chanspec = wlc->home_chanspec; - else - chanspec = brcms_c_init_chanspec(wlc); + chanspec = ch20mhz_chspec(ch->hw_value); brcms_b_init(wlc->hw, chanspec); -- cgit v1.2.3 From a3ce5cc1a60fd2e237599d4b9bc247422a7523c2 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:50 -0500 Subject: brcm80211: smac: remove unused code for 40MHz channels This code has been kept around in anticipation of adding support for 40MHz channels, but subsequent patches to better integrate with mac80211 regulatory support will render it completely broken. Therefore we should go ahead and remove it. Keep these changes separate from other cleanup patches in order to make it easier to resurrect 40MHz channel support at some point in the future. Reviewed-by: Arend Van Spriel Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 116 ---------------------- 1 file changed, 116 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 049d78fce103..089cc873109a 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -512,83 +512,6 @@ static const struct { "X2", LOCALES(i, 11, bn, 11n)}, /* Worldwide RoW 2 */ }; -#ifdef SUPPORT_40MHZ -/* 20MHz channel info for 40MHz pairing support */ -struct chan20_info { - u8 sb; - u8 adj_sbs; -}; - -/* indicates adjacent channels that are allowed for a 40 Mhz channel and - * those that permitted by the HT - */ -struct chan20_info chan20_info[] = { - /* 11b/11g */ -/* 0 */ {1, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 1 */ {2, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 2 */ {3, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 3 */ {4, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 4 */ {5, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, -/* 5 */ {6, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, -/* 6 */ {7, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, -/* 7 */ {8, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, -/* 8 */ {9, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)}, -/* 9 */ {10, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 10 */ {11, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 11 */ {12, (CH_LOWER_SB)}, -/* 12 */ {13, (CH_LOWER_SB)}, -/* 13 */ {14, (CH_LOWER_SB)}, - -/* 11a japan high */ -/* 14 */ {34, (CH_UPPER_SB)}, -/* 15 */ {38, (CH_LOWER_SB)}, -/* 16 */ {42, (CH_LOWER_SB)}, -/* 17 */ {46, (CH_LOWER_SB)}, - -/* 11a usa low */ -/* 18 */ {36, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 19 */ {40, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 20 */ {44, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 21 */ {48, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 22 */ {52, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 23 */ {56, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 24 */ {60, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 25 */ {64, (CH_LOWER_SB | CH_EWA_VALID)}, - -/* 11a Europe */ -/* 26 */ {100, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 27 */ {104, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 28 */ {108, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 29 */ {112, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 30 */ {116, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 31 */ {120, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 32 */ {124, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 33 */ {128, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 34 */ {132, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 35 */ {136, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 36 */ {140, (CH_LOWER_SB)}, - -/* 11a usa high, ref5 only */ -/* The 0x80 bit in pdiv means these are REF5, other entries are REF20 */ -/* 37 */ {149, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 38 */ {153, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 39 */ {157, (CH_UPPER_SB | CH_EWA_VALID)}, -/* 40 */ {161, (CH_LOWER_SB | CH_EWA_VALID)}, -/* 41 */ {165, (CH_LOWER_SB)}, - -/* 11a japan */ -/* 42 */ {184, (CH_UPPER_SB)}, -/* 43 */ {188, (CH_LOWER_SB)}, -/* 44 */ {192, (CH_UPPER_SB)}, -/* 45 */ {196, (CH_LOWER_SB)}, -/* 46 */ {200, (CH_UPPER_SB)}, -/* 47 */ {204, (CH_LOWER_SB)}, -/* 48 */ {208, (CH_UPPER_SB)}, -/* 49 */ {212, (CH_LOWER_SB)}, -/* 50 */ {216, (CH_LOWER_SB)} -}; -#endif /* SUPPORT_40MHZ */ - static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx) { if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) @@ -1449,45 +1372,6 @@ brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec, return brcms_c_valid_channel20(wlc_cm->wlc->cmi, channel); } -#ifdef SUPPORT_40MHZ - /* - * We know we are now checking a 40MHZ channel, so we should - * only be here for NPHYS - */ - if (BRCMS_ISNPHY(wlc->band) || BRCMS_ISSSLPNPHY(wlc->band)) { - u8 upper_sideband = 0, idx; - u8 num_ch20_entries = - sizeof(chan20_info) / sizeof(struct chan20_info); - - if (!VALID_40CHANSPEC_IN_BAND(wlc, chspec_bandunit(chspec))) - return false; - - if (dualband) { - if (!brcms_c_valid_channel20_db(wlc->cmi, - lower_20_sb(channel)) || - !brcms_c_valid_channel20_db(wlc->cmi, - upper_20_sb(channel))) - return false; - } else { - if (!brcms_c_valid_channel20(wlc->cmi, - lower_20_sb(channel)) || - !brcms_c_valid_channel20(wlc->cmi, - upper_20_sb(channel))) - return false; - } - - /* find the lower sideband info in the sideband array */ - for (idx = 0; idx < num_ch20_entries; idx++) { - if (chan20_info[idx].sb == lower_20_sb(channel)) - upper_sideband = chan20_info[idx].adj_sbs; - } - /* check that the lower sideband allows an upper sideband */ - if ((upper_sideband & (CH_UPPER_SB | CH_EWA_VALID)) == - (CH_UPPER_SB | CH_EWA_VALID)) - return true; - return false; - } -#endif /* 40 MHZ */ return false; } -- cgit v1.2.3 From 2810a619ca5716e315090119e5695e97814b1e60 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:51 -0500 Subject: brcm80211: smac: clean up channel.c Much of the code is either unsed or never put to any useful purpose. Remove this code in advance of reworking the driver's regulatory support. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Brett Rudley Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 138 +--------------------- 1 file changed, 5 insertions(+), 133 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 089cc873109a..c311ef7e99a7 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -58,18 +58,8 @@ LOCALE_CHAN_12_13 | \ LOCALE_CHAN_14) -#define LOCALE_RADAR_SET_NONE 0 -#define LOCALE_RADAR_SET_1 1 - #define LOCALE_RESTRICTED_NONE 0 #define LOCALE_RESTRICTED_SET_2G_SHORT 1 -#define LOCALE_RESTRICTED_CHAN_165 2 -#define LOCALE_CHAN_ALL_5G 3 -#define LOCALE_RESTRICTED_JAPAN_LEGACY 4 -#define LOCALE_RESTRICTED_11D_2G 5 -#define LOCALE_RESTRICTED_11D_5G 6 -#define LOCALE_RESTRICTED_LOW_HI 7 -#define LOCALE_RESTRICTED_12_13_14 8 #define LOCALE_2G_IDX_i 0 #define LOCALE_5G_IDX_11 0 @@ -118,8 +108,6 @@ (((c) < 100) ? 2 : \ (((c) < 149) ? 3 : 4)))) -#define ISDFS_EU(fl) (((fl) & BRCMS_DFS_EU) == BRCMS_DFS_EU) - struct brcms_cm_band { /* struct locale_info flags */ u8 locale_flags; @@ -127,9 +115,6 @@ struct brcms_cm_band { struct brcms_chanvec valid_channels; /* List of restricted use channels */ const struct brcms_chanvec *restricted_channels; - /* List of radar sensitive channels */ - const struct brcms_chanvec *radar_channels; - u8 PAD[8]; }; /* locale per-channel tx power limits for MIMO frames @@ -158,7 +143,6 @@ struct brcms_cm_info { char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */ uint srom_regrev; /* Regulatory Rev for the SROM ccode */ const struct country_info *country; /* current country def */ - char ccode[BRCM_CNTRY_BUF_SZ]; /* current internal Country Code */ uint regrev; /* current Regulatory Revision */ char country_abbrev[BRCM_CNTRY_BUF_SZ]; /* current advertised ccode */ /* per-band state (one per phy/radio) */ @@ -171,14 +155,10 @@ struct brcms_cm_info { /* locale channel and power info. */ struct locale_info { u32 valid_channels; - /* List of radar sensitive channels */ - u8 radar_channels; /* List of channels used only if APs are detected */ u8 restricted_channels; /* Max tx pwr in qdBm for each sub-band */ s8 maxpwr[BRCMS_MAXPWR_TBL_SIZE]; - /* Country IE advertised max tx pwr in dBm per sub-band */ - s8 pub_maxpwr[BAND_5G_PWR_LVLS]; u8 flags; }; @@ -196,46 +176,10 @@ static const struct brcms_chanvec chanvec_none = { 0x00, 0x00, 0x00, 0x00} }; -/* All 2.4 GHz HW channels */ -static const struct brcms_chanvec chanvec_all_2G = { - {0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -/* All 5 GHz HW channels */ -static const struct brcms_chanvec chanvec_all_5G = { - {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x11, 0x11, - 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x20, 0x22, 0x22, 0x00, 0x00, 0x11, - 0x11, 0x11, 0x11, 0x01} -}; - -/* - * Radar channel sets - */ - -/* Channels 52 - 64, 100 - 140 */ -static const struct brcms_chanvec radar_set1 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, /* 52 - 60 */ - 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, /* 64, 100 - 124 */ - 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - 140 */ - 0x00, 0x00, 0x00, 0x00} -}; - /* * Restricted channel sets */ -/* Channels 34, 38, 42, 46 */ -static const struct brcms_chanvec restricted_set_japan_legacy = { - {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - /* Channels 12, 13 */ static const struct brcms_chanvec restricted_set_2g_short = { {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -244,47 +188,11 @@ static const struct brcms_chanvec restricted_set_2g_short = { 0x00, 0x00, 0x00, 0x00} }; -/* Channel 165 */ -static const struct brcms_chanvec restricted_chan_165 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -/* Channels 36 - 48 & 149 - 165 */ -static const struct brcms_chanvec restricted_low_hi = { - {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x22, 0x22, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -/* Channels 12 - 14 */ -static const struct brcms_chanvec restricted_set_12_13_14 = { - {0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - /* global memory to provide working buffer for expanded locale */ -static const struct brcms_chanvec *g_table_radar_set[] = { - &chanvec_none, - &radar_set1 -}; - static const struct brcms_chanvec *g_table_restricted_chan[] = { &chanvec_none, /* restricted_set_none */ &restricted_set_2g_short, - &restricted_chan_165, - &chanvec_all_5G, - &restricted_set_japan_legacy, - &chanvec_all_2G, /* restricted_set_11d_2G */ - &chanvec_all_5G, /* restricted_set_11d_5G */ - &restricted_low_hi, - &restricted_set_12_13_14 }; static const struct brcms_chanvec locale_2g_01_11 = { @@ -445,11 +353,9 @@ static void brcms_c_locale_get_channels(const struct locale_info *locale, */ static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13, - LOCALE_RADAR_SET_NONE, LOCALE_RESTRICTED_SET_2G_SHORT, {QDB(19), QDB(19), QDB(19), QDB(19), QDB(19), QDB(19)}, - {20, 20, 20, 0}, BRCMS_EIRP }; @@ -459,10 +365,8 @@ static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ static const struct locale_info locale_11 = { /* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */ LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165, - LOCALE_RADAR_SET_1, LOCALE_RESTRICTED_NONE, {QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)}, - {23, 23, 23, 30, 30}, BRCMS_EIRP | BRCMS_DFS_EU }; @@ -544,13 +448,6 @@ static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx) return g_mimo_5g_table[locale_idx]; } -static int -brcms_c_country_aggregate_map(struct brcms_cm_info *wlc_cm, const char *ccode, - char *mapped_ccode, uint *mapped_regrev) -{ - return false; -} - /* * Indicates whether the country provided is valid to pass * to cfg80211 or not. @@ -620,7 +517,6 @@ brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm, const char *ccode, const struct country_info *country; uint srom_regrev = wlc_cm->srom_regrev; const char *srom_ccode = wlc_cm->srom_ccode; - int mapped; /* check for currently supported ccode size */ if (strlen(ccode) > (BRCM_CNTRY_BUF_SZ - 1)) { @@ -639,12 +535,7 @@ brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm, const char *ccode, */ if (!strcmp(srom_ccode, ccode)) { *mapped_regrev = srom_regrev; - mapped = 0; wiphy_err(wlc->wiphy, "srom_code == ccode %s\n", __func__); - } else { - mapped = - brcms_c_country_aggregate_map(wlc_cm, ccode, mapped_ccode, - mapped_regrev); } /* find the matching built-in country definition */ @@ -902,8 +793,6 @@ brcms_c_channels_init(struct brcms_cm_info *wlc_cm, wlc_cm->bandstate[band->bandunit].restricted_channels = g_table_restricted_chan[li->restricted_channels]; - wlc_cm->bandstate[band->bandunit].radar_channels = - g_table_radar_set[li->radar_channels]; /* * set the channel availability, masking out the channels @@ -938,17 +827,11 @@ brcms_c_set_country_common(struct brcms_cm_info *wlc_cm, { const struct locale_info *locale; struct brcms_c_info *wlc = wlc_cm->wlc; - char prev_country_abbrev[BRCM_CNTRY_BUF_SZ]; /* save current country state */ wlc_cm->country = country; - memset(&prev_country_abbrev, 0, BRCM_CNTRY_BUF_SZ); - strncpy(prev_country_abbrev, wlc_cm->country_abbrev, - BRCM_CNTRY_BUF_SZ - 1); - strncpy(wlc_cm->country_abbrev, country_abbrev, BRCM_CNTRY_BUF_SZ - 1); - strncpy(wlc_cm->ccode, ccode, BRCM_CNTRY_BUF_SZ - 1); wlc_cm->regrev = regrev; if ((wlc->pub->_n_enab & SUPPORT_11N) != @@ -1014,16 +897,12 @@ brcms_c_set_countrycode_rev(struct brcms_cm_info *wlc_cm, static int brcms_c_set_countrycode(struct brcms_cm_info *wlc_cm, const char *ccode) { - char country_abbrev[BRCM_CNTRY_BUF_SZ]; - strncpy(country_abbrev, ccode, BRCM_CNTRY_BUF_SZ); - return brcms_c_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1); + return brcms_c_set_countrycode_rev(wlc_cm, ccode, ccode, -1); } struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) { struct brcms_cm_info *wlc_cm; - char country_abbrev[BRCM_CNTRY_BUF_SZ]; - const struct country_info *country; struct brcms_pub *pub = wlc->pub; struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; @@ -1040,21 +919,14 @@ struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) if (sprom->alpha2 && brcms_c_country_valid(sprom->alpha2)) strncpy(wlc->pub->srom_ccode, sprom->alpha2, sizeof(sprom->alpha2)); - /* - * internal country information which must match - * regulatory constraints in firmware - */ - memset(country_abbrev, 0, BRCM_CNTRY_BUF_SZ); - strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1); - country = brcms_c_country_lookup(wlc, country_abbrev); - /* save default country for exiting 11d regulatory mode */ - strncpy(wlc->country_default, country_abbrev, BRCM_CNTRY_BUF_SZ - 1); + strncpy(wlc->country_default, "X2", BRCM_CNTRY_BUF_SZ - 1); /* initialize autocountry_default to driver default */ - strncpy(wlc->autocountry_default, "X2", BRCM_CNTRY_BUF_SZ - 1); + strncpy(wlc->autocountry_default, wlc->country_default, + BRCM_CNTRY_BUF_SZ - 1); - brcms_c_set_countrycode(wlc_cm, country_abbrev); + brcms_c_set_countrycode(wlc_cm, wlc->country_default); return wlc_cm; } -- cgit v1.2.3 From cf03c5dac83609f09d9f0e9fa3c09d86daed614d Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:52 -0500 Subject: brcm80211: smac: inform mac80211 of the X2 regulatory domain brcmsmac implements enforcement of regulatory constraints internally, using a Broadcom-specific world roaming domain named X2. Besides being duplication of functionality this can also conflict with mac80211's regulatory implementation, as mac80211 is unaware of the X2 domain and thus might apply a more restrictive domain. This patch is the first step in making brcmsmac cooperate with mac80211's regulatory support. X2 is registered as a custom domain with mac80211, so that at least both implementations will be enforcing the same set of constraints. The internal enforcement of rules is kept for now; this will be converted over to relying on mac80211 regulatory enforcement in later patches. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 385 +++++++++++---------- drivers/net/wireless/brcm80211/brcmsmac/channel.h | 1 + .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 2 + 3 files changed, 210 insertions(+), 178 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index c311ef7e99a7..65ef60bf9c8f 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -15,7 +15,9 @@ */ #include +#include #include +#include #include #include "pub.h" @@ -23,6 +25,7 @@ #include "main.h" #include "stf.h" #include "channel.h" +#include "mac80211_if.h" /* QDB() macro takes a dB value and converts to a quarter dB value */ #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) @@ -108,6 +111,39 @@ (((c) < 100) ? 2 : \ (((c) < 149) ? 3 : 4)))) +#define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) +#define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ + NL80211_RRF_PASSIVE_SCAN | \ + NL80211_RRF_NO_IBSS) + +#define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ + NL80211_RRF_PASSIVE_SCAN | \ + NL80211_RRF_NO_IBSS) +#define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ + NL80211_RRF_PASSIVE_SCAN | \ + NL80211_RRF_DFS | \ + NL80211_RRF_NO_IBSS) +#define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ + NL80211_RRF_PASSIVE_SCAN | \ + NL80211_RRF_DFS | \ + NL80211_RRF_NO_IBSS) +#define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ + NL80211_RRF_PASSIVE_SCAN | \ + NL80211_RRF_NO_IBSS) + +static const struct ieee80211_regdomain brcms_regdom_x2 = { + .n_reg_rules = 7, + .alpha2 = "X2", + .reg_rules = { + BRCM_2GHZ_2412_2462, + BRCM_2GHZ_2467_2472, + BRCM_5GHZ_5180_5240, + BRCM_5GHZ_5260_5320, + BRCM_5GHZ_5500_5700, + BRCM_5GHZ_5745_5825, + } +}; + struct brcms_cm_band { /* struct locale_info flags */ u8 locale_flags; @@ -137,14 +173,15 @@ struct country_info { const u8 locale_mimo_5G; /* 5G mimo info */ }; +struct brcms_regd { + struct country_info country; + const struct ieee80211_regdomain *regdomain; +}; + struct brcms_cm_info { struct brcms_pub *pub; struct brcms_c_info *wlc; - char srom_ccode[BRCM_CNTRY_BUF_SZ]; /* Country Code in SROM */ - uint srom_regrev; /* Regulatory Rev for the SROM ccode */ - const struct country_info *country; /* current country def */ - uint regrev; /* current Regulatory Revision */ - char country_abbrev[BRCM_CNTRY_BUF_SZ]; /* current advertised ccode */ + const struct brcms_regd *world_regd; /* per-band state (one per phy/radio) */ struct brcms_cm_band bandstate[MAXBANDS]; /* quiet channels currently for radar sensitivity or 11h support */ @@ -408,12 +445,12 @@ static const struct locale_mimo_info *g_mimo_5g_table[] = { &locale_11n }; -static const struct { - char abbrev[BRCM_CNTRY_BUF_SZ]; /* country abbreviation */ - struct country_info country; -} cntry_locales[] = { +static const struct brcms_regd cntry_locales[] = { + /* Worldwide RoW 2, must always be at index 0 */ { - "X2", LOCALES(i, 11, bn, 11n)}, /* Worldwide RoW 2 */ + .country = LOCALES(i, 11, bn, 11n), + .regdomain = &brcms_regdom_x2, + }, }; static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx) @@ -482,93 +519,24 @@ static bool brcms_c_country_valid(const char *ccode) return true; } -/* Lookup a country info structure from a null terminated country - * abbreviation and regrev directly with no translation. - */ -static const struct country_info * -brcms_c_country_lookup_direct(const char *ccode, uint regrev) +static const struct brcms_regd *brcms_world_regd(const char *regdom, int len) { - uint size, i; + const struct brcms_regd *regd = NULL; + int i; - /* Should just return 0 for single locale driver. */ - /* Keep it this way in case we add more locales. (for now anyway) */ - - /* - * all other country def arrays are for regrev == 0, so if - * regrev is non-zero, fail - */ - if (regrev > 0) - return NULL; - - /* find matched table entry from country code */ - size = ARRAY_SIZE(cntry_locales); - for (i = 0; i < size; i++) { - if (strcmp(ccode, cntry_locales[i].abbrev) == 0) - return &cntry_locales[i].country; - } - return NULL; -} - -static const struct country_info * -brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm, const char *ccode, - char *mapped_ccode, uint *mapped_regrev) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - const struct country_info *country; - uint srom_regrev = wlc_cm->srom_regrev; - const char *srom_ccode = wlc_cm->srom_ccode; - - /* check for currently supported ccode size */ - if (strlen(ccode) > (BRCM_CNTRY_BUF_SZ - 1)) { - wiphy_err(wlc->wiphy, "wl%d: %s: ccode \"%s\" too long for " - "match\n", wlc->pub->unit, __func__, ccode); - return NULL; - } - - /* default mapping is the given ccode and regrev 0 */ - strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ); - *mapped_regrev = 0; - - /* If the desired country code matches the srom country code, - * then the mapped country is the srom regulatory rev. - * Otherwise look for an aggregate mapping. - */ - if (!strcmp(srom_ccode, ccode)) { - *mapped_regrev = srom_regrev; - wiphy_err(wlc->wiphy, "srom_code == ccode %s\n", __func__); - } - - /* find the matching built-in country definition */ - country = brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev); - - /* if there is not an exact rev match, default to rev zero */ - if (country == NULL && *mapped_regrev != 0) { - *mapped_regrev = 0; - country = - brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev); + for (i = 0; i < ARRAY_SIZE(cntry_locales); i++) { + if (!strncmp(regdom, cntry_locales[i].regdomain->alpha2, len)) { + regd = &cntry_locales[i]; + break; + } } - return country; + return regd; } -/* Lookup a country info structure from a null terminated country code - * The lookup is case sensitive. - */ -static const struct country_info * -brcms_c_country_lookup(struct brcms_c_info *wlc, const char *ccode) +static const struct brcms_regd *brcms_default_world_regd(void) { - const struct country_info *country; - char mapped_ccode[BRCM_CNTRY_BUF_SZ]; - uint mapped_regrev; - - /* - * map the country code to a built-in country code, regrev, and - * country_info struct - */ - country = brcms_c_countrycode_map(wlc->cmi, ccode, mapped_ccode, - &mapped_regrev); - - return country; + return &cntry_locales[0]; } /* @@ -634,12 +602,6 @@ static bool brcms_c_japan_ccode(const char *ccode) (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9'))); } -/* Returns true if currently set country is Japan or variant */ -static bool brcms_c_japan(struct brcms_c_info *wlc) -{ - return brcms_c_japan_ccode(wlc->cmi->country_abbrev); -} - static void brcms_c_channel_min_txpower_limits_with_local_constraint( struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr, @@ -743,8 +705,8 @@ static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm) mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\" " "nbands %d bandlocked %d\n", wlc->pub->unit, - __func__, wlc_cm->country_abbrev, wlc->pub->_nbands, - wlc->bandlocked); + __func__, wlc_cm->world_regd->regdomain->alpha2, + wlc->pub->_nbands, wlc->bandlocked); } else if (mboolisset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE)) { /* @@ -753,15 +715,6 @@ static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm) */ mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); } - - /* - * Now that the country abbreviation is set, if the radio supports 2G, - * then set channel 14 restrictions based on the new locale. - */ - if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) - wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, - brcms_c_japan(wlc) ? true : - false); } static int @@ -820,20 +773,13 @@ brcms_c_channels_init(struct brcms_cm_info *wlc_cm, * information found with the country code. */ static void -brcms_c_set_country_common(struct brcms_cm_info *wlc_cm, - const char *country_abbrev, - const char *ccode, uint regrev, - const struct country_info *country) +brcms_c_set_country(struct brcms_cm_info *wlc_cm, + const struct brcms_regd *regd) { + const struct country_info *country = ®d->country; const struct locale_info *locale; struct brcms_c_info *wlc = wlc_cm->wlc; - /* save current country state */ - wlc_cm->country = country; - - strncpy(wlc_cm->country_abbrev, country_abbrev, BRCM_CNTRY_BUF_SZ - 1); - wlc_cm->regrev = regrev; - if ((wlc->pub->_n_enab & SUPPORT_11N) != wlc->protection->nmode_user) brcms_c_set_nmode(wlc); @@ -852,59 +798,13 @@ brcms_c_set_country_common(struct brcms_cm_info *wlc_cm, return; } -static int -brcms_c_set_countrycode_rev(struct brcms_cm_info *wlc_cm, - const char *country_abbrev, - const char *ccode, int regrev) -{ - const struct country_info *country; - char mapped_ccode[BRCM_CNTRY_BUF_SZ]; - uint mapped_regrev; - - /* if regrev is -1, lookup the mapped country code, - * otherwise use the ccode and regrev directly - */ - if (regrev == -1) { - /* - * map the country code to a built-in country - * code, regrev, and country_info - */ - country = - brcms_c_countrycode_map(wlc_cm, ccode, mapped_ccode, - &mapped_regrev); - } else { - /* find the matching built-in country definition */ - country = brcms_c_country_lookup_direct(ccode, regrev); - strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ); - mapped_regrev = regrev; - } - - if (country == NULL) - return -EINVAL; - - /* set the driver state for the country */ - brcms_c_set_country_common(wlc_cm, country_abbrev, mapped_ccode, - mapped_regrev, country); - - return 0; -} - -/* - * set the driver's current country and regulatory information using - * a country code as the source. Lookup built in country information - * found with the country code. - */ -static int -brcms_c_set_countrycode(struct brcms_cm_info *wlc_cm, const char *ccode) -{ - return brcms_c_set_countrycode_rev(wlc_cm, ccode, ccode, -1); -} - struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) { struct brcms_cm_info *wlc_cm; struct brcms_pub *pub = wlc->pub; struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; + const char *ccode = sprom->alpha2; + int ccode_len = sizeof(sprom->alpha2); BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); @@ -916,17 +816,27 @@ struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) wlc->cmi = wlc_cm; /* store the country code for passing up as a regulatory hint */ - if (sprom->alpha2 && brcms_c_country_valid(sprom->alpha2)) - strncpy(wlc->pub->srom_ccode, sprom->alpha2, sizeof(sprom->alpha2)); + wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); + if (brcms_c_country_valid(ccode)) + strncpy(wlc->pub->srom_ccode, ccode, ccode_len); + + /* + * If no custom world domain is found in the SROM, use the + * default "X2" domain. + */ + if (!wlc_cm->world_regd) { + wlc_cm->world_regd = brcms_default_world_regd(); + ccode = wlc_cm->world_regd->regdomain->alpha2; + ccode_len = BRCM_CNTRY_BUF_SZ - 1; + } /* save default country for exiting 11d regulatory mode */ - strncpy(wlc->country_default, "X2", BRCM_CNTRY_BUF_SZ - 1); + strncpy(wlc->country_default, ccode, ccode_len); /* initialize autocountry_default to driver default */ - strncpy(wlc->autocountry_default, wlc->country_default, - BRCM_CNTRY_BUF_SZ - 1); + strncpy(wlc->autocountry_default, ccode, ccode_len); - brcms_c_set_countrycode(wlc_cm, wlc->country_default); + brcms_c_set_country(wlc_cm, wlc_cm->world_regd); return wlc_cm; } @@ -994,13 +904,7 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, memset(txpwr, 0, sizeof(struct txpwr_limits)); - if (!brcms_c_valid_chanspec_db(wlc_cm, chanspec)) { - country = brcms_c_country_lookup(wlc, wlc->autocountry_default); - if (country == NULL) - return; - } else { - country = wlc_cm->country; - } + country = &wlc_cm->world_regd->country; chan = CHSPEC_CHANNEL(chanspec); band = wlc->bandstate[chspec_bandunit(chanspec)]; @@ -1252,3 +1156,128 @@ bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) { return brcms_c_valid_chanspec_ext(wlc_cm, chspec, true); } + +static bool brcms_is_radar_freq(u16 center_freq) +{ + return center_freq >= 5260 && center_freq <= 5700; +} + +static void brcms_reg_apply_radar_flags(struct wiphy *wiphy) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int i; + + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (!brcms_is_radar_freq(ch->center_freq)) + continue; + + /* + * All channels in this range should be passive and have + * DFS enabled. + */ + if (!(ch->flags & IEEE80211_CHAN_DISABLED)) + ch->flags |= IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_PASSIVE_SCAN; + } +} + +static void +brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + const struct ieee80211_reg_rule *rule; + int band, i, ret; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->flags & + (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) + continue; + + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { + ret = freq_reg_info(wiphy, ch->center_freq, + 0, &rule); + if (ret) + continue; + + if (!(rule->flags & NL80211_RRF_NO_IBSS)) + ch->flags &= ~IEEE80211_CHAN_NO_IBSS; + if (!(rule->flags & NL80211_RRF_PASSIVE_SCAN)) + ch->flags &= + ~IEEE80211_CHAN_PASSIVE_SCAN; + } else if (ch->beacon_found) { + ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_PASSIVE_SCAN); + } + } + } +} + +static int brcms_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct brcms_info *wl = hw->priv; + struct brcms_c_info *wlc = wl->wlc; + + brcms_reg_apply_radar_flags(wiphy); + + if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) + brcms_reg_apply_beaconing_flags(wiphy, request->initiator); + + if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) + wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, + brcms_c_japan_ccode(request->alpha2)); + + return 0; +} + +void brcms_c_regd_init(struct brcms_c_info *wlc) +{ + struct wiphy *wiphy = wlc->wiphy; + const struct brcms_regd *regd = wlc->cmi->world_regd; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct brcms_chanvec sup_chan; + struct brcms_band *band; + int band_idx, i; + + /* Disable any channels not supported by the phy */ + for (band_idx = 0; band_idx < IEEE80211_NUM_BANDS; band_idx++) { + if (band_idx == IEEE80211_BAND_2GHZ) + band = wlc->bandstate[BAND_2G_INDEX]; + else + band = wlc->bandstate[BAND_5G_INDEX]; + wlc_phy_chanspec_band_validch(band->pi, band->bandtype, + &sup_chan); + + sband = wiphy->bands[band_idx]; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + if (!isset(sup_chan.vec, ch->hw_value)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + } + + wlc->wiphy->reg_notifier = brcms_reg_notifier; + wlc->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | + WIPHY_FLAG_STRICT_REGULATORY; + wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); + brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); +} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h index 808cb4fbfbe7..de9887d95f2f 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.h @@ -49,5 +49,6 @@ extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, u8 local_constraint_qdbm); +extern void brcms_c_regd_init(struct brcms_c_info *wlc); #endif /* _WLC_CHANNEL_H */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 341e06a0d6ec..2d5a40412690 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -1050,6 +1050,8 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev) goto fail; } + brcms_c_regd_init(wl->wlc); + memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN); if (WARN_ON(!is_valid_ether_addr(perm))) goto fail; -- cgit v1.2.3 From 2ab631f48cac65864a61007215187cb7511b0846 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:53 -0500 Subject: brcm80211: smac: enable/disable radio on regulatory updates Currently the radio disable state is only updated during initialization, and it's only checked against the internal world domain. This is unnecessary, as there are always valid channels against this domain. Instead, check whether any channels are enabled in the regulatory notifier and update the radio state accordingly. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 67 +++++++++-------------- 1 file changed, 26 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 65ef60bf9c8f..3951ccf22c9b 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -677,46 +677,6 @@ brcms_c_channel_min_txpower_limits_with_local_constraint( } -/* Update the radio state (enable/disable) and tx power targets - * based on a new set of channel/regulatory information - */ -static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - uint chan; - - /* search for the existence of any valid channel */ - for (chan = 0; chan < MAXCHANNEL; chan++) { - if (brcms_c_valid_channel20_db(wlc->cmi, chan)) - break; - } - if (chan == MAXCHANNEL) - chan = INVCHANNEL; - - /* - * based on the channel search above, set or - * clear WL_RADIO_COUNTRY_DISABLE. - */ - if (chan == INVCHANNEL) { - /* - * country/locale with no valid channels, set - * the radio disable bit - */ - mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); - wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\" " - "nbands %d bandlocked %d\n", wlc->pub->unit, - __func__, wlc_cm->world_regd->regdomain->alpha2, - wlc->pub->_nbands, wlc->bandlocked); - } else if (mboolisset(wlc->pub->radio_disabled, - WL_RADIO_COUNTRY_DISABLE)) { - /* - * country/locale with valid channel, clear - * the radio disable bit - */ - mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); - } -} - static int brcms_c_channels_init(struct brcms_cm_info *wlc_cm, const struct country_info *country) @@ -762,7 +722,6 @@ brcms_c_channels_init(struct brcms_cm_info *wlc_cm, } brcms_c_quiet_channels_reset(wlc_cm); - brcms_c_channels_commit(wlc_cm); return 0; } @@ -1235,12 +1194,38 @@ static int brcms_reg_notifier(struct wiphy *wiphy, struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct brcms_info *wl = hw->priv; struct brcms_c_info *wlc = wl->wlc; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int band, i; + bool ch_found = false; brcms_reg_apply_radar_flags(wiphy); if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) brcms_reg_apply_beaconing_flags(wiphy, request->initiator); + /* Disable radio if all channels disallowed by regulatory */ + for (band = 0; !ch_found && band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; !ch_found && i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (!(ch->flags & IEEE80211_CHAN_DISABLED)) + ch_found = true; + } + } + + if (ch_found) { + mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); + } else { + mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); + wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\"\n", + wlc->pub->unit, __func__, request->alpha2); + } + if (wlc->pub->_nbands > 1 || wlc->band->bandtype == BRCM_BAND_2G) wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi, brcms_c_japan_ccode(request->alpha2)); -- cgit v1.2.3 From 2cf5089ed5ae4a8f73427234cb83c42eaaf34d46 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:54 -0500 Subject: brcm80211: smac: use mac80211 channel data for tx power limits Currently the limits from the internal X2 domain are used, regardless of what regulatory rules are in effect. Instead use the power limits set by the higher-level regulatory support. The rules for the MIMO power limits are still always derived from the world domain, pending guidance from Broadcom as to how these need to be handled. This will be fixed later, but using the limits from the world domain works for now. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 73 +++++------------------ 1 file changed, 14 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 3951ccf22c9b..bdb11ffcc154 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -74,15 +74,6 @@ /* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ #define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 -/* power level in group of 2.4GHz band channels: - * maxpwr[0] - CCK channels [1] - * maxpwr[1] - CCK channels [2-10] - * maxpwr[2] - CCK channels [11-14] - * maxpwr[3] - OFDM channels [1] - * maxpwr[4] - OFDM channels [2-10] - * maxpwr[5] - OFDM channels [11-14] - */ - /* maxpwr mapping to 5GHz band channels: * maxpwr[0] - channels [34-48] * maxpwr[1] - channels [52-60] @@ -101,10 +92,6 @@ #define LOCALES(band2, band5, mimo2, mimo5) \ {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)} -/* macro to get 2.4 GHz channel group index for tx power */ -#define CHANNEL_POWER_IDX_2G_CCK(c) (((c) < 2) ? 0 : (((c) < 11) ? 1 : 2)) -#define CHANNEL_POWER_IDX_2G_OFDM(c) (((c) < 2) ? 3 : (((c) < 11) ? 4 : 5)) - /* macro to get 5 GHz channel group index for tx power */ #define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ (((c) < 62) ? 1 : \ @@ -194,8 +181,6 @@ struct locale_info { u32 valid_channels; /* List of channels used only if APs are detected */ u8 restricted_channels; - /* Max tx pwr in qdBm for each sub-band */ - s8 maxpwr[BRCMS_MAXPWR_TBL_SIZE]; u8 flags; }; @@ -391,8 +376,6 @@ static void brcms_c_locale_get_channels(const struct locale_info *locale, static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13, LOCALE_RESTRICTED_SET_2G_SHORT, - {QDB(19), QDB(19), QDB(19), - QDB(19), QDB(19), QDB(19)}, BRCMS_EIRP }; @@ -403,7 +386,6 @@ static const struct locale_info locale_11 = { /* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */ LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165, LOCALE_RESTRICTED_NONE, - {QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)}, BRCMS_EIRP | BRCMS_DFS_EU }; @@ -847,6 +829,7 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, struct txpwr_limits *txpwr) { struct brcms_c_info *wlc = wlc_cm->wlc; + struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; uint i; uint chan; int maxpwr; @@ -855,7 +838,6 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, struct brcms_band *band; const struct locale_info *li; int conducted_max = BRCMS_TXPWR_MAX; - int conducted_ofdm_max = BRCMS_TXPWR_MAX; const struct locale_mimo_info *li_mimo; int maxpwr20, maxpwr40; int maxpwr_idx; @@ -863,6 +845,9 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, memset(txpwr, 0, sizeof(struct txpwr_limits)); + if (WARN_ON(!ch)) + return; + country = &wlc_cm->world_regd->country; chan = CHSPEC_CHANNEL(chanspec); @@ -875,49 +860,24 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, brcms_c_get_mimo_5g(country->locale_mimo_5G) : brcms_c_get_mimo_2g(country->locale_mimo_2G); - if (li->flags & BRCMS_EIRP) { - delta = band->antgain; - } else { - delta = 0; - if (band->antgain > QDB(6)) - delta = band->antgain - QDB(6); /* Excess over 6 dB */ - } + delta = band->antgain; - if (li == &locale_i) { + if (li == &locale_i) conducted_max = QDB(22); - conducted_ofdm_max = QDB(22); - } + + maxpwr = QDB(ch->max_power) - delta; + maxpwr = max(maxpwr, 0); + maxpwr = min(maxpwr, conducted_max); /* CCK txpwr limits for 2.4G band */ if (band->bandtype == BRCM_BAND_2G) { - maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_CCK(chan)]; - - maxpwr = maxpwr - delta; - maxpwr = max(maxpwr, 0); - maxpwr = min(maxpwr, conducted_max); - for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) txpwr->cck[i] = (u8) maxpwr; } - /* OFDM txpwr limits for 2.4G or 5G bands */ - if (band->bandtype == BRCM_BAND_2G) - maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_OFDM(chan)]; - else - maxpwr = li->maxpwr[CHANNEL_POWER_IDX_5G(chan)]; - - maxpwr = maxpwr - delta; - maxpwr = max(maxpwr, 0); - maxpwr = min(maxpwr, conducted_ofdm_max); - - /* Keep OFDM lmit below CCK limit */ - if (band->bandtype == BRCM_BAND_2G) - maxpwr = min_t(int, maxpwr, txpwr->cck[0]); - - for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) + for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { txpwr->ofdm[i] = (u8) maxpwr; - for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) { /* * OFDM 40 MHz SISO has the same power as the corresponding * MCS0-7 rate unless overriden by the locale specific code. @@ -932,14 +892,9 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, txpwr->ofdm_40_cdd[i] = 0; } - /* MIMO/HT specific limits */ - if (li_mimo->flags & BRCMS_EIRP) { - delta = band->antgain; - } else { - delta = 0; - if (band->antgain > QDB(6)) - delta = band->antgain - QDB(6); /* Excess over 6 dB */ - } + delta = 0; + if (band->antgain > QDB(6)) + delta = band->antgain - QDB(6); /* Excess over 6 dB */ if (band->bandtype == BRCM_BAND_2G) maxpwr_idx = (chan - 1); -- cgit v1.2.3 From 853346d8b60738467581d1cd9ccb678aeb086121 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:55 -0500 Subject: brcm80211: smac: don't validate channels against internal regulatory data The core regulatory support will disable channels not allowed by regulatory rules, so brcmsmac doesn't need to check whether or not the requested channel is permitted by regulatory. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 344 +--------------------- 1 file changed, 6 insertions(+), 338 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index bdb11ffcc154..04e6a6ec77ff 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -30,40 +30,6 @@ /* QDB() macro takes a dB value and converts to a quarter dB value */ #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) -#define LOCALE_CHAN_01_11 (1<<0) -#define LOCALE_CHAN_12_13 (1<<1) -#define LOCALE_CHAN_14 (1<<2) -#define LOCALE_SET_5G_LOW_JP1 (1<<3) /* 34-48, step 2 */ -#define LOCALE_SET_5G_LOW_JP2 (1<<4) /* 34-46, step 4 */ -#define LOCALE_SET_5G_LOW1 (1<<5) /* 36-48, step 4 */ -#define LOCALE_SET_5G_LOW2 (1<<6) /* 52 */ -#define LOCALE_SET_5G_LOW3 (1<<7) /* 56-64, step 4 */ -#define LOCALE_SET_5G_MID1 (1<<8) /* 100-116, step 4 */ -#define LOCALE_SET_5G_MID2 (1<<9) /* 120-124, step 4 */ -#define LOCALE_SET_5G_MID3 (1<<10) /* 128 */ -#define LOCALE_SET_5G_HIGH1 (1<<11) /* 132-140, step 4 */ -#define LOCALE_SET_5G_HIGH2 (1<<12) /* 149-161, step 4 */ -#define LOCALE_SET_5G_HIGH3 (1<<13) /* 165 */ -#define LOCALE_CHAN_52_140_ALL (1<<14) -#define LOCALE_SET_5G_HIGH4 (1<<15) /* 184-216 */ - -#define LOCALE_CHAN_36_64 (LOCALE_SET_5G_LOW1 | \ - LOCALE_SET_5G_LOW2 | \ - LOCALE_SET_5G_LOW3) -#define LOCALE_CHAN_52_64 (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3) -#define LOCALE_CHAN_100_124 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2) -#define LOCALE_CHAN_100_140 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | \ - LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1) -#define LOCALE_CHAN_149_165 (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3) -#define LOCALE_CHAN_184_216 LOCALE_SET_5G_HIGH4 - -#define LOCALE_CHAN_01_14 (LOCALE_CHAN_01_11 | \ - LOCALE_CHAN_12_13 | \ - LOCALE_CHAN_14) - -#define LOCALE_RESTRICTED_NONE 0 -#define LOCALE_RESTRICTED_SET_2G_SHORT 1 - #define LOCALE_2G_IDX_i 0 #define LOCALE_5G_IDX_11 0 #define LOCALE_MIMO_IDX_bn 0 @@ -134,10 +100,6 @@ static const struct ieee80211_regdomain brcms_regdom_x2 = { struct brcms_cm_band { /* struct locale_info flags */ u8 locale_flags; - /* List of valid channels in the country */ - struct brcms_chanvec valid_channels; - /* List of restricted use channels */ - const struct brcms_chanvec *restricted_channels; }; /* locale per-channel tx power limits for MIMO frames @@ -171,211 +133,17 @@ struct brcms_cm_info { const struct brcms_regd *world_regd; /* per-band state (one per phy/radio) */ struct brcms_cm_band bandstate[MAXBANDS]; - /* quiet channels currently for radar sensitivity or 11h support */ - /* channels on which we cannot transmit */ - struct brcms_chanvec quiet_channels; }; /* locale channel and power info. */ struct locale_info { - u32 valid_channels; - /* List of channels used only if APs are detected */ - u8 restricted_channels; u8 flags; }; -/* Regulatory Matrix Spreadsheet (CLM) MIMO v3.7.9 */ - -/* - * Some common channel sets - */ - -/* No channels */ -static const struct brcms_chanvec chanvec_none = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -/* - * Restricted channel sets - */ - -/* Channels 12, 13 */ -static const struct brcms_chanvec restricted_set_2g_short = { - {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -/* global memory to provide working buffer for expanded locale */ - -static const struct brcms_chanvec *g_table_restricted_chan[] = { - &chanvec_none, /* restricted_set_none */ - &restricted_set_2g_short, -}; - -static const struct brcms_chanvec locale_2g_01_11 = { - {0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_2g_12_13 = { - {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_2g_14 = { - {0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_LOW_JP1 = { - {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_LOW_JP2 = { - {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_LOW1 = { - {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_LOW2 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_LOW3 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_MID1 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_MID2 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_MID3 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_HIGH1 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_HIGH2 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x20, 0x22, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_HIGH3 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_52_140_ALL = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - 0x11, 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00} -}; - -static const struct brcms_chanvec locale_5g_HIGH4 = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, - 0x11, 0x11, 0x11, 0x11} -}; - -static const struct brcms_chanvec *g_table_locale_base[] = { - &locale_2g_01_11, - &locale_2g_12_13, - &locale_2g_14, - &locale_5g_LOW_JP1, - &locale_5g_LOW_JP2, - &locale_5g_LOW1, - &locale_5g_LOW2, - &locale_5g_LOW3, - &locale_5g_MID1, - &locale_5g_MID2, - &locale_5g_MID3, - &locale_5g_HIGH1, - &locale_5g_HIGH2, - &locale_5g_HIGH3, - &locale_5g_52_140_ALL, - &locale_5g_HIGH4 -}; - -static void brcms_c_locale_add_channels(struct brcms_chanvec *target, - const struct brcms_chanvec *channels) -{ - u8 i; - for (i = 0; i < sizeof(struct brcms_chanvec); i++) - target->vec[i] |= channels->vec[i]; -} - -static void brcms_c_locale_get_channels(const struct locale_info *locale, - struct brcms_chanvec *channels) -{ - u8 i; - - memset(channels, 0, sizeof(struct brcms_chanvec)); - - for (i = 0; i < ARRAY_SIZE(g_table_locale_base); i++) { - if (locale->valid_channels & (1 << i)) - brcms_c_locale_add_channels(channels, - g_table_locale_base[i]); - } -} - /* * Locale Definitions - 2.4 GHz */ static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ - LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13, - LOCALE_RESTRICTED_SET_2G_SHORT, BRCMS_EIRP }; @@ -383,9 +151,6 @@ static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ * Locale Definitions - 5 GHz */ static const struct locale_info locale_11 = { - /* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */ - LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165, - LOCALE_RESTRICTED_NONE, BRCMS_EIRP | BRCMS_DFS_EU }; @@ -521,62 +286,6 @@ static const struct brcms_regd *brcms_default_world_regd(void) return &cntry_locales[0]; } -/* - * reset the quiet channels vector to the union - * of the restricted and radar channel sets - */ -static void brcms_c_quiet_channels_reset(struct brcms_cm_info *wlc_cm) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - uint i, j; - struct brcms_band *band; - const struct brcms_chanvec *chanvec; - - memset(&wlc_cm->quiet_channels, 0, sizeof(struct brcms_chanvec)); - - band = wlc->band; - for (i = 0; i < wlc->pub->_nbands; - i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) { - - /* initialize quiet channels for restricted channels */ - chanvec = wlc_cm->bandstate[band->bandunit].restricted_channels; - for (j = 0; j < sizeof(struct brcms_chanvec); j++) - wlc_cm->quiet_channels.vec[j] |= chanvec->vec[j]; - - } -} - -/* Is the channel valid for the current locale and current band? */ -static bool brcms_c_valid_channel20(struct brcms_cm_info *wlc_cm, uint val) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - - return ((val < MAXCHANNEL) && - isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec, - val)); -} - -/* Is the channel valid for the current locale and specified band? */ -static bool brcms_c_valid_channel20_in_band(struct brcms_cm_info *wlc_cm, - uint bandunit, uint val) -{ - return ((val < MAXCHANNEL) - && isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val)); -} - -/* Is the channel valid for the current locale? (but don't consider channels not - * available due to bandlocking) - */ -static bool brcms_c_valid_channel20_db(struct brcms_cm_info *wlc_cm, uint val) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - - return brcms_c_valid_channel20(wlc->cmi, val) || - (!wlc->bandlocked - && brcms_c_valid_channel20_in_band(wlc->cmi, - OTHERBANDUNIT(wlc), val)); -} - /* JP, J1 - J10 are Japan ccodes */ static bool brcms_c_japan_ccode(const char *ccode) { @@ -664,10 +373,9 @@ brcms_c_channels_init(struct brcms_cm_info *wlc_cm, const struct country_info *country) { struct brcms_c_info *wlc = wlc_cm->wlc; - uint i, j; + uint i; struct brcms_band *band; const struct locale_info *li; - struct brcms_chanvec sup_chan; const struct locale_mimo_info *li_mimo; band = wlc->band; @@ -685,26 +393,8 @@ brcms_c_channels_init(struct brcms_cm_info *wlc_cm, /* merge the mimo non-mimo locale flags */ wlc_cm->bandstate[band->bandunit].locale_flags |= li_mimo->flags; - - wlc_cm->bandstate[band->bandunit].restricted_channels = - g_table_restricted_chan[li->restricted_channels]; - - /* - * set the channel availability, masking out the channels - * that may not be supported on this phy. - */ - wlc_phy_chanspec_band_validch(band->pi, band->bandtype, - &sup_chan); - brcms_c_locale_get_channels(li, - &wlc_cm->bandstate[band->bandunit]. - valid_channels); - for (j = 0; j < sizeof(struct brcms_chanvec); j++) - wlc_cm->bandstate[band->bandunit].valid_channels. - vec[j] &= sup_chan.vec[j]; } - brcms_c_quiet_channels_reset(wlc_cm); - return 0; } @@ -794,23 +484,12 @@ brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm, return wlc_cm->bandstate[bandunit].locale_flags; } -static bool -brcms_c_quiet_chanspec(struct brcms_cm_info *wlc_cm, u16 chspec) -{ - return (wlc_cm->wlc->pub->_n_enab & SUPPORT_11N) && - CHSPEC_IS40(chspec) ? - (isset(wlc_cm->quiet_channels.vec, - lower_20_sb(CHSPEC_CHANNEL(chspec))) || - isset(wlc_cm->quiet_channels.vec, - upper_20_sb(CHSPEC_CHANNEL(chspec)))) : - isset(wlc_cm->quiet_channels.vec, CHSPEC_CHANNEL(chspec)); -} - void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, u8 local_constraint_qdbm) { struct brcms_c_info *wlc = wlc_cm->wlc; + struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; struct txpwr_limits txpwr; brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); @@ -820,7 +499,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, ); brcms_b_set_chanspec(wlc->hw, chanspec, - (brcms_c_quiet_chanspec(wlc_cm, chanspec) != 0), + !!(ch->flags & IEEE80211_CHAN_PASSIVE_SCAN), &txpwr); } @@ -1036,8 +715,7 @@ static bool brcms_c_chspec_malformed(u16 chanspec) * and they are also a legal HT combination */ static bool -brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec, - bool dualband) +brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec) { struct brcms_c_info *wlc = wlc_cm->wlc; u8 channel = CHSPEC_CHANNEL(chspec); @@ -1053,22 +731,12 @@ brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec, chspec_bandunit(chspec)) return false; - /* Check a 20Mhz channel */ - if (CHSPEC_IS20(chspec)) { - if (dualband) - return brcms_c_valid_channel20_db(wlc_cm->wlc->cmi, - channel); - else - return brcms_c_valid_channel20(wlc_cm->wlc->cmi, - channel); - } - - return false; + return true; } bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec) { - return brcms_c_valid_chanspec_ext(wlc_cm, chspec, true); + return brcms_c_valid_chanspec_ext(wlc_cm, chspec); } static bool brcms_is_radar_freq(u16 center_freq) -- cgit v1.2.3 From edc7651f3a4ffa7fca37d92401562694121512ad Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Sat, 16 Jun 2012 07:47:56 -0500 Subject: brcm80211: smac: use current regulatory domain when checking whether OFDM is allowed The brcmsmac internal regulatory data is being used to determine whether OFDM should be allowed, and this is only done once during initialization. To be effective this needs to be checked against mac80211's regulatory rules for the current channel. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Seth Forshee Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 132 +++------------------- drivers/net/wireless/brcm80211/brcmsmac/channel.h | 3 - drivers/net/wireless/brcm80211/brcmsmac/main.c | 6 +- 3 files changed, 15 insertions(+), 126 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 04e6a6ec77ff..d3c726021c35 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -30,13 +30,9 @@ /* QDB() macro takes a dB value and converts to a quarter dB value */ #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) -#define LOCALE_2G_IDX_i 0 -#define LOCALE_5G_IDX_11 0 #define LOCALE_MIMO_IDX_bn 0 #define LOCALE_MIMO_IDX_11n 0 -/* max of BAND_5G_PWR_LVLS and 6 for 2.4 GHz */ -#define BRCMS_MAXPWR_TBL_SIZE 6 /* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */ #define BRCMS_MAXPWR_MIMO_TBL_SIZE 14 @@ -51,12 +47,8 @@ #define LC(id) LOCALE_MIMO_IDX_ ## id -#define LC_2G(id) LOCALE_2G_IDX_ ## id - -#define LC_5G(id) LOCALE_5G_IDX_ ## id - -#define LOCALES(band2, band5, mimo2, mimo5) \ - {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)} +#define LOCALES(mimo2, mimo5) \ + {LC(mimo2), LC(mimo5)} /* macro to get 5 GHz channel group index for tx power */ #define CHANNEL_POWER_IDX_5G(c) (((c) < 52) ? 0 : \ @@ -97,11 +89,6 @@ static const struct ieee80211_regdomain brcms_regdom_x2 = { } }; -struct brcms_cm_band { - /* struct locale_info flags */ - u8 locale_flags; -}; - /* locale per-channel tx power limits for MIMO frames * maxpwr arrays are index by channel for 2.4 GHz limits, and * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel) @@ -111,13 +98,10 @@ struct locale_mimo_info { s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE]; /* tx 40 MHz power limits, qdBm units */ s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE]; - u8 flags; }; /* Country names and abbreviations with locale defined from ISO 3166 */ struct country_info { - const u8 locale_2G; /* 2.4G band locale */ - const u8 locale_5G; /* 5G band locale */ const u8 locale_mimo_2G; /* 2.4G mimo info */ const u8 locale_mimo_5G; /* 5G mimo info */ }; @@ -131,35 +115,6 @@ struct brcms_cm_info { struct brcms_pub *pub; struct brcms_c_info *wlc; const struct brcms_regd *world_regd; - /* per-band state (one per phy/radio) */ - struct brcms_cm_band bandstate[MAXBANDS]; -}; - -/* locale channel and power info. */ -struct locale_info { - u8 flags; -}; - -/* - * Locale Definitions - 2.4 GHz - */ -static const struct locale_info locale_i = { /* locale i. channel 1 - 13 */ - BRCMS_EIRP -}; - -/* - * Locale Definitions - 5 GHz - */ -static const struct locale_info locale_11 = { - BRCMS_EIRP | BRCMS_DFS_EU -}; - -static const struct locale_info *g_locale_2g_table[] = { - &locale_i -}; - -static const struct locale_info *g_locale_5g_table[] = { - &locale_11 }; /* @@ -172,7 +127,6 @@ static const struct locale_mimo_info locale_bn = { {0, 0, QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), QDB(13), 0, 0}, - 0 }; static const struct locale_mimo_info *g_mimo_2g_table[] = { @@ -185,7 +139,6 @@ static const struct locale_mimo_info *g_mimo_2g_table[] = { static const struct locale_mimo_info locale_11n = { { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)}, {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)}, - 0 }; static const struct locale_mimo_info *g_mimo_5g_table[] = { @@ -195,27 +148,11 @@ static const struct locale_mimo_info *g_mimo_5g_table[] = { static const struct brcms_regd cntry_locales[] = { /* Worldwide RoW 2, must always be at index 0 */ { - .country = LOCALES(i, 11, bn, 11n), + .country = LOCALES(bn, 11n), .regdomain = &brcms_regdom_x2, }, }; -static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx) -{ - if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) - return NULL; /* error condition */ - - return g_locale_2g_table[locale_idx]; -} - -static const struct locale_info *brcms_c_get_locale_5g(u8 locale_idx) -{ - if (locale_idx >= ARRAY_SIZE(g_locale_5g_table)) - return NULL; /* error condition */ - - return g_locale_5g_table[locale_idx]; -} - static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx) { if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) @@ -368,36 +305,6 @@ brcms_c_channel_min_txpower_limits_with_local_constraint( } -static int -brcms_c_channels_init(struct brcms_cm_info *wlc_cm, - const struct country_info *country) -{ - struct brcms_c_info *wlc = wlc_cm->wlc; - uint i; - struct brcms_band *band; - const struct locale_info *li; - const struct locale_mimo_info *li_mimo; - - band = wlc->band; - for (i = 0; i < wlc->pub->_nbands; - i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) { - - li = (band->bandtype == BRCM_BAND_5G) ? - brcms_c_get_locale_5g(country->locale_5G) : - brcms_c_get_locale_2g(country->locale_2G); - wlc_cm->bandstate[band->bandunit].locale_flags = li->flags; - li_mimo = (band->bandtype == BRCM_BAND_5G) ? - brcms_c_get_mimo_5g(country->locale_mimo_5G) : - brcms_c_get_mimo_2g(country->locale_mimo_2G); - - /* merge the mimo non-mimo locale flags */ - wlc_cm->bandstate[band->bandunit].locale_flags |= - li_mimo->flags; - } - - return 0; -} - /* * set the driver's current country and regulatory information * using a country code as the source. Look up built in country @@ -407,8 +314,6 @@ static void brcms_c_set_country(struct brcms_cm_info *wlc_cm, const struct brcms_regd *regd) { - const struct country_info *country = ®d->country; - const struct locale_info *locale; struct brcms_c_info *wlc = wlc_cm->wlc; if ((wlc->pub->_n_enab & SUPPORT_11N) != @@ -417,14 +322,8 @@ brcms_c_set_country(struct brcms_cm_info *wlc_cm, brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]); brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]); - /* set or restore gmode as required by regulatory */ - locale = brcms_c_get_locale_2g(country->locale_2G); - if (locale && (locale->flags & BRCMS_NO_OFDM)) - brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); - else - brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); - brcms_c_channels_init(wlc_cm, country); + brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); return; } @@ -477,20 +376,15 @@ void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm) kfree(wlc_cm); } -u8 -brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm, - uint bandunit) -{ - return wlc_cm->bandstate[bandunit].locale_flags; -} - void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, u8 local_constraint_qdbm) { struct brcms_c_info *wlc = wlc_cm->wlc; struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; + const struct ieee80211_reg_rule *reg_rule; struct txpwr_limits txpwr; + int ret; brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr); @@ -498,6 +392,13 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, wlc_cm, &txpwr, local_constraint_qdbm ); + /* set or restore gmode as required by regulatory */ + ret = freq_reg_info(wlc->wiphy, ch->center_freq, 0, ®_rule); + if (!ret && (reg_rule->flags & NL80211_RRF_NO_OFDM)) + brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false); + else + brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); + brcms_b_set_chanspec(wlc->hw, chanspec, !!(ch->flags & IEEE80211_CHAN_PASSIVE_SCAN), &txpwr); @@ -515,7 +416,6 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, int delta; const struct country_info *country; struct brcms_band *band; - const struct locale_info *li; int conducted_max = BRCMS_TXPWR_MAX; const struct locale_mimo_info *li_mimo; int maxpwr20, maxpwr40; @@ -531,17 +431,13 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, chan = CHSPEC_CHANNEL(chanspec); band = wlc->bandstate[chspec_bandunit(chanspec)]; - li = (band->bandtype == BRCM_BAND_5G) ? - brcms_c_get_locale_5g(country->locale_5G) : - brcms_c_get_locale_2g(country->locale_2G); - li_mimo = (band->bandtype == BRCM_BAND_5G) ? brcms_c_get_mimo_5g(country->locale_mimo_5G) : brcms_c_get_mimo_2g(country->locale_mimo_2G); delta = band->antgain; - if (li == &locale_i) + if (band->bandtype == BRCM_BAND_2G) conducted_max = QDB(22); maxpwr = QDB(ch->max_power) - delta; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h index de9887d95f2f..006483a0abe6 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.h @@ -37,9 +37,6 @@ brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); -extern u8 brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm, - uint bandunit); - extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 93c865db7729..8776fbc8dcf1 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -5386,11 +5387,6 @@ int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config) else return -EINVAL; - /* Legacy or bust when no OFDM is supported by regulatory */ - if ((brcms_c_channel_locale_flags_in_band(wlc->cmi, band->bandunit) & - BRCMS_NO_OFDM) && (gmode != GMODE_LEGACY_B)) - return -EINVAL; - /* update configuration value */ if (config) brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode); -- cgit v1.2.3 From 7e05bedca084faa073c2e64bbcd6407dbaff3151 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 16 Jun 2012 22:31:48 +0300 Subject: wl1251: remove unused filter_work filter_work is never used, remove it. Signed-off-by: Grazvydas Ignotas Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl1251/main.c | 36 --------------------------------- drivers/net/wireless/ti/wl1251/wl1251.h | 1 - 2 files changed, 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index d1afb8e3b2ef..ff60a7a341be 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -348,33 +348,6 @@ out: return ret; } -static void wl1251_filter_work(struct work_struct *work) -{ - struct wl1251 *wl = - container_of(work, struct wl1251, filter_work); - int ret; - - mutex_lock(&wl->mutex); - - if (wl->state == WL1251_STATE_OFF) - goto out; - - ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, - wl->dtim_period); - if (ret < 0) - goto out_sleep; - -out_sleep: - wl1251_ps_elp_sleep(wl); - -out: - mutex_unlock(&wl->mutex); -} - static void wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1251 *wl = hw->priv; @@ -478,7 +451,6 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); - cancel_work_sync(&wl->filter_work); cancel_delayed_work_sync(&wl->elp_work); mutex_lock(&wl->mutex); @@ -723,13 +695,6 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, wl->rx_filter |= CFG_RX_CTL_EN; if (*total & FIF_OTHER_BSS) wl->rx_filter &= ~CFG_BSSID_FILTER_EN; - - /* - * FIXME: workqueues need to be properly cancelled on stop(), for - * now let's just disable changing the filter settings. They will - * be updated any on config(). - */ - /* schedule_work(&wl->filter_work); */ } /* HW encryption */ @@ -1390,7 +1355,6 @@ struct ieee80211_hw *wl1251_alloc_hw(void) skb_queue_head_init(&wl->tx_queue); - INIT_WORK(&wl->filter_work, wl1251_filter_work); INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; wl->scanning = false; diff --git a/drivers/net/wireless/ti/wl1251/wl1251.h b/drivers/net/wireless/ti/wl1251/wl1251.h index 9d8f5816c6f9..fd02060038de 100644 --- a/drivers/net/wireless/ti/wl1251/wl1251.h +++ b/drivers/net/wireless/ti/wl1251/wl1251.h @@ -315,7 +315,6 @@ struct wl1251 { bool tx_queue_stopped; struct work_struct tx_work; - struct work_struct filter_work; /* Pending TX frames */ struct sk_buff *tx_frames[16]; -- cgit v1.2.3 From a2d2bb8675fe9dc127d802b6be6517a0932e65b7 Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 16 Jun 2012 22:31:49 +0300 Subject: wl1251: fix filtering support This driver has a hack in cmd.c which effectively disables all filtering. This seems to be triggering a firmware bug where it stops reporting any rx packets after random time on some routers, which is eliminated (or at least appears much more rarely) when filtering is on. I have found that only BSSID filter needs to be disabled for association to work, so disable only that instead of all filtering. Signed-off-by: Grazvydas Ignotas Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl1251/cmd.c | 9 --------- drivers/net/wireless/ti/wl1251/main.c | 6 ++++++ 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c index d14d69d733a0..6822b845efc1 100644 --- a/drivers/net/wireless/ti/wl1251/cmd.c +++ b/drivers/net/wireless/ti/wl1251/cmd.c @@ -277,15 +277,6 @@ int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, join->rx_config_options = wl->rx_config; join->rx_filter_options = wl->rx_filter; - /* - * FIXME: disable temporarily all filters because after commit - * 9cef8737 "mac80211: fix managed mode BSSID handling" broke - * association. The filter logic needs to be implemented properly - * and once that is done, this hack can be removed. - */ - join->rx_config_options = 0; - join->rx_filter_options = WL1251_DEFAULT_RX_FILTER; - join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index ff60a7a341be..6094e3f9e2f8 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -334,6 +334,12 @@ static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel, if (ret < 0) goto out; + /* + * Join command applies filters, and if we are not associated, + * BSSID filter must be disabled for association to work. + */ + if (is_zero_ether_addr(wl->bssid)) + wl->rx_config &= ~CFG_BSSID_FILTER_EN; ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval, dtim_period); -- cgit v1.2.3 From 84b60c144cd32db5ca5185405e9b3f84cac9df9a Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 16 Jun 2012 22:31:50 +0300 Subject: wl1251: send filters to firmware as they are set Firmware supports changing filters using ACX_RX_CFG command, so use it in .configure_filter callback. Firmware also supports probe request filtering, so add it too along the way. This will also re-enable BSSID filter which is now removed by join command while associating. Signed-off-by: Grazvydas Ignotas Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl1251/main.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 6094e3f9e2f8..3118c425bcf1 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -659,13 +659,15 @@ out: FIF_FCSFAIL | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_CONTROL | \ - FIF_OTHER_BSS) + FIF_OTHER_BSS | \ + FIF_PROBE_REQ) static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *total,u64 multicast) { struct wl1251 *wl = hw->priv; + int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter"); @@ -676,7 +678,7 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, /* no filters which we support changed */ return; - /* FIXME: wl->rx_config and wl->rx_filter are not protected */ + mutex_lock(&wl->mutex); wl->rx_config = WL1251_DEFAULT_RX_CONFIG; wl->rx_filter = WL1251_DEFAULT_RX_FILTER; @@ -699,8 +701,25 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, } if (*total & FIF_CONTROL) wl->rx_filter |= CFG_RX_CTL_EN; - if (*total & FIF_OTHER_BSS) - wl->rx_filter &= ~CFG_BSSID_FILTER_EN; + if (*total & FIF_OTHER_BSS || is_zero_ether_addr(wl->bssid)) + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + if (*total & FIF_PROBE_REQ) + wl->rx_filter |= CFG_RX_PREQ_EN; + + if (wl->state == WL1251_STATE_OFF) + goto out; + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* send filters to firmware */ + wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter); + + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); } /* HW encryption */ -- cgit v1.2.3 From 305dd09f8ce05cc8a8cce4e790a6d3d02e5c4f1d Mon Sep 17 00:00:00 2001 From: Bala Shanmugam Date: Mon, 18 Jun 2012 11:36:58 +0530 Subject: ath9k: fix incorrect profile type manupulation Two MCI interrupts are generated while adding A2DP and headset profiles with different types and same connection handle. While disconnecting, only one MCI interrupt is generated with last added profile type value for both profiles. While adding second profile type decrement first one. Signed-off-by: Bala Shanmugam Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mci.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 7d34a504d617..c40e568b5c2b 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -233,8 +233,21 @@ static void ath_mci_process_profile(struct ath_softc *sc, struct ath_mci_profile_info *entry = NULL; entry = ath_mci_find_profile(mci, info); - if (entry) + if (entry) { + /* + * Two MCI interrupts are generated while connecting to + * headset and A2DP profile, but only one MCI interrupt + * is generated with last added profile type while disconnecting + * both profiles. + * So while adding second profile type decrement + * the first one. + */ + if (entry->type != info->type) { + DEC_PROF(mci, entry); + INC_PROF(mci, info); + } memcpy(entry, info, 10); + } if (info->start) { if (!entry && !ath_mci_add_profile(common, mci, info)) -- cgit v1.2.3 From a68807e9177a083dc09c24b141158539d71db21c Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 18 Jun 2012 19:02:37 +0530 Subject: ath9k_hw: fix BT mute at hw init WLAN driver initialization is muting BT which is terminating the ongoing BT traffic. The reason to mute BT is to avoid any incoming MCI messages from BT when MCI reset is in progress that could corrupt WLAN MCI RX state machine. But we should not dedicate radio completely to WLAN in driver init itself. So this patch removes the wlan weightage changes from mute BT to retain BT connection. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 4 ---- drivers/net/wireless/ath/ath9k/reg.h | 4 ---- 2 files changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index cc2853ade8f8..867238f9d139 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -767,10 +767,6 @@ static void ar9003_mci_mute_bt(struct ath_hw *ah) { /* disable all MCI messages */ REG_WRITE(ah, AR_MCI_MSG_ATTRIBUTES_TABLE, 0xffff0000); - REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS0, 0xffffffff); - REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS1, 0xffffffff); - REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS2, 0xffffffff); - REG_WRITE(ah, AR_BTCOEX_WL_WEIGHTS3, 0xffffffff); REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); /* wait pending HW messages to flush out */ diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 75acefbd4937..5046b282a93c 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -2162,10 +2162,6 @@ enum { #define AR_BTCOEX_CTRL_SPDT_POLARITY 0x80000000 #define AR_BTCOEX_CTRL_SPDT_POLARITY_S 31 -#define AR_BTCOEX_WL_WEIGHTS0 0x18b0 -#define AR_BTCOEX_WL_WEIGHTS1 0x18b4 -#define AR_BTCOEX_WL_WEIGHTS2 0x18b8 -#define AR_BTCOEX_WL_WEIGHTS3 0x18bc #define AR_BTCOEX_MAX_TXPWR(_x) (0x18c0 + ((_x) << 2)) #define AR_BTCOEX_WL_LNA 0x1940 #define AR_BTCOEX_RFGAIN_CTRL 0x1944 -- cgit v1.2.3 From e1ecad78e5c5c32f331925f340141a38aaa64cef Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 18 Jun 2012 19:02:38 +0530 Subject: ath9k: fix mci_is_enabled utility During driver stop, btcoex is disabled and also btcoex_hw.enabled is set to false. Afterwards mci_is_enabled returns false so that BT is not gaining SPDT control on WLAN sleep. Fix that. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index b49e4b48f459..94096607cbdd 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -1030,7 +1030,8 @@ static inline bool ath9k_hw_btcoex_is_enabled(struct ath_hw *ah) } static inline bool ath9k_hw_mci_is_enabled(struct ath_hw *ah) { - return ah->btcoex_hw.enabled && (ah->caps.hw_caps & ATH9K_HW_CAP_MCI); + return ah->common.btcoex_enabled && + (ah->caps.hw_caps & ATH9K_HW_CAP_MCI); } void ath9k_hw_btcoex_enable(struct ath_hw *ah); -- cgit v1.2.3 From 0967d862315366e2ef2401301ff2b1756928b4c1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 18 Jun 2012 19:02:39 +0530 Subject: ath9k_hw: update ar9462 dac_async_fifo initval Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 8f406ff2c95e..bbf48918a56c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -61,7 +61,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, - {0x0000a204, 0x013187c0, 0x013187c4, 0x013187c4, 0x013187c0}, + {0x0000a204, 0x01318fc0, 0x01318fc4, 0x01318fc4, 0x01318fc0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, -- cgit v1.2.3 From 4ff6a9d200c66f192da61822c36ad14424e218d6 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Mon, 18 Jun 2012 19:02:40 +0530 Subject: ath9k_hw: fix LNA control on WLAN sleep When WLAN enter full sleep mode, WLAN HW should send out a LNA_TAKE message for BT to take control of the shared LNA. Otherwise BT traffic is completely stopped whenever the wlan interface is moved full sleep mode. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_mci.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 867238f9d139..61558375bfbf 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1015,9 +1015,14 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) return; if (mci->is_2g) { - ar9003_mci_send_2g5g_status(ah, true); + if (!force) { + ar9003_mci_send_2g5g_status(ah, true); - REG_SET_BIT(ah, AR_MCI_TX_CTRL, + ar9003_mci_send_lna_transfer(ah, true); + udelay(5); + } + + REG_CLR_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); REG_CLR_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); @@ -1025,6 +1030,11 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) ar9003_mci_osla_setup(ah, true); } else { + if (!force) { + ar9003_mci_send_lna_take(ah, true); + udelay(5); + } + REG_SET_BIT(ah, AR_MCI_TX_CTRL, AR_MCI_TX_CTRL_DISABLE_LNA_UPDATE); REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, @@ -1251,6 +1261,9 @@ void ar9003_mci_bt_gain_ctrl(struct ath_hw *ah) ath_dbg(common, MCI, "Give LNA and SPDT control to BT\n"); + ar9003_mci_send_lna_take(ah, true); + udelay(50); + REG_SET_BIT(ah, AR_PHY_GLB_CONTROL, AR_BTCOEX_CTRL_BT_OWN_SPDT_CTRL); mci->is_2g = false; mci->update_2g5g = true; -- cgit v1.2.3 From 5039f38e16695a0a5c72b08c6a6cc66844a657b1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Tue, 19 Jun 2012 14:50:28 +0530 Subject: ath9k: do not sampling on ani timer when chip is in sleep The baseband and cycle counters are being sampled during ani processing for debugging purpose. Whenever the ani is postponded due to sleep state, taking samples on that time is of no use and also unneccesarily waking up the chip might increase the power consumption on idle associated state. Hence moving debug function within powersave block. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index a105c9426251..91650fe50461 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -407,6 +407,7 @@ void ath_ani_calibrate(unsigned long data) longcal ? "long" : "", shortcal ? "short" : "", aniflag ? "ani" : "", common->ani.caldone ? "true" : "false"); + ath9k_debug_samp_bb_mac(sc); ath9k_ps_restore(sc); set_timer: @@ -415,7 +416,6 @@ set_timer: * The interval must be the shortest necessary to satisfy ANI, * short calibration and long calibration. */ - ath9k_debug_samp_bb_mac(sc); cal_interval = ATH_LONG_CALINTERVAL; if (sc->sc_ah->config.enable_ani) cal_interval = min(cal_interval, -- cgit v1.2.3 From f761b6947dde42890beea59b020e1be87491809e Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 20 Jun 2012 11:47:26 -0500 Subject: rtlwifi: rtl8192se: Fix gcc 4.7.x warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.7.x, the following warning is issued as the routine that sets the array has the possibility of not initializing the values: CC [M] drivers/net/wireless/rtlwifi/rtl8192se/phy.o drivers/net/wireless/rtlwifi/rtl8192se/phy.c: In function ‘rtl92s_phy_set_txpower’: drivers/net/wireless/rtlwifi/rtl8192se/phy.c:1268:23: warning: ‘ofdmpowerLevel[0]’ may be used uninitialized in this function [-Wuninitialized] Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192se/phy.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c index 8d7099bc472c..b917a2a3caf7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c @@ -1247,6 +1247,9 @@ static void _rtl92s_phy_get_txpower_index(struct ieee80211_hw *hw, u8 channel, /* Read HT 40 OFDM TX power */ ofdmpowerLevel[0] = rtlefuse->txpwrlevel_ht40_2s[0][index]; ofdmpowerLevel[1] = rtlefuse->txpwrlevel_ht40_2s[1][index]; + } else { + ofdmpowerLevel[0] = 0; + ofdmpowerLevel[1] = 0; } } -- cgit v1.2.3 From 1aa2d1daf067c8c9e625449e2e6f54caa3e34023 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Wed, 20 Jun 2012 06:04:26 +0000 Subject: can: c_can_pci: fix compilation on non HAVE_CLK archs In commit: 5b92da0 c_can_pci: generic module for C_CAN/D_CAN on PCI the c_can_pci driver has been added. It uses clk_*() functions resulting in a link error on archs without clock support. This patch removed these clk_() functions as these parts of the driver are not tested. Cc: Federico Vaga Signed-off-by: Marc Kleine-Budde Signed-off-by: David S. Miller --- drivers/net/can/c_can/c_can_pci.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index 914aecfa09a9..1011146ea513 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,7 @@ struct c_can_pci_data { enum c_can_dev_id type; /* Set the register alignment in the memory */ enum c_can_pci_reg_align reg_align; - /* Set the frequency if clk is not usable */ + /* Set the frequency */ unsigned int freq; }; @@ -71,7 +70,6 @@ static int __devinit c_can_pci_probe(struct pci_dev *pdev, struct c_can_priv *priv; struct net_device *dev; void __iomem *addr; - struct clk *clk; int ret; ret = pci_enable_device(pdev); @@ -113,18 +111,11 @@ static int __devinit c_can_pci_probe(struct pci_dev *pdev, priv->base = addr; if (!c_can_pci_data->freq) { - /* get the appropriate clk */ - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "no clock defined\n"); - ret = -ENODEV; - goto out_free_c_can; - } - priv->can.clock.freq = clk_get_rate(clk); - priv->priv = clk; + dev_err(&pdev->dev, "no clock frequency defined\n"); + ret = -ENODEV; + goto out_free_c_can; } else { priv->can.clock.freq = c_can_pci_data->freq; - priv->priv = NULL; } /* Configure CAN type */ @@ -138,7 +129,7 @@ static int __devinit c_can_pci_probe(struct pci_dev *pdev, break; default: ret = -EINVAL; - goto out_free_clock; + goto out_free_c_can; } /* Configure access to registers */ @@ -153,14 +144,14 @@ static int __devinit c_can_pci_probe(struct pci_dev *pdev, break; default: ret = -EINVAL; - goto out_free_clock; + goto out_free_c_can; } ret = register_c_can_dev(dev); if (ret) { dev_err(&pdev->dev, "registering %s failed (err=%d)\n", KBUILD_MODNAME, ret); - goto out_free_clock; + goto out_free_c_can; } dev_dbg(&pdev->dev, "%s device registered (regs=%p, irq=%d)\n", @@ -168,9 +159,6 @@ static int __devinit c_can_pci_probe(struct pci_dev *pdev, return 0; -out_free_clock: - if (priv->priv) - clk_put(priv->priv); out_free_c_can: pci_set_drvdata(pdev, NULL); free_c_can_dev(dev); @@ -193,9 +181,6 @@ static void __devexit c_can_pci_remove(struct pci_dev *pdev) unregister_c_can_dev(dev); - if (priv->priv) - clk_put(priv->priv); - pci_set_drvdata(pdev, NULL); free_c_can_dev(dev); -- cgit v1.2.3 From b443a2307d3ade1520daa15d79ef97ad00641504 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 20 Jun 2012 05:32:00 +0000 Subject: team: use rcu_access_pointer to access RCU pointer by writer Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team_mode_activebackup.c | 7 +++++-- drivers/net/team/team_mode_loadbalance.c | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index 2fe02a8713ea..c9e7621b4480 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -61,8 +61,11 @@ static void ab_port_leave(struct team *team, struct team_port *port) static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) { - if (ab_priv(team)->active_port) - ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex; + struct team_port *active_port; + + active_port = rcu_access_pointer(ab_priv(team)->active_port); + if (active_port) + ctx->data.u32_val = active_port->dev->ifindex; else ctx->data.u32_val = 0; return 0; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 45cc0951aa48..b4475a5d2d4b 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -96,7 +96,7 @@ static void lb_tx_hash_to_port_mapping_null_port(struct team *team, struct lb_port_mapping *pm; pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; - if (pm->port == port) { + if (rcu_access_pointer(pm->port) == port) { rcu_assign_pointer(pm->port, NULL); team_option_inst_set_change(pm->opt_inst_info); changed = true; @@ -292,7 +292,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) if (lb_priv->ex->orig_fprog) { /* Clear old filter data */ __fprog_destroy(lb_priv->ex->orig_fprog); - sk_unattached_filter_destroy(lb_priv->fp); + sk_unattached_filter_destroy(rcu_access_pointer(lb_priv->fp)); } rcu_assign_pointer(lb_priv->fp, fp); @@ -303,9 +303,11 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) static int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx) { struct lb_priv *lb_priv = get_lb_priv(team); + lb_select_tx_port_func_t *func; char *name; - name = lb_select_tx_port_get_name(lb_priv->select_tx_port_func); + func = rcu_access_pointer(lb_priv->select_tx_port_func); + name = lb_select_tx_port_get_name(func); BUG_ON(!name); ctx->data.str_val = name; return 0; -- cgit v1.2.3 From d8dbd96e507e1f1133d346be123e7be4b16b72c3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 20 Jun 2012 05:32:01 +0000 Subject: team: use RCU_INIT_POINTER for NULL assignment of RCU pointer Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team_mode_loadbalance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index b4475a5d2d4b..c385b45cad18 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -97,7 +97,7 @@ static void lb_tx_hash_to_port_mapping_null_port(struct team *team, pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; if (rcu_access_pointer(pm->port) == port) { - rcu_assign_pointer(pm->port, NULL); + RCU_INIT_POINTER(pm->port, NULL); team_option_inst_set_change(pm->opt_inst_info); changed = true; } -- cgit v1.2.3 From f643776e4d1906ceff59f18315d6aba8e85db343 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 20 Jun 2012 14:28:58 -0700 Subject: team: Revert previous two changes. I didn't notice that these were superceded by a more uptodate version of the changes. Signed-off-by: David S. Miller --- drivers/net/team/team_mode_activebackup.c | 7 ++----- drivers/net/team/team_mode_loadbalance.c | 10 ++++------ 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index c9e7621b4480..2fe02a8713ea 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -61,11 +61,8 @@ static void ab_port_leave(struct team *team, struct team_port *port) static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) { - struct team_port *active_port; - - active_port = rcu_access_pointer(ab_priv(team)->active_port); - if (active_port) - ctx->data.u32_val = active_port->dev->ifindex; + if (ab_priv(team)->active_port) + ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex; else ctx->data.u32_val = 0; return 0; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index c385b45cad18..45cc0951aa48 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -96,8 +96,8 @@ static void lb_tx_hash_to_port_mapping_null_port(struct team *team, struct lb_port_mapping *pm; pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; - if (rcu_access_pointer(pm->port) == port) { - RCU_INIT_POINTER(pm->port, NULL); + if (pm->port == port) { + rcu_assign_pointer(pm->port, NULL); team_option_inst_set_change(pm->opt_inst_info); changed = true; } @@ -292,7 +292,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) if (lb_priv->ex->orig_fprog) { /* Clear old filter data */ __fprog_destroy(lb_priv->ex->orig_fprog); - sk_unattached_filter_destroy(rcu_access_pointer(lb_priv->fp)); + sk_unattached_filter_destroy(lb_priv->fp); } rcu_assign_pointer(lb_priv->fp, fp); @@ -303,11 +303,9 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) static int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx) { struct lb_priv *lb_priv = get_lb_priv(team); - lb_select_tx_port_func_t *func; char *name; - func = rcu_access_pointer(lb_priv->select_tx_port_func); - name = lb_select_tx_port_get_name(func); + name = lb_select_tx_port_get_name(lb_priv->select_tx_port_func); BUG_ON(!name); ctx->data.str_val = name; return 0; -- cgit v1.2.3 From 6dab015cf8c9d2fabb13d0332998bc440e9c6555 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 20 Jun 2012 08:39:39 +0000 Subject: team: do RCU update path fixups Use rcu_access_pointer and rcu_dereference_protected to access RCU pointer by updater. Use RCU_INIT_POINTER for NULL assignment of RCU pointer. Signed-off-by: Jiri Pirko Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/team/team_mode_activebackup.c | 8 ++++++-- drivers/net/team/team_mode_loadbalance.c | 14 ++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c index 2fe02a8713ea..253b8a5f3427 100644 --- a/drivers/net/team/team_mode_activebackup.c +++ b/drivers/net/team/team_mode_activebackup.c @@ -61,8 +61,12 @@ static void ab_port_leave(struct team *team, struct team_port *port) static int ab_active_port_get(struct team *team, struct team_gsetter_ctx *ctx) { - if (ab_priv(team)->active_port) - ctx->data.u32_val = ab_priv(team)->active_port->dev->ifindex; + struct team_port *active_port; + + active_port = rcu_dereference_protected(ab_priv(team)->active_port, + lockdep_is_held(&team->lock)); + if (active_port) + ctx->data.u32_val = active_port->dev->ifindex; else ctx->data.u32_val = 0; return 0; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index 45cc0951aa48..c92fa02d6a63 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -96,8 +96,8 @@ static void lb_tx_hash_to_port_mapping_null_port(struct team *team, struct lb_port_mapping *pm; pm = &lb_priv->ex->tx_hash_to_port_mapping[i]; - if (pm->port == port) { - rcu_assign_pointer(pm->port, NULL); + if (rcu_access_pointer(pm->port) == port) { + RCU_INIT_POINTER(pm->port, NULL); team_option_inst_set_change(pm->opt_inst_info); changed = true; } @@ -274,6 +274,7 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) { struct lb_priv *lb_priv = get_lb_priv(team); struct sk_filter *fp = NULL; + struct sk_filter *orig_fp; struct sock_fprog *fprog = NULL; int err; @@ -292,7 +293,9 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) if (lb_priv->ex->orig_fprog) { /* Clear old filter data */ __fprog_destroy(lb_priv->ex->orig_fprog); - sk_unattached_filter_destroy(lb_priv->fp); + orig_fp = rcu_dereference_protected(lb_priv->fp, + lockdep_is_held(&team->lock)); + sk_unattached_filter_destroy(orig_fp); } rcu_assign_pointer(lb_priv->fp, fp); @@ -303,9 +306,12 @@ static int lb_bpf_func_set(struct team *team, struct team_gsetter_ctx *ctx) static int lb_tx_method_get(struct team *team, struct team_gsetter_ctx *ctx) { struct lb_priv *lb_priv = get_lb_priv(team); + lb_select_tx_port_func_t *func; char *name; - name = lb_select_tx_port_get_name(lb_priv->select_tx_port_func); + func = rcu_dereference_protected(lb_priv->select_tx_port_func, + lockdep_is_held(&team->lock)); + name = lb_select_tx_port_get_name(func); BUG_ON(!name); ctx->data.str_val = name; return 0; -- cgit v1.2.3 From 8291550f8479fde2cee571d1b367e6918819f189 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 19 Jun 2012 07:35:34 -0600 Subject: PCI: fix upstream P2P bridge checks when enabling OBFF and LTR pci_enable_obff() and pci_enable_ltr() incorrectly check "dev->bus" instead of "dev->bus->self" to determine whether the upstream device is a P2P bridge or a host bridge. For devices on the root bus, the upstream device is a host bridge, "dev->bus != NULL" and "dev->bus->self == NULL", and we panic with a null pointer dereference. These functions should previously have panicked when called on devices supporting OBFF or LTR, so they should be regarded as untested. Found by Coverity (CID 143038 and CID 143039). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8f4a5ea543fc..b9e93cf1eb40 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2108,7 +2108,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type) return -ENOTSUPP; /* no OBFF support at all */ /* Make sure the topology supports OBFF as well */ - if (dev->bus) { + if (dev->bus->self) { ret = pci_enable_obff(dev->bus->self, type); if (ret) return ret; @@ -2215,7 +2215,7 @@ int pci_enable_ltr(struct pci_dev *dev) return -EINVAL; /* Enable upstream ports first */ - if (dev->bus) { + if (dev->bus->self) { ret = pci_enable_ltr(dev->bus->self); if (ret) return ret; -- cgit v1.2.3 From 8f38eaca55d0fab7499b33adb1dec33e16de5abb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 19 Jun 2012 07:45:44 -0600 Subject: PCI: fix P2P bridge I/O port window sign extension On P2P bridges with 32-bit I/O decoding, we incorrectly sign-extended windows starting at 0x80000000 or above. In "base |= (io_base_hi << 16)", "io_base_hi" is promoted to a signed int before being extended to an unsigned long. This would cause a window starting at I/O address 0x80000000 to be treated as though it started at 0xffffffff80008000 instead, which should cause "no compatible bridge window" errors when we enumerate devices using that I/O space. The mmio and mmio_pref casts are not strictly necessary, but without them, correctness depends on the types of the PCI_MEMORY_RANGE_MASK and PCI_PREF_RANGE_MASK constants, which are not obvious from reading the local code. Found by Coverity (CID 138747 and CID 138748). Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 658ac977cb56..a7a504fc82b9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -281,10 +281,11 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { u16 io_base_hi, io_limit_hi; + pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); - base |= (io_base_hi << 16); - limit |= (io_limit_hi << 16); + base |= ((unsigned long) io_base_hi << 16); + limit |= ((unsigned long) io_limit_hi << 16); } if (base && base <= limit) { @@ -312,8 +313,8 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); - base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; - limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; + base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; + limit = ((unsigned long) mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; region.start = base; @@ -334,11 +335,12 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); - base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; - limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; + base = ((unsigned long) mem_base_lo & PCI_PREF_RANGE_MASK) << 16; + limit = ((unsigned long) mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { u32 mem_base_hi, mem_limit_hi; + pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); @@ -349,8 +351,8 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) */ if (mem_base_hi <= mem_limit_hi) { #if BITS_PER_LONG == 64 - base |= ((long) mem_base_hi) << 32; - limit |= ((long) mem_limit_hi) << 32; + base |= ((unsigned long) mem_base_hi) << 32; + limit |= ((unsigned long) mem_limit_hi) << 32; #else if (mem_base_hi || mem_limit_hi) { dev_err(&dev->dev, "can't handle 64-bit " -- cgit v1.2.3 From 67454b6602c3519d15193630e6765a5f02f39160 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 20 Jun 2012 16:11:45 -0600 Subject: PCI: shpchp: remove dead code "slots_not_empty" is initialized to zero and can't be set again before reaching this point, so this return statement is dead. Remove it. Found by Coverity (CID 114324). Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/shpchp_ctrl.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/shpchp_ctrl.c b/drivers/pci/hotplug/shpchp_ctrl.c index b00b09bdd38a..f9b5a52e4115 100644 --- a/drivers/pci/hotplug/shpchp_ctrl.c +++ b/drivers/pci/hotplug/shpchp_ctrl.c @@ -262,9 +262,6 @@ static int board_added(struct slot *p_slot) } if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { - if (slots_not_empty) - return WRONG_BUS_FREQUENCY; - if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { ctrl_err(ctrl, "%s: Issue of set bus speed mode command" " failed\n", __func__); -- cgit v1.2.3 From dfb117b3e50c52c7b3416db4a4569224b8db80bb Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 20 Jun 2012 16:18:29 -0600 Subject: PCI: acpiphp: check whether _ADR evaluation succeeded Check whether we evaluated _ADR successfully. Previously we ignored failure, so we would have used garbage data from the stack as the device and function number. We return AE_OK so that we ignore only this slot and continue looking for other slots. Found by Coverity (CID 113981). Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp_glue.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 806c44fa645a..09bf37721842 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -132,6 +132,15 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) return AE_OK; + status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); + if (ACPI_FAILURE(status)) { + warn("can't evaluate _ADR (%#x)\n", status); + return AE_OK; + } + + device = (adr >> 16) & 0xffff; + function = adr & 0xffff; + pdev = pbus->self; if (pdev && pci_is_pcie(pdev)) { tmp = acpi_find_root_bridge_handle(pdev); @@ -144,10 +153,6 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) } } - acpi_evaluate_integer(handle, "_ADR", NULL, &adr); - device = (adr >> 16) & 0xffff; - function = adr & 0xffff; - newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL); if (!newfunc) return AE_NO_MEMORY; -- cgit v1.2.3 From 809a3bf9f34cb6d0c0383b31b3495fa1ed3508a6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 20 Jun 2012 16:41:16 -0600 Subject: PCI: remove useless pcix_set_mmrbc() dev->bus check For a valid pci_dev, dev->bus != NULL always, so remove this unnecessary test. Found by Coverity (CID 101680). Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b9e93cf1eb40..7f1310e58534 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3395,8 +3395,7 @@ int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc) o = (cmd & PCI_X_CMD_MAX_READ) >> 2; if (o != v) { - if (v > o && dev->bus && - (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC)) + if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC)) return -EIO; cmd &= ~PCI_X_CMD_MAX_READ; -- cgit v1.2.3 From 745664ebe0a97237fb026d2c0aa1c689f9395f0c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:01 +0300 Subject: dw_dmac: fix constant in the comment Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index f298f69ecbf9..e248481ad5f1 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -82,7 +82,7 @@ struct dw_dma_regs { DW_REG(ID); DW_REG(TEST); - /* optional encoded params, 0x3c8..0x3 */ + /* optional encoded params, 0x3c8..0x3f7 */ }; /* Bitfields in CTL_LO */ -- cgit v1.2.3 From 2f45d613ffae00d3355de7871d9cbfd0f2292971 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:02 +0300 Subject: dw_dmac: use proper casting to print dma_addr_t values dma_addr_t is sometimes 32 bit and sometimes 64. We normally cast them to unsigned long long for printk(). Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index e23dc82d43ac..9794f00810f6 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -337,7 +337,8 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) return; } - dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%x\n", llp); + dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%llx\n", + (unsigned long long)llp); list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { /* check first descriptors addr */ @@ -387,8 +388,10 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) static void dwc_dump_lli(struct dw_dma_chan *dwc, struct dw_lli *lli) { dev_printk(KERN_CRIT, chan2dev(&dwc->chan), - " desc: s0x%x d0x%x l0x%x c0x%x:%x\n", - lli->sar, lli->dar, lli->llp, + " desc: s0x%llx d0x%llx l0x%llx c0x%x:%x\n", + (unsigned long long)lli->sar, + (unsigned long long)lli->dar, + (unsigned long long)lli->llp, lli->ctlhi, lli->ctllo); } @@ -627,8 +630,10 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, unsigned int dst_width; u32 ctllo; - dev_vdbg(chan2dev(chan), "prep_dma_memcpy d0x%x s0x%x l0x%zx f0x%lx\n", - dest, src, len, flags); + dev_vdbg(chan2dev(chan), + "prep_dma_memcpy d0x%llx s0x%llx l0x%zx f0x%lx\n", + (unsigned long long)dest, (unsigned long long)src, + len, flags); if (unlikely(!len)) { dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n"); @@ -1308,9 +1313,9 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, dma_sync_single_for_device(chan2parent(chan), last->txd.phys, sizeof(last->lli), DMA_TO_DEVICE); - dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%08x len %zu " - "period %zu periods %d\n", buf_addr, buf_len, - period_len, periods); + dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%llx len %zu " + "period %zu periods %d\n", (unsigned long long)buf_addr, + buf_len, period_len, periods); cdesc->periods = periods; dwc->cdesc = cdesc; -- cgit v1.2.3 From 1d4554376549d50e8555e6617a4005623ea91788 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:03 +0300 Subject: dw_dmac: introduce dwc_dump_chan_regs to dump registers There is three places where values of the most significant registers were printed. Make such piece of code as separate function. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 9794f00810f6..467599a4b9b6 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -191,6 +191,19 @@ static void dwc_initialize(struct dw_dma_chan *dwc) /*----------------------------------------------------------------------*/ +static void dwc_dump_chan_regs(struct dw_dma_chan *dwc) +{ + dev_err(chan2dev(&dwc->chan), + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); +} + +/*----------------------------------------------------------------------*/ + /* Called with dwc->lock held and bh disabled */ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) { @@ -200,13 +213,7 @@ static void dwc_dostart(struct dw_dma_chan *dwc, struct dw_desc *first) if (dma_readl(dw, CH_EN) & dwc->mask) { dev_err(chan2dev(&dwc->chan), "BUG: Attempted to start non-idle channel\n"); - dev_err(chan2dev(&dwc->chan), - " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", - channel_readl(dwc, SAR), - channel_readl(dwc, DAR), - channel_readl(dwc, LLP), - channel_readl(dwc, CTL_HI), - channel_readl(dwc, CTL_LO)); + dwc_dump_chan_regs(dwc); /* The tasklet will hopefully advance the queue... */ return; @@ -490,13 +497,7 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, spin_lock_irqsave(&dwc->lock, flags); - dev_err(chan2dev(&dwc->chan), - " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", - channel_readl(dwc, SAR), - channel_readl(dwc, DAR), - channel_readl(dwc, LLP), - channel_readl(dwc, CTL_HI), - channel_readl(dwc, CTL_LO)); + dwc_dump_chan_regs(dwc); channel_clear_bit(dw, CH_EN, dwc->mask); while (dma_readl(dw, CH_EN) & dwc->mask) @@ -1131,13 +1132,7 @@ int dw_dma_cyclic_start(struct dma_chan *chan) if (dma_readl(dw, CH_EN) & dwc->mask) { dev_err(chan2dev(&dwc->chan), "BUG: Attempted to start non-idle channel\n"); - dev_err(chan2dev(&dwc->chan), - " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", - channel_readl(dwc, SAR), - channel_readl(dwc, DAR), - channel_readl(dwc, LLP), - channel_readl(dwc, CTL_HI), - channel_readl(dwc, CTL_LO)); + dwc_dump_chan_regs(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return -EBUSY; } -- cgit v1.2.3 From 2ab3727634bf88583b40541ea32df6dadfb8cc6c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:04 +0300 Subject: dw_dmac: print correct number of scanned descriptors In case the first descriptor we found is available, the counter still remains 0 value which is wrong. This patch fixes the counter behaviour. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 467599a4b9b6..a37608d02a2c 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -105,13 +105,13 @@ static struct dw_desc *dwc_desc_get(struct dw_dma_chan *dwc) spin_lock_irqsave(&dwc->lock, flags); list_for_each_entry_safe(desc, _desc, &dwc->free_list, desc_node) { + i++; if (async_tx_test_ack(&desc->txd)) { list_del(&desc->desc_node); ret = desc; break; } dev_dbg(chan2dev(&dwc->chan), "desc %p not ACKed\n", desc); - i++; } spin_unlock_irqrestore(&dwc->lock, flags); -- cgit v1.2.3 From 2e4c364e151ba9085c77836733af36d3bd0e8ffd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:05 +0300 Subject: dw_dmac: use __func__ constant in the debug prints Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index a37608d02a2c..41f03f37df2a 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -344,7 +344,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) return; } - dev_vdbg(chan2dev(&dwc->chan), "scan_descriptors: llp=0x%llx\n", + dev_vdbg(chan2dev(&dwc->chan), "%s: llp=0x%llx\n", __func__, (unsigned long long)llp); list_for_each_entry_safe(desc, _desc, &dwc->active_list, desc_node) { @@ -531,7 +531,7 @@ static void dw_dma_tasklet(unsigned long data) status_xfer = dma_readl(dw, RAW.XFER); status_err = dma_readl(dw, RAW.ERROR); - dev_vdbg(dw->dma.dev, "tasklet: status_err=%x\n", status_err); + dev_vdbg(dw->dma.dev, "%s: status_err=%x\n", __func__, status_err); for (i = 0; i < dw->dma.chancnt; i++) { dwc = &dw->chan[i]; @@ -555,7 +555,7 @@ static irqreturn_t dw_dma_interrupt(int irq, void *dev_id) struct dw_dma *dw = dev_id; u32 status; - dev_vdbg(dw->dma.dev, "interrupt: status=0x%x\n", + dev_vdbg(dw->dma.dev, "%s: status=0x%x\n", __func__, dma_readl(dw, STATUS_INT)); /* @@ -601,12 +601,12 @@ static dma_cookie_t dwc_tx_submit(struct dma_async_tx_descriptor *tx) * for DMA. But this is hard to do in a race-free manner. */ if (list_empty(&dwc->active_list)) { - dev_vdbg(chan2dev(tx->chan), "tx_submit: started %u\n", + dev_vdbg(chan2dev(tx->chan), "%s: started %u\n", __func__, desc->txd.cookie); list_add_tail(&desc->desc_node, &dwc->active_list); dwc_dostart(dwc, dwc_first_active(dwc)); } else { - dev_vdbg(chan2dev(tx->chan), "tx_submit: queued %u\n", + dev_vdbg(chan2dev(tx->chan), "%s: queued %u\n", __func__, desc->txd.cookie); list_add_tail(&desc->desc_node, &dwc->queue); @@ -632,12 +632,12 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, u32 ctllo; dev_vdbg(chan2dev(chan), - "prep_dma_memcpy d0x%llx s0x%llx l0x%zx f0x%lx\n", + "%s: d0x%llx s0x%llx l0x%zx f0x%lx\n", __func__, (unsigned long long)dest, (unsigned long long)src, len, flags); if (unlikely(!len)) { - dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n"); + dev_dbg(chan2dev(chan), "%s: length is zero!\n", __func__); return NULL; } @@ -726,7 +726,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, struct scatterlist *sg; size_t total_len = 0; - dev_vdbg(chan2dev(chan), "prep_dma_slave\n"); + dev_vdbg(chan2dev(chan), "%s\n", __func__); if (unlikely(!dws || !sg_len)) return NULL; @@ -1020,7 +1020,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) int i; unsigned long flags; - dev_vdbg(chan2dev(chan), "alloc_chan_resources\n"); + dev_vdbg(chan2dev(chan), "%s\n", __func__); /* ASSERT: channel is idle */ if (dma_readl(dw, CH_EN) & dwc->mask) { @@ -1063,8 +1063,7 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) spin_unlock_irqrestore(&dwc->lock, flags); - dev_dbg(chan2dev(chan), - "alloc_chan_resources allocated %d descriptors\n", i); + dev_dbg(chan2dev(chan), "%s: allocated %d descriptors\n", __func__, i); return i; } @@ -1077,7 +1076,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) unsigned long flags; LIST_HEAD(list); - dev_dbg(chan2dev(chan), "free_chan_resources (descs allocated=%u)\n", + dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, dwc->descs_allocated); /* ASSERT: channel is idle */ @@ -1103,7 +1102,7 @@ static void dwc_free_chan_resources(struct dma_chan *chan) kfree(desc); } - dev_vdbg(chan2dev(chan), "free_chan_resources done\n"); + dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } /* --------------------- Cyclic DMA API extensions -------------------- */ @@ -1340,7 +1339,7 @@ void dw_dma_cyclic_free(struct dma_chan *chan) int i; unsigned long flags; - dev_dbg(chan2dev(&dwc->chan), "cyclic free\n"); + dev_dbg(chan2dev(&dwc->chan), "%s\n", __func__); if (!cdesc) return; -- cgit v1.2.3 From 11f932ec221ee07c3a9d49e83df7d44b13f8885f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:06 +0300 Subject: dw_dmac: disable dma in optimal way in probe The dw_dma_off call needs to have the all_chan_mask calculated. So, done this calculations before the call. Moreover, remove duplicate code that masks the DMA interrupts. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 41f03f37df2a..8a37f6d17b47 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1431,6 +1431,9 @@ static int __init dw_probe(struct platform_device *pdev) } clk_prepare_enable(dw->clk); + /* Calculate all channel mask before DMA setup */ + dw->all_chan_mask = (1 << pdata->nr_channels) - 1; + /* force dma off, just in case */ dw_dma_off(dw); @@ -1442,8 +1445,6 @@ static int __init dw_probe(struct platform_device *pdev) tasklet_init(&dw->tasklet, dw_dma_tasklet, (unsigned long)dw); - dw->all_chan_mask = (1 << pdata->nr_channels) - 1; - INIT_LIST_HEAD(&dw->dma.channels); for (i = 0; i < pdata->nr_channels; i++) { struct dw_dma_chan *dwc = &dw->chan[i]; @@ -1473,17 +1474,12 @@ static int __init dw_probe(struct platform_device *pdev) channel_clear_bit(dw, CH_EN, dwc->mask); } - /* Clear/disable all interrupts on all channels. */ + /* Clear all interrupts on all channels. */ dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); - channel_clear_bit(dw, MASK.XFER, dw->all_chan_mask); - channel_clear_bit(dw, MASK.SRC_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.DST_TRAN, dw->all_chan_mask); - channel_clear_bit(dw, MASK.ERROR, dw->all_chan_mask); - dma_cap_set(DMA_MEMCPY, dw->dma.cap_mask); dma_cap_set(DMA_SLAVE, dw->dma.cap_mask); if (pdata->is_private) -- cgit v1.2.3 From 236b106f1a66a272708e4960cde8cbcdd1611435 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:07 +0300 Subject: dw_dmac: disable BLOCK interrupts Just to be sure we are in known state we disable the BLOCK interupts. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 8a37f6d17b47..d3038a03503d 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1437,6 +1437,9 @@ static int __init dw_probe(struct platform_device *pdev) /* force dma off, just in case */ dw_dma_off(dw); + /* disable BLOCK interrupts as well */ + channel_clear_bit(dw, MASK.BLOCK, dw->all_chan_mask); + err = request_irq(irq, dw_dma_interrupt, 0, "dw_dmac", dw); if (err) goto err_irq; @@ -1476,6 +1479,7 @@ static int __init dw_probe(struct platform_device *pdev) /* Clear all interrupts on all channels. */ dma_writel(dw, CLEAR.XFER, dw->all_chan_mask); + dma_writel(dw, CLEAR.BLOCK, dw->all_chan_mask); dma_writel(dw, CLEAR.SRC_TRAN, dw->all_chan_mask); dma_writel(dw, CLEAR.DST_TRAN, dw->all_chan_mask); dma_writel(dw, CLEAR.ERROR, dw->all_chan_mask); -- cgit v1.2.3 From 4c2d56c574db84ef6e0101f28a37c044e89b0302 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:08 +0300 Subject: dw_dmac: introduce dwc_fast_fls() There were three places where such function is used. We still avoid to use native fls() because in one case it requires to use 64bit version which is suboptimal in our case. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index d3038a03503d..6b456f042557 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -191,6 +191,21 @@ static void dwc_initialize(struct dw_dma_chan *dwc) /*----------------------------------------------------------------------*/ +static inline unsigned int dwc_fast_fls(unsigned long long v) +{ + /* + * We can be a lot more clever here, but this should take care + * of the most common optimization. + */ + if (!(v & 7)) + return 3; + else if (!(v & 3)) + return 2; + else if (!(v & 1)) + return 1; + return 0; +} + static void dwc_dump_chan_regs(struct dw_dma_chan *dwc) { dev_err(chan2dev(&dwc->chan), @@ -641,18 +656,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, return NULL; } - /* - * We can be a lot more clever here, but this should take care - * of the most common optimization. - */ - if (!((src | dest | len) & 7)) - src_width = dst_width = 3; - else if (!((src | dest | len) & 3)) - src_width = dst_width = 2; - else if (!((src | dest | len) & 1)) - src_width = dst_width = 1; - else - src_width = dst_width = 0; + src_width = dst_width = dwc_fast_fls(src | dest | len); ctllo = DWC_DEFAULT_CTLLO(chan) | DWC_CTLL_DST_WIDTH(dst_width) @@ -752,14 +756,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, mem = sg_dma_address(sg); len = sg_dma_len(sg); - if (!((mem | len) & 7)) - mem_width = 3; - else if (!((mem | len) & 3)) - mem_width = 2; - else if (!((mem | len) & 1)) - mem_width = 1; - else - mem_width = 0; + mem_width = dwc_fast_fls(mem | len); slave_sg_todev_fill_desc: desc = dwc_desc_get(dwc); @@ -819,14 +816,7 @@ slave_sg_todev_fill_desc: mem = sg_dma_address(sg); len = sg_dma_len(sg); - if (!((mem | len) & 7)) - mem_width = 3; - else if (!((mem | len) & 3)) - mem_width = 2; - else if (!((mem | len) & 1)) - mem_width = 1; - else - mem_width = 0; + mem_width = dwc_fast_fls(mem | len); slave_sg_fromdev_fill_desc: desc = dwc_desc_get(dwc); -- cgit v1.2.3 From 0272e93f364eac1a30f2831adcaca3dd633d5f14 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:34:09 +0300 Subject: dw_dmac: move from __init to __devinit We usually have more than one DMA device. Thus, the probe function should serve for all of them in case when the driver is built as a module. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 6b456f042557..83f63b38dcc0 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1375,7 +1375,7 @@ static void dw_dma_off(struct dw_dma *dw) dw->chan[i].initialized = false; } -static int __init dw_probe(struct platform_device *pdev) +static int __devinit dw_probe(struct platform_device *pdev) { struct dw_dma_platform_data *pdata; struct resource *io; @@ -1512,7 +1512,7 @@ err_kfree: return err; } -static int __exit dw_remove(struct platform_device *pdev) +static int __devexit dw_remove(struct platform_device *pdev) { struct dw_dma *dw = platform_get_drvdata(pdev); struct dw_dma_chan *dwc, *_dwc; @@ -1591,7 +1591,7 @@ MODULE_DEVICE_TABLE(of, dw_dma_id_table); #endif static struct platform_driver dw_driver = { - .remove = __exit_p(dw_remove), + .remove = __devexit_p(dw_remove), .shutdown = dw_shutdown, .driver = { .name = "dw_dmac", -- cgit v1.2.3 From 3f9362078b101bd57bfed635e49c8ecb8b6d465d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 19 Jun 2012 13:46:32 +0300 Subject: dw_dmac: introduce dwc_chan_disable This piece of code is used often. Make it as a separate function. Signed-off-by: Andy Shevchenko Acked-by: Viresh Kumar Signed-off-by: Vinod Koul --- drivers/dma/dw_dmac.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 83f63b38dcc0..85ed5f20e027 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -217,6 +217,14 @@ static void dwc_dump_chan_regs(struct dw_dma_chan *dwc) channel_readl(dwc, CTL_LO)); } + +static inline void dwc_chan_disable(struct dw_dma *dw, struct dw_dma_chan *dwc) +{ + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); +} + /*----------------------------------------------------------------------*/ /* Called with dwc->lock held and bh disabled */ @@ -312,9 +320,7 @@ static void dwc_complete_all(struct dw_dma *dw, struct dw_dma_chan *dwc) "BUG: XFER bit set, but channel not idle!\n"); /* Try to continue after resetting the channel... */ - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); + dwc_chan_disable(dw, dwc); } /* @@ -396,9 +402,7 @@ static void dwc_scan_descriptors(struct dw_dma *dw, struct dw_dma_chan *dwc) "BUG: All descriptors done, but channel not idle!\n"); /* Try to continue after resetting the channel... */ - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); + dwc_chan_disable(dw, dwc); if (!list_empty(&dwc->queue)) { list_move(dwc->queue.next, &dwc->active_list); @@ -514,9 +518,7 @@ static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, dwc_dump_chan_regs(dwc); - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); + dwc_chan_disable(dw, dwc); /* make sure DMA does not restart by loading a new list */ channel_writel(dwc, LLP, 0); @@ -946,9 +948,7 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, } else if (cmd == DMA_TERMINATE_ALL) { spin_lock_irqsave(&dwc->lock, flags); - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); + dwc_chan_disable(dw, dwc); dwc->paused = false; @@ -1156,9 +1156,7 @@ void dw_dma_cyclic_stop(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); + dwc_chan_disable(dw, dwc); spin_unlock_irqrestore(&dwc->lock, flags); } @@ -1336,9 +1334,7 @@ void dw_dma_cyclic_free(struct dma_chan *chan) spin_lock_irqsave(&dwc->lock, flags); - channel_clear_bit(dw, CH_EN, dwc->mask); - while (dma_readl(dw, CH_EN) & dwc->mask) - cpu_relax(); + dwc_chan_disable(dw, dwc); dma_writel(dw, CLEAR.ERROR, dwc->mask); dma_writel(dw, CLEAR.XFER, dwc->mask); -- cgit v1.2.3 From 3f8a9e76672202bfb55cc83b5fdc66306d3d170a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 20 Jun 2012 21:25:15 +0200 Subject: iwlwifi: fix radio reset scan dwell vs. quiet time My previous commit to shorten the radio reset time caused issues as the firmware checks the active dwell time against the quiet time, asserting that the dwell is >= quiet time. This isn't really needed in case of passive scanning like here, but of course we need to pass that check. To fix this, override the quiet time to be the same as the radio reset dwell time. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/scan.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index f5b1452a60b3..6633074258c7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -720,6 +720,12 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) switch (priv->scan_type) { case IWL_SCAN_RADIO_RESET: IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); + /* + * Override quiet time as firmware checks that active + * dwell is >= quiet; since we use passive scan it'll + * not actually be used. + */ + scan->quiet_time = cpu_to_le16(IWL_RADIO_RESET_DWELL_TIME); break; case IWL_SCAN_NORMAL: if (priv->scan_request->n_ssids) { -- cgit v1.2.3 From e9c03d18c4291c4351d6b9f74bd1278a761015a2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 20 Jun 2012 16:24:59 +0200 Subject: iwlwifi: increase scan timeout When the first interface is active, then scanning on it or the second interface can take a little longer than 7s (I observed around 8s.) Bump the timeout to 15s to avoid aborting a scan that is still running, just taking more time. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/agn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 6d102413dd94..9bb16bdf6d26 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -269,7 +269,7 @@ void iwl_scan_offchannel_skb_status(struct iwl_priv *priv); #define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ #define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ -#define IWL_SCAN_CHECK_WATCHDOG (HZ * 7) +#define IWL_SCAN_CHECK_WATCHDOG (HZ * 15) /* bt coex */ -- cgit v1.2.3 From e75dac921d88ac1fa1ad08686ab242556f8b888b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 20 Jun 2012 11:47:42 +0200 Subject: iwlwifi: limit mac_change_interface to BSS context Currently when mac80211 asks to change the interface type, we will accept it for both the BSS and PAN contexts. This is not terribly complicated today, but with the addition of the P2P Device abstraction the PAN context handling will get more complex, so restrict mac_change_interface to the BSS context. Also fix a small locking issue and use is_active instead of the vif pointer to check if the other context is activated, guarding exclusive interface types on the BSS context (IBSS) against the PAN context being used for something else. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/mac80211.c | 37 +++++++++++++---------------- 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index b581f035d4b0..b83ca358eb18 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1411,13 +1411,11 @@ static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, } static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p) + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - struct iwl_rxon_context *bss_ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_rxon_context *tmp; + struct iwl_rxon_context *ctx, *tmp; enum nl80211_iftype newviftype = newtype; u32 interface_modes; int err; @@ -1428,6 +1426,18 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); + ctx = iwl_rxon_ctx_from_vif(vif); + + /* + * To simplify this code, only support changes on the + * BSS context. The PAN context is usually reassigned + * by creating/removing P2P interfaces anyway. + */ + if (ctx->ctxid != IWL_RXON_CTX_BSS) { + err = -EBUSY; + goto out; + } + if (!ctx->vif || !iwl_is_ready_rf(priv)) { /* * Huh? But wait ... this can maybe happen when @@ -1437,32 +1447,19 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, goto out; } + /* Check if the switch is supported in the same context */ interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; - if (!(interface_modes & BIT(newtype))) { err = -EBUSY; goto out; } - /* - * Refuse a change that should be done by moving from the PAN - * context to the BSS context instead, if the BSS context is - * available and can support the new interface type. - */ - if (ctx->ctxid == IWL_RXON_CTX_PAN && !bss_ctx->vif && - (bss_ctx->interface_modes & BIT(newtype) || - bss_ctx->exclusive_interface_modes & BIT(newtype))) { - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - err = -EBUSY; - goto out; - } - if (ctx->exclusive_interface_modes & BIT(newtype)) { for_each_context(priv, tmp) { if (ctx == tmp) continue; - if (!tmp->vif) + if (!tmp->is_active) continue; /* -- cgit v1.2.3 From 01bc3a1400373a0f6bd79f2328eb822ca099c784 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 20 Jun 2012 22:40:10 +0800 Subject: regulator: tps65910: Convert to regulator_set_voltage_time_sel() Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index e1b01ec82301..373c529b379d 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -824,31 +824,6 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) return (LDO_MIN_VOLT + selector * step_mv) * 1000; } -static int tps65910_set_voltage_dcdc_time_sel(struct regulator_dev *dev, - unsigned int old_selector, unsigned int new_selector) -{ - int id = rdev_get_id(dev); - int old_volt, new_volt; - - old_volt = tps65910_list_voltage_dcdc(dev, old_selector); - if (old_volt < 0) - return old_volt; - - new_volt = tps65910_list_voltage_dcdc(dev, new_selector); - if (new_volt < 0) - return new_volt; - - /* VDD1 and VDD2 are 12.5mV/us, VDDCTRL is 100mV/20us */ - switch (id) { - case TPS65910_REG_VDD1: - case TPS65910_REG_VDD2: - return DIV_ROUND_UP(abs(old_volt - new_volt), 12500); - case TPS65911_REG_VDDCTRL: - return DIV_ROUND_UP(abs(old_volt - new_volt), 5000); - } - return -EINVAL; -} - /* Regulator ops (except VRTC) */ static struct regulator_ops tps65910_ops_dcdc = { .is_enabled = regulator_is_enabled_regmap, @@ -859,7 +834,7 @@ static struct regulator_ops tps65910_ops_dcdc = { .get_mode = tps65910_get_mode, .get_voltage_sel = tps65910_get_voltage_dcdc_sel, .set_voltage_sel = tps65910_set_voltage_dcdc_sel, - .set_voltage_time_sel = tps65910_set_voltage_dcdc_time_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, .list_voltage = tps65910_list_voltage_dcdc, }; @@ -1236,11 +1211,14 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->desc[i].ops = &tps65910_ops_dcdc; pmic->desc[i].n_voltages = VDD1_2_NUM_VOLT_FINE * VDD1_2_NUM_VOLT_COARSE; + pmic->desc[i].ramp_delay = 12500; } else if (i == TPS65910_REG_VDD3) { - if (tps65910_chip_id(tps65910) == TPS65910) + if (tps65910_chip_id(tps65910) == TPS65910) { pmic->desc[i].ops = &tps65910_ops_vdd3; - else + } else { pmic->desc[i].ops = &tps65910_ops_dcdc; + pmic->desc[i].ramp_delay = 5000; + } } else { if (tps65910_chip_id(tps65910) == TPS65910) pmic->desc[i].ops = &tps65910_ops; -- cgit v1.2.3 From dc2060cfe2aa2b62ba2f4a19fce0c20eceefe7ca Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Jun 2012 10:35:58 +0100 Subject: regulator: lp872x: Depend on I2C=y Kconfig doesn't do the right thing for us here. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e056c09661a2..27fe52f592d9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -234,7 +234,7 @@ config REGULATOR_LP3972 config REGULATOR_LP872X bool "TI/National Semiconductor LP8720/LP8725 voltage regulators" - depends on I2C + depends on I2C=y select REGMAP_I2C help This driver supports LP8720/LP8725 PMIC -- cgit v1.2.3 From c954910bc4501447cc647d5fca5bd0d9439e177d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 6 Jun 2012 10:48:56 +0300 Subject: wlcore: suppress error message on Rx BA session removal The ampdu_action() function is called on the reconfig() path to remove existing Rx BA sessions. Since these don't exist for the low level driver, we output an error message. Turn the message into a debug message for now, until the mac80211 reconfig flow is changed. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 8eefcd7505e2..22b342f64b7b 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4366,9 +4366,14 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_RX_STOP: if (!(*ba_bitmap & BIT(tid))) { - ret = -EINVAL; - wl1271_error("no active RX BA session on tid: %d", + /* + * this happens on reconfig - so only output a debug + * message for now, and don't fail the function. + */ + wl1271_debug(DEBUG_MAC80211, + "no active RX BA session on tid: %d", tid); + ret = 0; break; } -- cgit v1.2.3 From 26b5858a67e4316ecd8159e2c0dc5591ef68226a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 28 Feb 2012 19:13:28 +0200 Subject: wlcore: add a debugfs entry to allow changing the sleep mode by hand For FW debugging purposes, we may need to change the sleep mode (aka. sleep_auth) by hand, and set it to the mode we want. To allow this, a debugfs entry is added. Now we store the sleep_auth value that has been set and use that instead of the quirk to decide whether we should enter ELP or not. Signed-off-by: Luciano Coelho Signed-off-by: Arik Nemtsov --- drivers/net/wireless/ti/wlcore/acx.c | 3 ++ drivers/net/wireless/ti/wlcore/acx.h | 2 ++ drivers/net/wireless/ti/wlcore/debugfs.c | 58 ++++++++++++++++++++++++++++++++ drivers/net/wireless/ti/wlcore/main.c | 3 ++ drivers/net/wireless/ti/wlcore/ps.c | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 3 ++ 6 files changed, 70 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index b9ec42c83757..b56217f9bfd4 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -81,7 +81,10 @@ int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth) auth->sleep_auth = sleep_auth; ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); + if (ret < 0) + goto out; + wl->sleep_auth = sleep_auth; out: kfree(auth); return ret; diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index c0181258b722..168e0464411d 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -118,6 +118,8 @@ enum wl1271_psm_mode { /* Extreme low power */ WL1271_PSM_ELP = 2, + + WL1271_PSM_MAX = WL1271_PSM_ELP, }; struct acx_sleep_auth { diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 689a847005c9..91e43def013d 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -963,6 +963,63 @@ static const struct file_operations fw_stats_raw_ops = { .llseek = default_llseek, }; +static ssize_t sleep_auth_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + return wl1271_format_buffer(user_buf, count, + ppos, "%d\n", + wl->sleep_auth); +} + +static ssize_t sleep_auth_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + unsigned long value; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value in sleep_auth"); + return -EINVAL; + } + + if (value < 0 || value > WL1271_PSM_MAX) { + wl1271_warning("sleep_auth must be between 0 and %d", + WL1271_PSM_MAX); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_acx_sleep_auth(wl, value); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations sleep_auth_ops = { + .read = sleep_auth_read, + .write = sleep_auth_write, + .open = simple_open, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -988,6 +1045,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(irq_blk_threshold, rootdir); DEBUGFS_ADD(irq_timeout, rootdir); DEBUGFS_ADD(fw_stats_raw, rootdir); + DEBUGFS_ADD(sleep_auth, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 22b342f64b7b..7677cd55f83e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1082,6 +1082,7 @@ int wl1271_plt_stop(struct wl1271 *wl) mutex_lock(&wl->mutex); wl1271_power_off(wl); wl->flags = 0; + wl->sleep_auth = WL1271_PSM_CAM; wl->state = WL1271_STATE_OFF; wl->plt = false; wl->rx_counter = 0; @@ -1740,6 +1741,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; wl->sched_scanning = false; + wl->sleep_auth = WL1271_PSM_CAM; memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); @@ -5174,6 +5176,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) wl->channel_type = NL80211_CHAN_NO_HT; wl->flags = 0; wl->sg_enabled = true; + wl->sleep_auth = WL1271_PSM_CAM; wl->hw_pg_ver = -1; wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 47e81b32f7da..95d8797cfa28 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -76,7 +76,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) struct wl12xx_vif *wlvif; u32 timeout; - if (wl->quirks & WLCORE_QUIRK_NO_ELP) + if (wl->sleep_auth != WL1271_PSM_ELP) return; /* we shouldn't get consecutive sleep requests */ diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 761a72f4b8d1..eae8edcc5fc7 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -387,6 +387,9 @@ struct wl1271 { /* mutex for protecting the tx_flush function */ struct mutex flush_mutex; + + /* sleep auth value currently configured to FW */ + int sleep_auth; }; int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); -- cgit v1.2.3 From 66340e5b259bd7ca67cf0ca079dd3997fa198d4b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 10 Jun 2012 17:09:22 +0300 Subject: wlcore: allow setting sleep_auth before interface init Hold a value for sta_sleep_auth that is amenable to change by debugfs. When detecting a legal value in this variable on interface init, use it as an override value for sleep_auth. This makes debugging more intuitive using the debugfs value. Increment the conf version since we added an element to the conf structure. Note: An AP going up will always set sleep_auth to PSM_CAM. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 1 + drivers/net/wireless/ti/wl18xx/main.c | 1 + drivers/net/wireless/ti/wlcore/acx.c | 2 +- drivers/net/wireless/ti/wlcore/acx.h | 3 +++ drivers/net/wireless/ti/wlcore/conf.h | 8 +++++++- drivers/net/wireless/ti/wlcore/debugfs.c | 7 ++++++- drivers/net/wireless/ti/wlcore/init.c | 8 +++++++- drivers/net/wireless/ti/wlcore/main.c | 2 +- 8 files changed, 27 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 85d1600ee340..7974ed55dd5b 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -246,6 +246,7 @@ static struct wlcore_conf wl12xx_conf = { .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, + .sta_sleep_auth = WL1271_PSM_ILLEGAL, }, .itrim = { .enable = false, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index b2ccff7d6188..d3f171ddebae 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -372,6 +372,7 @@ static struct wlcore_conf wl18xx_conf = { .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, + .sta_sleep_auth = WL1271_PSM_ILLEGAL, }, .itrim = { .enable = false, diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index b56217f9bfd4..3384bc14c824 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -70,7 +70,7 @@ int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth) struct acx_sleep_auth *auth; int ret; - wl1271_debug(DEBUG_ACX, "acx sleep auth"); + wl1271_debug(DEBUG_ACX, "acx sleep auth %d", sleep_auth); auth = kzalloc(sizeof(*auth), GFP_KERNEL); if (!auth) { diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 168e0464411d..d03215d6b3bd 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -120,6 +120,9 @@ enum wl1271_psm_mode { WL1271_PSM_ELP = 2, WL1271_PSM_MAX = WL1271_PSM_ELP, + + /* illegal out of band value of PSM mode */ + WL1271_PSM_ILLEGAL = 0xff }; struct acx_sleep_auth { diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 03c635872335..d77224f2ac6b 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -951,6 +951,12 @@ struct conf_conn_settings { * Range: u16 */ u8 max_listen_interval; + + /* + * Default sleep authorization for a new STA interface. This determines + * whether we can go to ELP. + */ + u8 sta_sleep_auth; } __packed; enum { @@ -1276,7 +1282,7 @@ struct conf_hangover_settings { * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0001 << 16) +#define WLCORE_CONF_VERSION (0x0002 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 91e43def013d..1768f37049bd 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -995,8 +995,13 @@ static ssize_t sleep_auth_write(struct file *file, mutex_lock(&wl->mutex); - if (wl->state == WL1271_STATE_OFF) + wl->conf.conn.sta_sleep_auth = value; + + if (wl->state == WL1271_STATE_OFF) { + /* this will show up on "read" in case we are off */ + wl->sleep_auth = value; goto out; + } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 645abd4b660d..3fb9352bf504 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -565,7 +565,13 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) if (ret < 0) return ret; } else if (!wl->sta_count) { - if (wl->quirks & WLCORE_QUIRK_NO_ELP) { + u8 sta_auth = wl->conf.conn.sta_sleep_auth; + if (sta_auth != WL1271_PSM_ILLEGAL) { + /* Configure for power according to debugfs */ + ret = wl1271_acx_sleep_auth(wl, sta_auth); + if (ret < 0) + return ret; + } else if (wl->quirks & WLCORE_QUIRK_NO_ELP) { /* Configure for power always on */ ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 7677cd55f83e..69643d194301 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5176,7 +5176,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) wl->channel_type = NL80211_CHAN_NO_HT; wl->flags = 0; wl->sg_enabled = true; - wl->sleep_auth = WL1271_PSM_CAM; + wl->sleep_auth = WL1271_PSM_ILLEGAL; wl->hw_pg_ver = -1; wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; -- cgit v1.2.3 From 2f18cf7c3b99779465def78318b4243d1f66cce8 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 10 Jun 2012 19:10:45 +0300 Subject: wlcore: reconfigure sleep_auth when removing interfaces The sleep_auth value of the last interface to be set up prevailed when an interface was removed. Take care of this by correctly configuring the value according to the remaining STA/AP interfaces. Take this opportunity to refactor the sleep_auth setting code for better readability. [Small style fix. -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/acx.c | 5 +++- drivers/net/wireless/ti/wlcore/init.c | 49 +++++++++++++++-------------------- drivers/net/wireless/ti/wlcore/main.c | 21 ++++++++++++--- 3 files changed, 43 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index 3384bc14c824..ce108a736bd0 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -81,8 +81,11 @@ int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth) auth->sleep_auth = sleep_auth; ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); - if (ret < 0) + if (ret < 0) { + wl1271_error("could not configure sleep_auth to %d: %d", + sleep_auth, ret); goto out; + } wl->sleep_auth = sleep_auth; out: diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 3fb9352bf504..8a8a8971befa 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -554,35 +554,28 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); int ret, i; - /* - * consider all existing roles before configuring psm. - * TODO: reconfigure on interface removal. - */ - if (!wl->ap_count) { - if (is_ap) { - /* Configure for power always on */ + /* consider all existing roles before configuring psm. */ + + if (wl->ap_count == 0 && is_ap) { /* first AP */ + /* Configure for power always on */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + if (ret < 0) + return ret; + /* first STA, no APs */ + } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) { + u8 sta_auth = wl->conf.conn.sta_sleep_auth; + /* Configure for power according to debugfs */ + if (sta_auth != WL1271_PSM_ILLEGAL) + ret = wl1271_acx_sleep_auth(wl, sta_auth); + /* Configure for power always on */ + else if (wl->quirks & WLCORE_QUIRK_NO_ELP) ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); - if (ret < 0) - return ret; - } else if (!wl->sta_count) { - u8 sta_auth = wl->conf.conn.sta_sleep_auth; - if (sta_auth != WL1271_PSM_ILLEGAL) { - /* Configure for power according to debugfs */ - ret = wl1271_acx_sleep_auth(wl, sta_auth); - if (ret < 0) - return ret; - } else if (wl->quirks & WLCORE_QUIRK_NO_ELP) { - /* Configure for power always on */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); - if (ret < 0) - return ret; - } else { - /* Configure for ELP power saving */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); - if (ret < 0) - return ret; - } - } + /* Configure for ELP power saving */ + else + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); + + if (ret < 0) + return ret; } /* Mode specific init */ diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 69643d194301..3279a94163f3 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1082,7 +1082,7 @@ int wl1271_plt_stop(struct wl1271 *wl) mutex_lock(&wl->mutex); wl1271_power_off(wl); wl->flags = 0; - wl->sleep_auth = WL1271_PSM_CAM; + wl->sleep_auth = WL1271_PSM_ILLEGAL; wl->state = WL1271_STATE_OFF; wl->plt = false; wl->rx_counter = 0; @@ -1741,7 +1741,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; wl->sched_scanning = false; - wl->sleep_auth = WL1271_PSM_CAM; + wl->sleep_auth = WL1271_PSM_ILLEGAL; memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); @@ -2148,6 +2148,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int i, ret; + bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); @@ -2228,11 +2229,25 @@ deinit: wlvif->role_id = WL12XX_INVALID_ROLE_ID; wlvif->dev_role_id = WL12XX_INVALID_ROLE_ID; - if (wlvif->bss_type == BSS_TYPE_AP_BSS) + if (is_ap) wl->ap_count--; else wl->sta_count--; + /* Last AP, have more stations. Configure according to STA. */ + if (wl->ap_count == 0 && is_ap && wl->sta_count) { + u8 sta_auth = wl->conf.conn.sta_sleep_auth; + /* Configure for power according to debugfs */ + if (sta_auth != WL1271_PSM_ILLEGAL) + wl1271_acx_sleep_auth(wl, sta_auth); + /* Configure for power always on */ + else if (wl->quirks & WLCORE_QUIRK_NO_ELP) + wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + /* Configure for ELP power saving */ + else + wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); + } + mutex_unlock(&wl->mutex); del_timer_sync(&wlvif->rx_streaming_timer); -- cgit v1.2.3 From b222a817b45c27f3411dbe9eb6b4041076351bc2 Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 21 Jun 2012 08:03:56 +0000 Subject: regulator: change message level on probing lp872x driver Use err log rather than warning message when the platform data is NULL Signed-off-by: Milo(Woogyom) Kim Signed-off-by: Mark Brown --- drivers/regulator/lp872x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index e8f54efb1985..471f8e82be41 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -885,7 +885,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) }; if (!pdata) { - dev_warn(&cl->dev, "no platform data\n"); + dev_err(&cl->dev, "no platform data\n"); return -EINVAL; } -- cgit v1.2.3 From ce7d16a1752accbff1ff7c813594aad0b7168563 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 21 Jun 2012 10:05:56 -0300 Subject: [media] smiapp-core: fix compilation build error smiapp-core.c:2472:3: error: implicit declaration of function 'kzalloc' [-Werror=implicit-function-declaration] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/smiapp/smiapp-core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c index e8c93c89265a..9cf5bda35fbe 100644 --- a/drivers/media/video/smiapp/smiapp-core.c +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From b6a509df59c6abd0a2177e3be29642207711145d Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 20 Jun 2012 21:30:30 -0300 Subject: [media] media: pms.c needs linux/slab.h drivers/media/video/pms.c uses kzalloc() and kfree() so it should include to fix build errors and a warning. drivers/media/video/pms.c:1047:2: error: implicit declaration of function 'kzalloc' drivers/media/video/pms.c:1047:6: warning: assignment makes pointer from integer without a cast drivers/media/video/pms.c:1116:2: error: implicit declaration of function 'kfree' Found in mmotm but applies to mainline. Signed-off-by: Randy Dunlap Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pms.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 77f9c92186f4..b4c679b3fb0f 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 09aad14f6533d0db47b8077792fcb7c8fc881cf1 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 10 Jun 2012 22:57:30 +0300 Subject: wl18xx: increase Rx descriptors for PG2 New PG2 firmwares have additional Rx descriptors. Add a module parameter to manually set the number of Rx descriptors for older versions (PG1). We cannot discriminate based on chip-id, since this value must be set on probe. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index d3f171ddebae..18cf19c12941 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -47,6 +47,7 @@ static char *ht_mode_param = "wide"; static char *board_type_param = "hdk"; static bool checksum_param = false; static bool enable_11a_param = true; +static int num_rx_desc_param = -1; /* phy paramters */ static int dc2dc_param = -1; @@ -1286,7 +1287,7 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) wl->ptable = wl18xx_ptable; wl->rtable = wl18xx_rtable; wl->num_tx_desc = 32; - wl->num_rx_desc = 16; + wl->num_rx_desc = 32; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; @@ -1294,6 +1295,9 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); + if (num_rx_desc_param != -1) + wl->num_rx_desc = num_rx_desc_param; + if (!strcmp(ht_mode_param, "wide")) { memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl18xx_siso40_ht_cap, @@ -1458,6 +1462,11 @@ module_param_named(pwr_limit_reference_11_abg, MODULE_PARM_DESC(pwr_limit_reference_11_abg, "Power limit reference: u8 " "(default is 0xc8)"); +module_param_named(num_rx_desc, + num_rx_desc_param, int, S_IRUSR); +MODULE_PARM_DESC(num_rx_desc_param, + "Number of Rx descriptors: u8 (default is 32)"); + MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Luciano Coelho "); MODULE_FIRMWARE(WL18XX_FW_NAME); -- cgit v1.2.3 From bf7c46a7672830eb13898b2871de7780448f0674 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 11 Jun 2012 10:41:08 +0300 Subject: wl18xx: set Tx align quirk for PG2 Before patch b5d6d9b (wlcore/wl12xx/wl18xx: don't use TX align quirk for wl127x), this was automatically set for all platforms. As this should now be set explicitly, set it for PG2 as well. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 18cf19c12941..066e8e5cbb66 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -608,8 +608,8 @@ static int wl18xx_identify_chip(struct wl1271 *wl) wl->plt_fw_name = WL18XX_FW_NAME; wl->quirks |= WLCORE_QUIRK_NO_ELP | WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | + WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN | WLCORE_QUIRK_TX_PAD_LAST_FRAME; - break; case CHIP_ID_185x_PG10: wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)", -- cgit v1.2.3 From add779a0738a6bd199b3c5b9f0f8090036e53ff0 Mon Sep 17 00:00:00 2001 From: Yoni Divinsky Date: Wed, 13 Jun 2012 18:56:54 +0300 Subject: wlcore: do not report noise level in get survey op The get survey op expects the low level driver to report the noise level for a a given channel. The noise calculated in wlcore is (rssi-snr/2), but since the snr reported by the FW is a derivative from the rssi this calculation is useless, and should not be reported to the user space. Reporting incorrect noise, results in the wpa_supplicant miscalculating the roaming candidate priority, thus causing a situation where an AP with a lower rssi level would be chosen over a better AP. Signed-off-by: Yoni Divinsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 3279a94163f3..a7c5e32e90db 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -4108,16 +4108,13 @@ out: static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) { - struct wl1271 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; if (idx != 0) return -ENOENT; survey->channel = conf->channel; - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = wl->noise; - + survey->filled = 0; return 0; } -- cgit v1.2.3 From 1e41213fe738e8f8e3fd69dd490ac7e4faaa184f Mon Sep 17 00:00:00 2001 From: Igal Chernobelsky Date: Mon, 18 Jun 2012 11:05:39 +0300 Subject: wlcore: read FW logs from FW memory on watchdog recovery FW uses a few memory blocks as a buffer to accumulate FW logs before transmitting them to the host over SDIO. When FW WatchDog recovery occurs, the last FW traces are still pending in the buffer. Driver is to read these FW traces whether log mode is continuous or on demand. FW memory blocks allocated for the log buffer are handled as a link list: the first 4 bytes in each memory block contain FW address to the next block. The end of list condition depends on FW log mode: - on demand: the list is cyclic, the next address is equal to the first address - continuous: the address is equal to 0x2000000 Log data resides inside FW memory block with offset depending on logger mode: - on demand: 4 bytes (address of the next memory block) - continuous: 4 bytes and Rx Descriptor structure size Described FW logger API is backward compatible with previous FW versions. Signed-off-by: Igal Chernobelsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index a7c5e32e90db..78edc58da210 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -770,14 +770,16 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) return len; } +#define WLCORE_FW_LOG_END 0x2000000 + static void wl12xx_read_fwlog_panic(struct wl1271 *wl) { u32 addr; - u32 first_addr; + u32 offset; + u32 end_of_log; u8 *block; if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) || - (wl->conf.fwlog.mode != WL12XX_FWLOG_ON_DEMAND) || (wl->conf.fwlog.mem_blocks == 0)) return; @@ -791,19 +793,26 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) * Make sure the chip is awake and the logger isn't active. * Do not send a stop fwlog command if the fw is hanged. */ - if (!wl1271_ps_elp_wakeup(wl) && !wl->watchdog_recovery) - wl12xx_cmd_stop_fwlog(wl); - else + if (wl1271_ps_elp_wakeup(wl)) goto out; + if (!wl->watchdog_recovery) + wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2); - first_addr = le32_to_cpu(wl->fw_status_2->log_start_addr); - if (!first_addr) + addr = le32_to_cpu(wl->fw_status_2->log_start_addr); + if (!addr) goto out; + if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) { + offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor); + end_of_log = WLCORE_FW_LOG_END; + } else { + offset = sizeof(addr); + end_of_log = addr; + } + /* Traverse the memory blocks linked list */ - addr = first_addr; do { memset(block, 0, WL12XX_HW_BLOCK_SIZE); wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, @@ -812,13 +821,14 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) /* * Memory blocks are linked to one another. The first 4 bytes * of each memory block hold the hardware address of the next - * one. The last memory block points to the first one. + * one. The last memory block points to the first one in + * on demand mode and is equal to 0x2000000 in continuous mode. */ addr = le32_to_cpup((__le32 *)block); - if (!wl12xx_copy_fwlog(wl, block + sizeof(addr), - WL12XX_HW_BLOCK_SIZE - sizeof(addr))) + if (!wl12xx_copy_fwlog(wl, block + offset, + WL12XX_HW_BLOCK_SIZE - offset)) break; - } while (addr && (addr != first_addr)); + } while (addr && (addr != end_of_log)); wake_up_interruptible(&wl->fwlog_waitq); -- cgit v1.2.3 From 68a847f2c1ea2b974a28c5b537fe846522d7a9c0 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 13 Jun 2012 19:09:24 +0300 Subject: wl18xx: explicitly remove the 5Ghz MIMO HT cap The 18xx chip does not support MIMO in 5Ghz. Use the siso20 HT cap as fallback in 5Ghz when "mimo" is requested. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 066e8e5cbb66..365063b6f7c3 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1255,18 +1255,6 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = { }, }; -static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_5ghz = { - .cap = IEEE80211_HT_CAP_SGI_20, - .ht_supported = true, - .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, - .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, - .mcs = { - .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, - .rx_highest = cpu_to_le16(72), - .tx_params = IEEE80211_HT_MCS_TX_DEFINED, - }, -}; - static int __devinit wl18xx_probe(struct platform_device *pdev) { struct wl1271 *wl; @@ -1309,9 +1297,10 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl18xx_mimo_ht_cap_2ghz, sizeof(wl18xx_mimo_ht_cap_2ghz)); + /* we don't support MIMO in 5Ghz */ memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], - &wl18xx_mimo_ht_cap_5ghz, - sizeof(wl18xx_mimo_ht_cap_5ghz)); + &wl18xx_siso20_ht_cap, + sizeof(wl18xx_siso20_ht_cap)); } else if (!strcmp(ht_mode_param, "siso20")) { memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl18xx_siso20_ht_cap, -- cgit v1.2.3 From fa2adfcdbd88124e8b7cc46c6363b1343dabc09d Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 13 Jun 2012 19:09:25 +0300 Subject: wl18xx: sane defaults for HT capabilities Introduce a default set of HT capabilities that are set according to the number of antennas on the board. Move the HT setting code down to allow the number of antennas to be set (and optionally overridden) before it. Remove the "mimo" HT option, since the default mode now enables MIMO is possible. Use this opportunity to add a helper function for setting HT capabilities and reduce the volume of the code a bit. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 6 ++-- drivers/net/wireless/ti/wl18xx/main.c | 63 +++++++++++++++++---------------- drivers/net/wireless/ti/wlcore/wlcore.h | 7 ++++ 3 files changed, 42 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 7974ed55dd5b..0d2fdca2aa32 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1449,10 +1449,8 @@ static int __devinit wl12xx_probe(struct platform_device *pdev) wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; wl->fw_status_priv_len = 0; wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); - memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], &wl12xx_ht_cap, - sizeof(wl12xx_ht_cap)); - memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], &wl12xx_ht_cap, - sizeof(wl12xx_ht_cap)); + wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap); + wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap); wl12xx_conf_init(wl); if (!fref_param) { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 365063b6f7c3..485aeae2f777 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -43,7 +43,7 @@ #define WL18XX_RX_CHECKSUM_MASK 0x40 -static char *ht_mode_param = "wide"; +static char *ht_mode_param = "default"; static char *board_type_param = "hdk"; static bool checksum_param = false; static bool enable_11a_param = true; @@ -1286,34 +1286,6 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) if (num_rx_desc_param != -1) wl->num_rx_desc = num_rx_desc_param; - if (!strcmp(ht_mode_param, "wide")) { - memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], - &wl18xx_siso40_ht_cap, - sizeof(wl18xx_siso40_ht_cap)); - memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], - &wl18xx_siso40_ht_cap, - sizeof(wl18xx_siso40_ht_cap)); - } else if (!strcmp(ht_mode_param, "mimo")) { - memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], - &wl18xx_mimo_ht_cap_2ghz, - sizeof(wl18xx_mimo_ht_cap_2ghz)); - /* we don't support MIMO in 5Ghz */ - memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], - &wl18xx_siso20_ht_cap, - sizeof(wl18xx_siso20_ht_cap)); - } else if (!strcmp(ht_mode_param, "siso20")) { - memcpy(&wl->ht_cap[IEEE80211_BAND_2GHZ], - &wl18xx_siso20_ht_cap, - sizeof(wl18xx_siso20_ht_cap)); - memcpy(&wl->ht_cap[IEEE80211_BAND_5GHZ], - &wl18xx_siso20_ht_cap, - sizeof(wl18xx_siso20_ht_cap)); - } else { - wl1271_error("invalid ht_mode '%s'", ht_mode_param); - ret = -EINVAL; - goto out_free; - } - ret = wl18xx_conf_init(wl, &pdev->dev); if (ret < 0) goto out_free; @@ -1359,6 +1331,37 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) if (dc2dc_param != -1) priv->conf.phy.external_pa_dc2dc = dc2dc_param; + if (!strcmp(ht_mode_param, "default")) { + /* + * Only support mimo with multiple antennas. Fall back to + * siso20. + */ + if (priv->conf.phy.number_of_assembled_ant2_4 >= 2) + wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, + &wl18xx_mimo_ht_cap_2ghz); + else + wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, + &wl18xx_siso20_ht_cap); + + /* 5Ghz is always wide */ + wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, + &wl18xx_siso40_ht_cap); + } else if (!strcmp(ht_mode_param, "wide")) { + wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, + &wl18xx_siso40_ht_cap); + wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, + &wl18xx_siso40_ht_cap); + } else if (!strcmp(ht_mode_param, "siso20")) { + wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, + &wl18xx_siso20_ht_cap); + wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, + &wl18xx_siso20_ht_cap); + } else { + wl1271_error("invalid ht_mode '%s'", ht_mode_param); + ret = -EINVAL; + goto out_free; + } + if (!checksum_param) { wl18xx_ops.set_rx_csum = NULL; wl18xx_ops.init_vif = NULL; @@ -1403,7 +1406,7 @@ static void __exit wl18xx_exit(void) module_exit(wl18xx_exit); module_param_named(ht_mode, ht_mode_param, charp, S_IRUSR); -MODULE_PARM_DESC(ht_mode, "Force HT mode: wide (default), mimo or siso20"); +MODULE_PARM_DESC(ht_mode, "Force HT mode: wide or siso20"); module_param_named(board_type, board_type_param, charp, S_IRUSR); MODULE_PARM_DESC(board_type, "Board type: fpga, hdk (default), evb, com8 or " diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index eae8edcc5fc7..205d8ad2b761 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -401,6 +401,13 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); +static inline void +wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, + struct ieee80211_sta_ht_cap *ht_cap) +{ + memcpy(&wl->ht_cap[band], ht_cap, sizeof(*ht_cap)); +} + /* Firmware image load chunk size */ #define CHUNK_SIZE 16384 -- cgit v1.2.3 From 93fb19bbb37c734ec0c662aa600d1d6a12c1be70 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 13 Jun 2012 19:09:26 +0300 Subject: wl18xx: split siso40 HT cap between 2Ghz and 5Ghz Remove the cap IEEE80211_HT_CAP_DSSSCCK40 from the 5Ghz variant of the siso40 HT capabilities. It is meaningless in 5Ghz. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 485aeae2f777..2c0f51b449c4 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1215,8 +1215,8 @@ static struct wlcore_ops wl18xx_ops = { .pre_pkt_send = wl18xx_pre_pkt_send, }; -/* HT cap appropriate for wide channels */ -static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = { +/* HT cap appropriate for wide channels in 2Ghz */ +static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = { .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40, .ht_supported = true, @@ -1229,6 +1229,20 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap = { }, }; +/* HT cap appropriate for wide channels in 5Ghz */ +static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = { + .cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_SUP_WIDTH_20_40, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(150), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, +}; + /* HT cap appropriate for SISO 20 */ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = { .cap = IEEE80211_HT_CAP_SGI_20, @@ -1345,12 +1359,12 @@ static int __devinit wl18xx_probe(struct platform_device *pdev) /* 5Ghz is always wide */ wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, - &wl18xx_siso40_ht_cap); + &wl18xx_siso40_ht_cap_5ghz); } else if (!strcmp(ht_mode_param, "wide")) { wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, - &wl18xx_siso40_ht_cap); + &wl18xx_siso40_ht_cap_2ghz); wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, - &wl18xx_siso40_ht_cap); + &wl18xx_siso40_ht_cap_5ghz); } else if (!strcmp(ht_mode_param, "siso20")) { wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl18xx_siso20_ht_cap); -- cgit v1.2.3 From 41844076c5c3a33636b5c26d19b16c6141c5d6cd Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 21 Jun 2012 15:33:10 +0300 Subject: wl18xx: use %zu for size_t arguments in printk calls After 934b9d1e (wl18xx: avoid some -Wformat warnings) there was still a warning with (at least) ARM gcc version 4.4.1: drivers/net/wireless/ti/wl18xx/main.c: In function 'wl18xx_conf_init': drivers/net/wireless/ti/wl18xx/main.c:1026: warning: format '%ld' expects type 'long int', but argument 2 has type 'unsigned int' Fix this by using %zu for the both formats, since the fw->size and the macro (derived from sizeof()) are size_t. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 2c0f51b449c4..271ff81da922 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1023,8 +1023,8 @@ static int wl18xx_conf_init(struct wl1271 *wl, struct device *dev) } if (fw->size != WL18XX_CONF_SIZE) { - wl1271_error("configuration binary file size is wrong, " - "expected %d got %d", WL18XX_CONF_SIZE, fw->size); + wl1271_error("configuration binary file size is wrong, expected %zu got %zu", + WL18XX_CONF_SIZE, fw->size); ret = -EINVAL; goto out; } -- cgit v1.2.3 From e734568b675c985db2026848fefaac01c22977a5 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 8 Jun 2012 16:16:04 +0100 Subject: oprofile: perf: use NR_CPUS instead or nr_cpumask_bits for static array The OProfile perf backend uses a static array to keep track of the perf events on the system. When compiling with CONFIG_CPUMASK_OFFSTACK=y && SMP, nr_cpumask_bits is not a compile-time constant and the build will fail with: oprofile_perf.c:28: error: variably modified 'perf_events' at file scope This patch uses NR_CPUs instead of nr_cpumask_bits for the array initialisation. If this causes space problems in the future, we can always move to dynamic allocation for the events array. Cc: Matt Fleming Reported-by: Russell King - ARM Linux Signed-off-by: Will Deacon Cc: # v2.6.37+ Signed-off-by: Robert Richter --- drivers/oprofile/oprofile_perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index da14432806c6..efc4b7f308cf 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -25,7 +25,7 @@ static int oprofile_perf_enabled; static DEFINE_MUTEX(oprofile_perf_mutex); static struct op_counter_config *counter_config; -static struct perf_event **perf_events[nr_cpumask_bits]; +static struct perf_event **perf_events[NR_CPUS]; static int num_counters; /* -- cgit v1.2.3 From f73332fc39e35a6ac14f892390adcd34a63b00d3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 21 Jun 2012 02:15:10 +0000 Subject: ixgbe: simplify padding and length checks The check for length <= 0 is bogus because length is unsigned, and network stack never sends zero length packets (unless it is totally broken). The check for really small packets can be optimized (using unlikely) and calling skb_pad directly. Signed-off-by: Stephen Hemminger Tested-by: Phil Schmitt Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index b0ddfd47e473..69a660b5621a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -6380,17 +6380,12 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_ring *tx_ring; - if (skb->len <= 0) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - /* * The minimum packet size for olinfo paylen is 17 so pad the skb * in order to meet this minimum size requirement. */ - if (skb->len < 17) { - if (skb_padto(skb, 17)) + if (unlikely(skb->len < 17)) { + if (skb_pad(skb, 17 - skb->len)) return NETDEV_TX_OK; skb->len = 17; } -- cgit v1.2.3 From d660030061dd7e20ad18557368188d9c1002ec71 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Thu, 21 Jun 2012 21:40:37 -0600 Subject: W1: OMAP HDQ1W: allow driver to be built on all OMAP2+ Allow the OMAP HDQ1W driver to be built for all OMAP2+ SoCs by adjusting KConfig dependencies. The previous dependency required either SOC_OMAP2430 or ARCH_OMAP3 to be set, but the HDQ IP block is present on OMAP2420 and OMAP44xx SoCs. The driver was still selectable on multi-OMAP kernel configurations, however; so the previous prohibition was rather pointless. Signed-off-by: Paul Walmsley Cc: Evgeniy Polyakov Acked-by: Evgeniy Polyakov --- drivers/w1/masters/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index 979d6eed9a0f..5ceb1cd50195 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -60,7 +60,7 @@ config W1_MASTER_GPIO config HDQ_MASTER_OMAP tristate "OMAP HDQ driver" - depends on SOC_OMAP2430 || ARCH_OMAP3 + depends on ARCH_OMAP2PLUS help Say Y here if you want support for the 1-wire or HDQ Interface on an OMAP processor. -- cgit v1.2.3 From 2acd089471d93373e051c6b1f9f9e0d9e51a76bc Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Thu, 21 Jun 2012 21:40:37 -0600 Subject: W1: OMAP HDQ1W: use 32-bit register accesses HDQ/1-wire registers are 32 bits long, even if the register contents fit into 8 bits, so accesses must be 32-bit aligned. Evidently the OMAP2/3 interconnects allowed the driver to get away with 8 bit accesses, but the OMAP4 puts a stop to that: [ 1.488800] Driver for 1-wire Dallas network protocol. [ 1.495025] Bad mode in data abort handler detected [ 1.500122] Internal error: Oops - bad mode: 0 [#1] SMP [ 1.505615] Modules linked in: [ 1.508819] CPU: 0 Not tainted (3.3.0-rc1-00008-g45030e9 #992) [ 1.515289] PC is at 0xffff0018 [ 1.518615] LR is at omap_hdq_probe+0xd4/0x2cc The OMAP4430 ES2 Rev X TRM does warn about this restriction in section 23.2.6.2 "HDQ/1-Wire Registers". Fixes the crash on OMAP4430 ES2 Pandaboard. Tested also on OMAP34xx and OMAP2420; it seems to work fine on those chips, although due to the lack of boards with HDQ/1-wire devices here, a more indepth test was not possible. Signed-off-by: Paul Walmsley Cc: NeilBrown Cc: Evgeniy Polyakov Acked-by: Evgeniy Polyakov --- drivers/w1/masters/omap_hdq.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 5ef385bfed18..344db9c5ab88 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -102,20 +102,20 @@ static struct w1_bus_master omap_w1_master = { /* HDQ register I/O routines */ static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset) { - return __raw_readb(hdq_data->hdq_base + offset); + return __raw_readl(hdq_data->hdq_base + offset); } static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val) { - __raw_writeb(val, hdq_data->hdq_base + offset); + __raw_writel(val, hdq_data->hdq_base + offset); } static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset, u8 val, u8 mask) { - u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask) + u8 new_val = (__raw_readl(hdq_data->hdq_base + offset) & ~mask) | (val & mask); - __raw_writeb(new_val, hdq_data->hdq_base + offset); + __raw_writel(new_val, hdq_data->hdq_base + offset); return new_val; } -- cgit v1.2.3 From c354a86484b61e32100eb94c1f3f0aa512958cee Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Thu, 21 Jun 2012 21:40:40 -0600 Subject: W1: OMAP HDQ1W: use runtime PM Convert the OMAP HDQ driver to use runtime PM. Compile- and boot-tested, but not tested in actual use. Signed-off-by: Paul Walmsley Cc: NeilBrown Cc: Evgeniy Polyakov Acked-by: Evgeniy Polyakov Tested-by: NeilBrown --- drivers/w1/masters/omap_hdq.c | 78 ++++++++----------------------------------- 1 file changed, 13 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 344db9c5ab88..291897c881be 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -1,7 +1,7 @@ /* * drivers/w1/masters/omap_hdq.c * - * Copyright (C) 2007 Texas Instruments, Inc. + * Copyright (C) 2007,2012 Texas Instruments, Inc. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any @@ -14,9 +14,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -61,8 +61,6 @@ struct hdq_data { /* lock status update */ struct mutex hdq_mutex; int hdq_usecount; - struct clk *hdq_ick; - struct clk *hdq_fck; u8 hdq_irqstatus; /* device lock */ spinlock_t hdq_spinlock; @@ -419,17 +417,8 @@ static int omap_hdq_get(struct hdq_data *hdq_data) hdq_data->hdq_usecount++; try_module_get(THIS_MODULE); if (1 == hdq_data->hdq_usecount) { - if (clk_enable(hdq_data->hdq_ick)) { - dev_dbg(hdq_data->dev, "Can not enable ick\n"); - ret = -ENODEV; - goto clk_err; - } - if (clk_enable(hdq_data->hdq_fck)) { - dev_dbg(hdq_data->dev, "Can not enable fck\n"); - clk_disable(hdq_data->hdq_ick); - ret = -ENODEV; - goto clk_err; - } + + pm_runtime_get_sync(hdq_data->dev); /* make sure HDQ is out of reset */ if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) & @@ -450,9 +439,6 @@ static int omap_hdq_get(struct hdq_data *hdq_data) } } -clk_err: - clk_put(hdq_data->hdq_ick); - clk_put(hdq_data->hdq_fck); out: mutex_unlock(&hdq_data->hdq_mutex); rtn: @@ -475,10 +461,8 @@ static int omap_hdq_put(struct hdq_data *hdq_data) } else { hdq_data->hdq_usecount--; module_put(THIS_MODULE); - if (0 == hdq_data->hdq_usecount) { - clk_disable(hdq_data->hdq_ick); - clk_disable(hdq_data->hdq_fck); - } + if (0 == hdq_data->hdq_usecount) + pm_runtime_put_sync(hdq_data->dev); } mutex_unlock(&hdq_data->hdq_mutex); @@ -591,35 +575,11 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev) goto err_ioremap; } - /* get interface & functional clock objects */ - hdq_data->hdq_ick = clk_get(&pdev->dev, "ick"); - if (IS_ERR(hdq_data->hdq_ick)) { - dev_dbg(&pdev->dev, "Can't get HDQ ick clock object\n"); - ret = PTR_ERR(hdq_data->hdq_ick); - goto err_ick; - } - - hdq_data->hdq_fck = clk_get(&pdev->dev, "fck"); - if (IS_ERR(hdq_data->hdq_fck)) { - dev_dbg(&pdev->dev, "Can't get HDQ fck clock object\n"); - ret = PTR_ERR(hdq_data->hdq_fck); - goto err_fck; - } - hdq_data->hdq_usecount = 0; mutex_init(&hdq_data->hdq_mutex); - if (clk_enable(hdq_data->hdq_ick)) { - dev_dbg(&pdev->dev, "Can not enable ick\n"); - ret = -ENODEV; - goto err_intfclk; - } - - if (clk_enable(hdq_data->hdq_fck)) { - dev_dbg(&pdev->dev, "Can not enable fck\n"); - ret = -ENODEV; - goto err_fnclk; - } + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", @@ -641,9 +601,7 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev) omap_hdq_break(hdq_data); - /* don't clock the HDQ until it is needed */ - clk_disable(hdq_data->hdq_ick); - clk_disable(hdq_data->hdq_fck); + pm_runtime_put_sync(&pdev->dev); omap_w1_master.data = hdq_data; @@ -655,20 +613,11 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev) return 0; -err_w1: err_irq: - clk_disable(hdq_data->hdq_fck); - -err_fnclk: - clk_disable(hdq_data->hdq_ick); - -err_intfclk: - clk_put(hdq_data->hdq_fck); - -err_fck: - clk_put(hdq_data->hdq_ick); + pm_runtime_put_sync(&pdev->dev); +err_w1: + pm_runtime_disable(&pdev->dev); -err_ick: iounmap(hdq_data->hdq_base); err_ioremap: @@ -696,8 +645,7 @@ static int omap_hdq_remove(struct platform_device *pdev) mutex_unlock(&hdq_data->hdq_mutex); /* remove module dependency */ - clk_put(hdq_data->hdq_ick); - clk_put(hdq_data->hdq_fck); + pm_runtime_disable(&pdev->dev); free_irq(INT_24XX_HDQ_IRQ, hdq_data); platform_set_drvdata(pdev, NULL); iounmap(hdq_data->hdq_base); -- cgit v1.2.3 From 645865fc377c9ac73df590abf8e6af65824390a3 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sun, 20 May 2012 10:38:16 +0300 Subject: wlcore: Fix sdio out-of-sync power state wl12xx_sdio_power_off() manually powers down the card regardless of the runtime pm state. If wl12xx_sdio_power_on() is called before the card was suspended by runtime PM, it will not power up the card. As part of the HW detection, the chip's power is toggled. Since this happens in the context of probing sdio, the power reference counter will be higher than zero. As a result, when wl12xx_sdio_power_off() is called, the chip will be powered down while still having a positive power reference counter. If the interface is quickly activated, the driver might try to transfer data to a powered off chip. Fix this by ensuring that wl12xx_sdio_power_on() explicitly powers on the chip in case runtime pm claims the chip is already powered on. To avoid cases in which it is not possible to determine if the chip was really powered on (card's power reference counter is positive), operate on the mmc_card instead of the function. Also verify that the chip is indeed powered on before powering off, to avoid wrong reference counter values in error cases. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/io.h | 10 ++++++++-- drivers/net/wireless/ti/wlcore/sdio.c | 34 +++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 8942954b56a0..404cb14458eb 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -160,8 +160,14 @@ static inline void wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) static inline void wl1271_power_off(struct wl1271 *wl) { - wl->if_ops->power(wl->dev, false); - clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); + int ret; + + if (!test_bit(WL1271_FLAG_GPIO_POWER, &wl->flags)) + return; + + ret = wl->if_ops->power(wl->dev, false); + if (!ret) + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); } static inline int wl1271_power_on(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index c67ec482e445..4edaa20acfb1 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -147,17 +147,20 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue) { int ret; struct sdio_func *func = dev_to_sdio_func(glue->dev); + struct mmc_card *card = func->card; - /* If enabled, tell runtime PM not to power off the card */ - if (pm_runtime_enabled(&func->dev)) { - ret = pm_runtime_get_sync(&func->dev); - if (ret < 0) - goto out; - } else { - /* Runtime PM is disabled: power up the card manually */ - ret = mmc_power_restore_host(func->card->host); - if (ret < 0) + ret = pm_runtime_get_sync(&card->dev); + if (ret) { + /* + * Runtime PM might be temporarily disabled, or the device + * might have a positive reference counter. Make sure it is + * really powered on. + */ + ret = mmc_power_restore_host(card->host); + if (ret < 0) { + pm_runtime_put_sync(&card->dev); goto out; + } } sdio_claim_host(func); @@ -172,20 +175,21 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue) { int ret; struct sdio_func *func = dev_to_sdio_func(glue->dev); + struct mmc_card *card = func->card; sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); - /* Power off the card manually, even if runtime PM is enabled. */ - ret = mmc_power_save_host(func->card->host); + /* Power off the card manually in case it wasn't powered off above */ + ret = mmc_power_save_host(card->host); if (ret < 0) - return ret; + goto out; - /* If enabled, let runtime PM know the card is powered off */ - if (pm_runtime_enabled(&func->dev)) - ret = pm_runtime_put_sync(&func->dev); + /* Let runtime PM know the card is powered off */ + pm_runtime_put_sync(&card->dev); +out: return ret; } -- cgit v1.2.3 From b666bb7f2fe2bdc0309b0d58afb48eae85d92221 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 21 May 2012 01:10:11 +0300 Subject: wlcore: Disable interrupts while recovering In case a recovery is initiated, the FW can no longer be trusted, and the driver should not handle any new FW events. Disable the interrupt handler when a recovery is scheduled and balance it back in the op_stop callback. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/io.c | 6 ++++++ drivers/net/wireless/ti/wlcore/io.h | 1 + drivers/net/wireless/ti/wlcore/main.c | 21 +++++++++++++++------ 3 files changed, 22 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.c b/drivers/net/wireless/ti/wlcore/io.c index 7cd0081aede5..62d657389996 100644 --- a/drivers/net/wireless/ti/wlcore/io.c +++ b/drivers/net/wireless/ti/wlcore/io.c @@ -48,6 +48,12 @@ void wlcore_disable_interrupts(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wlcore_disable_interrupts); +void wlcore_disable_interrupts_nosync(struct wl1271 *wl) +{ + disable_irq_nosync(wl->irq); +} +EXPORT_SYMBOL_GPL(wlcore_disable_interrupts_nosync); + void wlcore_enable_interrupts(struct wl1271 *wl) { enable_irq(wl->irq); diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 404cb14458eb..bbaf7117204e 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -45,6 +45,7 @@ struct wl1271; void wlcore_disable_interrupts(struct wl1271 *wl); +void wlcore_disable_interrupts_nosync(struct wl1271 *wl); void wlcore_enable_interrupts(struct wl1271 *wl); void wl1271_io_reset(struct wl1271 *wl); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 78edc58da210..c94351a92419 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -743,8 +743,11 @@ out: void wl12xx_queue_recovery_work(struct wl1271 *wl) { - if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) + /* Avoid a recursive recovery */ + if (!test_and_set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { + wlcore_disable_interrupts_nosync(wl); ieee80211_queue_work(wl->hw, &wl->recovery_work); + } } size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) @@ -848,9 +851,6 @@ static void wl1271_recovery_work(struct work_struct *work) if (wl->state != WL1271_STATE_ON || wl->plt) goto out_unlock; - /* Avoid a recursive recovery */ - set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); - wl12xx_read_fwlog_panic(wl); /* change partitions momentarily so we can read the FW pc */ @@ -902,8 +902,6 @@ static void wl1271_recovery_work(struct work_struct *work) mutex_unlock(&wl->mutex); wl1271_op_stop(wl->hw); - clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); - ieee80211_restart_hw(wl->hw); /* @@ -1706,6 +1704,10 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wlcore_disable_interrupts(wl); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { + if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, + &wl->flags)) + wlcore_enable_interrupts(wl); + mutex_unlock(&wl->mutex); /* @@ -1737,6 +1739,13 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) mutex_lock(&wl->mutex); wl1271_power_off(wl); + /* + * In case a recovery was scheduled, interrupts were disabled to avoid + * an interrupt storm. Now that the power is down, it is safe to + * re-enable interrupts to balance the disable depth + */ + if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) + wlcore_enable_interrupts(wl); wl->band = IEEE80211_BAND_2GHZ; -- cgit v1.2.3 From 02eb1d9d3bc307e2b540b8c095fa19342789f86d Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sun, 17 Jun 2012 20:30:05 +0300 Subject: wlcore: Change read/write ops to return errors While bus operations may fail, either due to HW or FW issues, these are never propagated to higher layers. As a result, the core driver has no way of knowing that the operations failed, and will only recover if high level logic requires it (e.g. no command completion). Change read/write bus operations to return errors to let higher layer functionality handle these. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/sdio.c | 16 ++++++++++------ drivers/net/wireless/ti/wlcore/spi.c | 14 +++++++++----- drivers/net/wireless/ti/wlcore/wlcore_i.h | 6 +++--- 3 files changed, 22 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 4edaa20acfb1..9069dc93b1bc 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -71,8 +71,8 @@ static void wl1271_sdio_set_block_size(struct device *child, sdio_release_host(func); } -static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, + size_t len, bool fixed) { int ret; struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); @@ -103,12 +103,14 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, sdio_release_host(func); - if (ret) + if (WARN_ON(ret)) dev_err(child->parent, "sdio read failed (%d)\n", ret); + + return ret; } -static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, + size_t len, bool fixed) { int ret; struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); @@ -139,8 +141,10 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, sdio_release_host(func); - if (ret) + if (WARN_ON(ret)) dev_err(child->parent, "sdio write failed (%d)\n", ret); + + return ret; } static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue) diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 553cd3cbb98c..d6768e9d7dab 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -193,8 +193,8 @@ static int wl12xx_spi_read_busy(struct device *child) return -ETIMEDOUT; } -static void wl12xx_spi_raw_read(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int wl12xx_spi_raw_read(struct device *child, int addr, void *buf, + size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct wl1271 *wl = dev_get_drvdata(child); @@ -238,7 +238,7 @@ static void wl12xx_spi_raw_read(struct device *child, int addr, void *buf, if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1) && wl12xx_spi_read_busy(child)) { memset(buf, 0, chunk_len); - return; + return 0; } spi_message_init(&m); @@ -256,10 +256,12 @@ static void wl12xx_spi_raw_read(struct device *child, int addr, void *buf, buf += chunk_len; len -= chunk_len; } + + return 0; } -static void wl12xx_spi_raw_write(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int wl12xx_spi_raw_write(struct device *child, int addr, void *buf, + size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; @@ -304,6 +306,8 @@ static void wl12xx_spi_raw_write(struct device *child, int addr, void *buf, } spi_sync(to_spi_device(glue->dev), &m); + + return 0; } static struct wl1271_if_operations spi_ops = { diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 8260b1e9288a..5ab31ff4080e 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -209,9 +209,9 @@ struct wl1271_scan { }; struct wl1271_if_operations { - void (*read)(struct device *child, int addr, void *buf, size_t len, - bool fixed); - void (*write)(struct device *child, int addr, void *buf, size_t len, + int (*read)(struct device *child, int addr, void *buf, size_t len, + bool fixed); + int (*write)(struct device *child, int addr, void *buf, size_t len, bool fixed); void (*reset)(struct device *child); void (*init)(struct device *child); -- cgit v1.2.3 From 0c2a6ce04eb4d742170a4ddfeb57263fb7964698 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sun, 17 Jun 2012 21:29:51 +0300 Subject: wlcore: Change raw io functions to return errors Make wl1271_raw_write and wl1271_raw_read return errors so the driver could handle these appropriately. Since the prototype has changed, also rename the prefix of these functions to wlcore. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/io.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index bbaf7117204e..60b95033cdde 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -53,33 +53,33 @@ void wl1271_io_init(struct wl1271 *wl); int wlcore_translate_addr(struct wl1271 *wl, int addr); /* Raw target IO, address is not translated */ -static inline void wl1271_raw_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int wlcore_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { - wl->if_ops->write(wl->dev, addr, buf, len, fixed); + return wl->if_ops->write(wl->dev, addr, buf, len, fixed); } -static inline void wl1271_raw_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int wlcore_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { - wl->if_ops->read(wl->dev, addr, buf, len, fixed); + return wl->if_ops->read(wl->dev, addr, buf, len, fixed); } static inline void wlcore_raw_read_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { - wl1271_raw_read(wl, wl->rtable[reg], buf, len, fixed); + wlcore_raw_read(wl, wl->rtable[reg], buf, len, fixed); } static inline void wlcore_raw_write_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { - wl1271_raw_write(wl, wl->rtable[reg], buf, len, fixed); + wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed); } static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) { - wl1271_raw_read(wl, addr, &wl->buffer_32, + wlcore_raw_read(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32), false); return le32_to_cpu(wl->buffer_32); @@ -88,7 +88,7 @@ static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) { wl->buffer_32 = cpu_to_le32(val); - wl1271_raw_write(wl, addr, &wl->buffer_32, + wlcore_raw_write(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32), false); } @@ -99,7 +99,7 @@ static inline void wl1271_read(struct wl1271 *wl, int addr, void *buf, physical = wlcore_translate_addr(wl, addr); - wl1271_raw_read(wl, physical, buf, len, fixed); + wlcore_raw_read(wl, physical, buf, len, fixed); } static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf, @@ -109,7 +109,7 @@ static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf, physical = wlcore_translate_addr(wl, addr); - wl1271_raw_write(wl, physical, buf, len, fixed); + wlcore_raw_write(wl, physical, buf, len, fixed); } static inline void wlcore_write_data(struct wl1271 *wl, int reg, void *buf, @@ -135,7 +135,7 @@ static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr, physical = wlcore_translate_addr(wl, addr); - wl1271_raw_read(wl, physical, buf, len, fixed); + wlcore_raw_read(wl, physical, buf, len, fixed); } static inline u32 wl1271_read32(struct wl1271 *wl, int addr) -- cgit v1.2.3 From 8b7c0fc3569693c3a68103b7d5a7dad5b84109bc Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Sun, 17 Jun 2012 21:59:42 +0300 Subject: wlcore: Propagate errors from wlcore_raw_*_data functions wlcore_raw_read_data is called when the FW status is read which happens while handling interrupts and when the FW log is read following a recovery. Request a recovery in the former case, and don't read the FW log in case the FW status read failed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/io.h | 12 ++++++------ drivers/net/wireless/ti/wlcore/main.c | 27 ++++++++++++++++++++------- 2 files changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 60b95033cdde..2713ce11e21b 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -65,16 +65,16 @@ static inline int wlcore_raw_read(struct wl1271 *wl, int addr, void *buf, return wl->if_ops->read(wl->dev, addr, buf, len, fixed); } -static inline void wlcore_raw_read_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int wlcore_raw_read_data(struct wl1271 *wl, int reg, void *buf, + size_t len, bool fixed) { - wlcore_raw_read(wl, wl->rtable[reg], buf, len, fixed); + return wlcore_raw_read(wl, wl->rtable[reg], buf, len, fixed); } -static inline void wlcore_raw_write_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int wlcore_raw_write_data(struct wl1271 *wl, int reg, void *buf, + size_t len, bool fixed) { - wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed); + return wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed); } static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index c94351a92419..085cd17fa074 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -378,9 +378,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, } } -static void wl12xx_fw_status(struct wl1271 *wl, - struct wl_fw_status_1 *status_1, - struct wl_fw_status_2 *status_2) +static int wlcore_fw_status(struct wl1271 *wl, + struct wl_fw_status_1 *status_1, + struct wl_fw_status_2 *status_2) { struct wl12xx_vif *wlvif; struct timespec ts; @@ -388,12 +388,15 @@ static void wl12xx_fw_status(struct wl1271 *wl, int avail, freed_blocks; int i; size_t status_len; + int ret; status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + sizeof(*status_2) + wl->fw_status_priv_len; - wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1, - status_len, false); + ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1, + status_len, false); + if (ret < 0) + return ret; wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", @@ -462,6 +465,8 @@ static void wl12xx_fw_status(struct wl1271 *wl, getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - (s64)le32_to_cpu(status_2->fw_localtime); + + return 0; } static void wl1271_flush_deferred_work(struct wl1271 *wl) @@ -530,7 +535,11 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); smp_mb__after_clear_bit(); - wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } wlcore_hw_tx_immediate_compl(wl); @@ -781,6 +790,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) u32 offset; u32 end_of_log; u8 *block; + int ret; if ((wl->quirks & WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED) || (wl->conf.fwlog.mem_blocks == 0)) @@ -802,7 +812,10 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ - wl12xx_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + if (ret < 0) + goto out; + addr = le32_to_cpu(wl->fw_status_2->log_start_addr); if (!addr) goto out; -- cgit v1.2.3 From 045b9b5f4172b2b21af0b9bf5e6dda51146d51a4 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Jun 2012 12:31:16 +0300 Subject: wlcore: Propagate errors from wl1271_read Propagate errors from wl1271_read and request for recovery when appropriate. Also rename prefixes of wlcore functions which their prototypes had to be changed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 6 +++--- drivers/net/wireless/ti/wlcore/boot.c | 4 +++- drivers/net/wireless/ti/wlcore/cmd.c | 20 +++++++++++++++----- drivers/net/wireless/ti/wlcore/event.c | 6 ++++-- drivers/net/wireless/ti/wlcore/hw_ops.h | 6 ++++-- drivers/net/wireless/ti/wlcore/io.h | 10 +++++----- drivers/net/wireless/ti/wlcore/main.c | 24 ++++++++++++++++++++---- drivers/net/wireless/ti/wlcore/rx.c | 13 ++++++++++--- drivers/net/wireless/ti/wlcore/rx.h | 2 +- drivers/net/wireless/ti/wlcore/tx.c | 15 +++++++++++---- drivers/net/wireless/ti/wlcore/tx.h | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- 12 files changed, 78 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 0d2fdca2aa32..916cee76b37f 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1162,13 +1162,13 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, return data_len - sizeof(*desc) - desc->pad_len; } -static void wl12xx_tx_delayed_compl(struct wl1271 *wl) +static int wl12xx_tx_delayed_compl(struct wl1271 *wl) { if (wl->fw_status_1->tx_results_counter == (wl->tx_results_count & 0xff)) - return; + return 0; - wl1271_tx_complete(wl); + return wlcore_tx_complete(wl); } static int wl12xx_hw_init(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 0fda500c01c9..d7abc505f2a3 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -87,7 +87,9 @@ static int wlcore_boot_static_data(struct wl1271 *wl) goto out; } - wl1271_read(wl, wl->cmd_box_addr, static_data, len, false); + ret = wlcore_read(wl, wl->cmd_box_addr, static_data, len, false); + if (ret < 0) + goto out_free; ret = wlcore_boot_parse_fw_ver(wl, static_data); if (ret < 0) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 885364ca4344..69f27d1fdcdf 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -95,7 +95,10 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, /* read back the status code of the command */ if (res_len == 0) res_len = sizeof(struct wl1271_cmd_header); - wl1271_read(wl, wl->cmd_box_addr, cmd, res_len, false); + + ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false); + if (ret < 0) + goto fail; status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { @@ -141,11 +144,18 @@ static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask) msleep(1); /* read from both event fields */ - wl1271_read(wl, wl->mbox_ptr[0], events_vector, - sizeof(*events_vector), false); + ret = wlcore_read(wl, wl->mbox_ptr[0], events_vector, + sizeof(*events_vector), false); + if (ret < 0) + goto out; + event = *events_vector & mask; - wl1271_read(wl, wl->mbox_ptr[1], events_vector, - sizeof(*events_vector), false); + + ret = wlcore_read(wl, wl->mbox_ptr[1], events_vector, + sizeof(*events_vector), false); + if (ret < 0) + goto out; + event |= *events_vector & mask; } while (!event); diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index c976f0409865..858ac33f5980 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -301,8 +301,10 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) return -EINVAL; /* first we read the mbox descriptor */ - wl1271_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, - sizeof(*wl->mbox), false); + ret = wlcore_read(wl, wl->mbox_ptr[mbox_num], wl->mbox, + sizeof(*wl->mbox), false); + if (ret < 0) + return ret; /* process the descriptor */ ret = wl1271_event_process(wl); diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 9e7787ba9610..f44d586048ab 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -81,10 +81,12 @@ wlcore_hw_get_rx_packet_len(struct wl1271 *wl, void *rx_data, u32 data_len) return wl->ops->get_rx_packet_len(wl, rx_data, data_len); } -static inline void wlcore_hw_tx_delayed_compl(struct wl1271 *wl) +static inline int wlcore_hw_tx_delayed_compl(struct wl1271 *wl) { if (wl->ops->tx_delayed_compl) - wl->ops->tx_delayed_compl(wl); + return wl->ops->tx_delayed_compl(wl); + + return 0; } static inline void wlcore_hw_tx_immediate_compl(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 2713ce11e21b..d114bb42a924 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -92,14 +92,14 @@ static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) sizeof(wl->buffer_32), false); } -static inline void wl1271_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int wlcore_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { int physical; physical = wlcore_translate_addr(wl, addr); - wlcore_raw_read(wl, physical, buf, len, fixed); + return wlcore_raw_read(wl, physical, buf, len, fixed); } static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf, @@ -118,10 +118,10 @@ static inline void wlcore_write_data(struct wl1271 *wl, int reg, void *buf, wl1271_write(wl, wl->rtable[reg], buf, len, fixed); } -static inline void wlcore_read_data(struct wl1271 *wl, int reg, void *buf, +static inline int wlcore_read_data(struct wl1271 *wl, int reg, void *buf, size_t len, bool fixed) { - wl1271_read(wl, wl->rtable[reg], buf, len, fixed); + return wlcore_read(wl, wl->rtable[reg], buf, len, fixed); } static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 085cd17fa074..deb22f8e193d 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -572,7 +572,11 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (likely(intr & WL1271_ACX_INTR_DATA)) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); - wl12xx_rx(wl, wl->fw_status_1); + ret = wlcore_rx(wl, wl->fw_status_1); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } /* Check if any tx blocks were freed */ spin_lock_irqsave(&wl->wl_lock, flags); @@ -589,7 +593,11 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) } /* check for tx results */ - wlcore_hw_tx_delayed_compl(wl); + ret = wlcore_hw_tx_delayed_compl(wl); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } /* Make sure the deferred queues don't get too long */ defer_count = skb_queue_len(&wl->deferred_tx_queue) + @@ -600,12 +608,20 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (intr & WL1271_ACX_INTR_EVENT_A) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); - wl1271_event_handle(wl, 0); + ret = wl1271_event_handle(wl, 0); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } } if (intr & WL1271_ACX_INTR_EVENT_B) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); - wl1271_event_handle(wl, 1); + ret = wl1271_event_handle(wl, 1); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } } if (intr & WL1271_ACX_INTR_INIT_COMPLETE) diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index a1db4e032409..59d0956c5d09 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -200,7 +200,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, return is_data; } -void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) +int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) { unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; u32 buf_size; @@ -211,6 +211,7 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) u32 pkt_offset, des; u8 hlid; enum wl_rx_buf_align rx_align; + int ret = 0; while (drv_rx_counter != fw_rx_counter) { buf_size = 0; @@ -235,8 +236,11 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) /* Read all available packets at once */ des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]); wlcore_hw_prepare_read(wl, des, buf_size); - wlcore_read_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, - buf_size, true); + + ret = wlcore_read_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, + buf_size, true); + if (ret < 0) + goto out; /* Split data into separate packets */ pkt_offset = 0; @@ -278,6 +282,9 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) wl->rx_counter); wl12xx_rearm_rx_streaming(wl, active_hlids); + +out: + return ret; } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 4324a427e835..79f7839a06e2 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -143,7 +143,7 @@ struct wl1271_rx_descriptor { u8 reserved; } __packed; -void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status); +int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); int wl1271_rx_filter_enable(struct wl1271 *wl, int index, bool enable, diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 8ee82b9f93f4..fc890cba8d39 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -881,16 +881,20 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, } /* Called upon reception of a TX complete interrupt */ -void wl1271_tx_complete(struct wl1271 *wl) +int wlcore_tx_complete(struct wl1271 *wl) { struct wl1271_acx_mem_map *memmap = (struct wl1271_acx_mem_map *)wl->target_mem_map; u32 count, fw_counter; u32 i; + int ret; /* read the tx results from the chipset */ - wl1271_read(wl, le32_to_cpu(memmap->tx_result), - wl->tx_res_if, sizeof(*wl->tx_res_if), false); + ret = wlcore_read(wl, le32_to_cpu(memmap->tx_result), + wl->tx_res_if, sizeof(*wl->tx_res_if), false); + if (ret < 0) + goto out; + fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter); /* write host counter to chipset (to ack) */ @@ -916,8 +920,11 @@ void wl1271_tx_complete(struct wl1271 *wl) wl->tx_results_count++; } + +out: + return ret; } -EXPORT_SYMBOL(wl1271_tx_complete); +EXPORT_SYMBOL(wlcore_tx_complete); void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) { diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index fa4be1b91135..10540944a80c 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -235,7 +235,7 @@ static inline int wl1271_tx_total_queue_count(struct wl1271 *wl) void wl1271_tx_work(struct work_struct *work); void wl1271_tx_work_locked(struct wl1271 *wl); -void wl1271_tx_complete(struct wl1271 *wl); +int wlcore_tx_complete(struct wl1271 *wl); void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_tx_reset(struct wl1271 *wl); void wl1271_tx_flush(struct wl1271 *wl); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 205d8ad2b761..fd37307ebb93 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -56,7 +56,7 @@ struct wlcore_ops { void (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len); u32 (*get_rx_packet_len)(struct wl1271 *wl, void *rx_data, u32 data_len); - void (*tx_delayed_compl)(struct wl1271 *wl); + int (*tx_delayed_compl)(struct wl1271 *wl); void (*tx_immediate_compl)(struct wl1271 *wl); int (*hw_init)(struct wl1271 *wl); int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif); -- cgit v1.2.3 From eb96f841b9563ba34969be25615548635728faf5 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Jun 2012 13:21:55 +0300 Subject: wlcore: Propagate errors from wl1271_write Propagate errors from wl1271_write and request for recovery when appropriate. Also rename prefixes of wlcore functions which their prototypes had to be changed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 23 ++++++++++++++++++----- drivers/net/wireless/ti/wl18xx/main.c | 19 ++++++++++++------- drivers/net/wireless/ti/wlcore/boot.c | 17 +++++++++++------ drivers/net/wireless/ti/wlcore/cmd.c | 12 +++++++++--- drivers/net/wireless/ti/wlcore/event.c | 5 ++++- drivers/net/wireless/ti/wlcore/hw_ops.h | 6 ++++-- drivers/net/wireless/ti/wlcore/io.h | 12 ++++++------ drivers/net/wireless/ti/wlcore/main.c | 13 ++++++++++--- drivers/net/wireless/ti/wlcore/rx.c | 4 +++- drivers/net/wireless/ti/wlcore/tx.c | 29 +++++++++++++++++++++-------- drivers/net/wireless/ti/wlcore/tx.h | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 6 +++--- 12 files changed, 102 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 916cee76b37f..257745fbdff9 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -598,8 +598,10 @@ static const int wl12xx_rtable[REG_TABLE_LEN] = { #define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" -static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) +static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) { + int ret; + if (wl->chip.id != CHIP_ID_1283_PG20) { struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; struct wl127x_rx_mem_pool_addr rx_mem_addr; @@ -616,9 +618,13 @@ static void wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; - wl1271_write(wl, WL1271_SLV_REG_DATA, - &rx_mem_addr, sizeof(rx_mem_addr), false); + ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr, + sizeof(rx_mem_addr), false); + if (ret < 0) + return ret; } + + return 0; } static int wl12xx_identify_chip(struct wl1271 *wl) @@ -1073,11 +1079,18 @@ out: return ret; } -static void wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, +static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len) { - wl1271_write(wl, cmd_box_addr, buf, len, false); + int ret; + + ret = wlcore_write(wl, cmd_box_addr, buf, len, false); + if (ret < 0) + return ret; + wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD); + + return ret; } static void wl12xx_ack_event(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 271ff81da922..974a6ff11f6d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -720,10 +720,11 @@ static void wl18xx_pre_upload(struct wl1271 *wl) tmp = wl1271_read32(wl, WL18XX_SCR_PAD2); } -static void wl18xx_set_mac_and_phy(struct wl1271 *wl) +static int wl18xx_set_mac_and_phy(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; size_t len; + int ret; /* the parameters struct is smaller for PG1 */ if (wl->chip.id == CHIP_ID_185x_PG10) @@ -732,8 +733,10 @@ static void wl18xx_set_mac_and_phy(struct wl1271 *wl) len = sizeof(struct wl18xx_mac_and_phy_params); wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); - wl1271_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, len, - false); + ret = wlcore_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, + len, false); + + return ret; } static void wl18xx_enable_interrupts(struct wl1271 *wl) @@ -769,7 +772,9 @@ static int wl18xx_boot(struct wl1271 *wl) if (ret < 0) goto out; - wl18xx_set_mac_and_phy(wl); + ret = wl18xx_set_mac_and_phy(wl); + if (ret < 0) + goto out; ret = wlcore_boot_run_firmware(wl); if (ret < 0) @@ -781,7 +786,7 @@ out: return ret; } -static void wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, +static int wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len) { struct wl18xx_priv *priv = wl->priv; @@ -789,8 +794,8 @@ static void wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, memcpy(priv->cmd_buf, buf, len); memset(priv->cmd_buf + len, 0, WL18XX_CMD_MAX_SIZE - len); - wl1271_write(wl, cmd_box_addr, priv->cmd_buf, WL18XX_CMD_MAX_SIZE, - false); + return wlcore_write(wl, cmd_box_addr, priv->cmd_buf, + WL18XX_CMD_MAX_SIZE, false); } static void wl18xx_ack_event(struct wl1271 *wl) diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index d7abc505f2a3..ee7a401478a9 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -111,6 +111,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, struct wlcore_partition_set partition; int addr, chunk_num, partition_limit; u8 *p, *chunk; + int ret; /* whal_FwCtrl_LoadFwImageSm() */ @@ -155,7 +156,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(chunk, p, CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", p, addr); - wl1271_write(wl, addr, chunk, CHUNK_SIZE, false); + ret = wlcore_write(wl, addr, chunk, CHUNK_SIZE, false); + if (ret < 0) + goto out; chunk_num++; } @@ -166,10 +169,11 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(chunk, p, fw_data_len % CHUNK_SIZE); wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x", fw_data_len % CHUNK_SIZE, p, addr); - wl1271_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); + ret = wlcore_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE, false); +out: kfree(chunk); - return 0; + return ret; } int wlcore_boot_upload_firmware(struct wl1271 *wl) @@ -212,6 +216,7 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl) int i; u32 dest_addr, val; u8 *nvs_ptr, *nvs_aligned; + int ret; if (wl->nvs == NULL) { wl1271_error("NVS file is needed during boot"); @@ -343,11 +348,11 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl) return -ENOMEM; /* And finally we upload the NVS tables */ - wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, - nvs_aligned, nvs_len, false); + ret = wlcore_write_data(wl, REG_CMD_MBOX_ADDRESS, nvs_aligned, nvs_len, + false); kfree(nvs_aligned); - return 0; + return ret; out_badnvs: wl1271_error("nvs data is malformed"); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 69f27d1fdcdf..658dccbfd7ed 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -65,13 +65,17 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, WARN_ON(len % 4 != 0); WARN_ON(test_bit(WL1271_FLAG_IN_ELP, &wl->flags)); - wl1271_write(wl, wl->cmd_box_addr, buf, len, false); + ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false); + if (ret < 0) + goto fail; /* * TODO: we just need this because one bit is in a different * place. Is there any better way? */ - wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len); + ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len); + if (ret < 0) + goto fail; timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); @@ -1764,7 +1768,9 @@ int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif) return -EINVAL; /* flush all pending packets */ - wl1271_tx_work_locked(wl); + ret = wlcore_tx_work_locked(wl); + if (ret < 0) + goto out; if (test_bit(wlvif->dev_role_id, wl->roc_map)) { ret = wl12xx_croc(wl, wlvif->dev_role_id); diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 858ac33f5980..123d26d17ba4 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -105,6 +105,7 @@ static int wl1271_event_process(struct wl1271 *wl) u32 vector; bool disconnect_sta = false; unsigned long sta_bitmap = 0; + int ret; wl1271_event_mbox_dump(mbox); @@ -228,7 +229,9 @@ static int wl1271_event_process(struct wl1271 *wl) if ((vector & DUMMY_PACKET_EVENT_ID)) { wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); - wl1271_tx_dummy_packet(wl); + ret = wl1271_tx_dummy_packet(wl); + if (ret < 0) + return ret; } /* diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index f44d586048ab..2673d783ec1e 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -65,11 +65,13 @@ wlcore_hw_get_rx_buf_align(struct wl1271 *wl, u32 rx_desc) return wl->ops->get_rx_buf_align(wl, rx_desc); } -static inline void +static inline int wlcore_hw_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len) { if (wl->ops->prepare_read) - wl->ops->prepare_read(wl, rx_desc, len); + return wl->ops->prepare_read(wl, rx_desc, len); + + return 0; } static inline u32 diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index d114bb42a924..2cbf7623ddbb 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -102,20 +102,20 @@ static inline int wlcore_read(struct wl1271 *wl, int addr, void *buf, return wlcore_raw_read(wl, physical, buf, len, fixed); } -static inline void wl1271_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int wlcore_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { int physical; physical = wlcore_translate_addr(wl, addr); - wlcore_raw_write(wl, physical, buf, len, fixed); + return wlcore_raw_write(wl, physical, buf, len, fixed); } -static inline void wlcore_write_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int wlcore_write_data(struct wl1271 *wl, int reg, void *buf, + size_t len, bool fixed) { - wl1271_write(wl, wl->rtable[reg], buf, len, fixed); + return wlcore_write(wl, wl->rtable[reg], buf, len, fixed); } static inline int wlcore_read_data(struct wl1271 *wl, int reg, void *buf, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index deb22f8e193d..0461d4eecfd2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -587,7 +587,11 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) * In order to avoid starvation of the TX path, * call the work function directly. */ - wl1271_tx_work_locked(wl); + ret = wlcore_tx_work_locked(wl); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } } else { spin_unlock_irqrestore(&wl->wl_lock, flags); } @@ -1211,7 +1215,7 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) /* The FW is low on RX memory blocks, so send the dummy packet asap */ if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) - wl1271_tx_work_locked(wl); + return wlcore_tx_work_locked(wl); /* * If the FW TX is busy, TX work will be scheduled by the threaded @@ -2513,7 +2517,10 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, (wlvif->channel != channel) || (wlvif->channel_type != conf->channel_type))) { /* send all pending packets */ - wl1271_tx_work_locked(wl); + ret = wlcore_tx_work_locked(wl); + if (ret < 0) + return ret; + wlvif->band = conf->channel->band; wlvif->channel = channel; wlvif->channel_type = conf->channel_type; diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 59d0956c5d09..be24b3030f92 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -235,7 +235,9 @@ int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) /* Read all available packets at once */ des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]); - wlcore_hw_prepare_read(wl, des, buf_size); + ret = wlcore_hw_prepare_read(wl, des, buf_size); + if (ret < 0) + goto out; ret = wlcore_read_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, buf_size, true); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index fc890cba8d39..90bddf56f8ed 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -662,7 +662,7 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids) } } -void wl1271_tx_work_locked(struct wl1271 *wl) +int wlcore_tx_work_locked(struct wl1271 *wl) { struct wl12xx_vif *wlvif; struct sk_buff *skb; @@ -670,10 +670,10 @@ void wl1271_tx_work_locked(struct wl1271 *wl) u32 buf_offset = 0, last_len = 0; bool sent_packets = false; unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; - int ret; + int ret = 0; if (unlikely(wl->state == WL1271_STATE_OFF)) - return; + return -EIO; while ((skb = wl1271_skb_dequeue(wl))) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -694,8 +694,11 @@ void wl1271_tx_work_locked(struct wl1271 *wl) buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); - wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, - buf_offset, true); + ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, + wl->aggr_buf, buf_offset, true); + if (ret < 0) + goto out; + sent_packets = true; buf_offset = 0; continue; @@ -731,8 +734,11 @@ void wl1271_tx_work_locked(struct wl1271 *wl) out_ack: if (buf_offset) { buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); - wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, - buf_offset, true); + ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, + buf_offset, true); + if (ret < 0) + goto out; + sent_packets = true; } if (sent_packets) { @@ -747,6 +753,9 @@ out_ack: wl1271_handle_tx_low_watermark(wl); } wl12xx_rearm_rx_streaming(wl, active_hlids); + +out: + return ret; } void wl1271_tx_work(struct work_struct *work) @@ -759,7 +768,11 @@ void wl1271_tx_work(struct work_struct *work) if (ret < 0) goto out; - wl1271_tx_work_locked(wl); + ret = wlcore_tx_work_locked(wl); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } wl1271_ps_elp_sleep(wl); out: diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 10540944a80c..1e939b016155 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -234,7 +234,7 @@ static inline int wl1271_tx_total_queue_count(struct wl1271 *wl) } void wl1271_tx_work(struct work_struct *work); -void wl1271_tx_work_locked(struct wl1271 *wl); +int wlcore_tx_work_locked(struct wl1271 *wl); int wlcore_tx_complete(struct wl1271 *wl); void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_tx_reset(struct wl1271 *wl); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index fd37307ebb93..5d51647e6154 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -41,8 +41,8 @@ struct wlcore_ops { int (*identify_fw)(struct wl1271 *wl); int (*boot)(struct wl1271 *wl); int (*plt_init)(struct wl1271 *wl); - void (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, - void *buf, size_t len); + int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, + void *buf, size_t len); void (*ack_event)(struct wl1271 *wl); u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks); void (*set_tx_desc_blocks)(struct wl1271 *wl, @@ -53,7 +53,7 @@ struct wlcore_ops { struct sk_buff *skb); enum wl_rx_buf_align (*get_rx_buf_align)(struct wl1271 *wl, u32 rx_desc); - void (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len); + int (*prepare_read)(struct wl1271 *wl, u32 rx_desc, u32 len); u32 (*get_rx_packet_len)(struct wl1271 *wl, void *rx_data, u32 data_len); int (*tx_delayed_compl)(struct wl1271 *wl); -- cgit v1.2.3 From 6134323f42b0dbae8e8206414d26cb167b9bedfc Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Jun 2012 15:50:21 +0300 Subject: wlcore: Propagate errors from wl1271_raw_read32 Propagate errors from wl1271_raw_read32. Since the read functions had no way of returning errors in-band, change their prototypes. Also rename prefixes of wlcore functions which their prototypes had to be changed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 152 ++++++++++++++++++++++++-------- drivers/net/wireless/ti/wl18xx/io.c | 35 +++++--- drivers/net/wireless/ti/wl18xx/io.h | 4 +- drivers/net/wireless/ti/wl18xx/main.c | 114 +++++++++++++++++------- drivers/net/wireless/ti/wlcore/boot.c | 31 +++++-- drivers/net/wireless/ti/wlcore/cmd.c | 9 +- drivers/net/wireless/ti/wlcore/io.h | 26 ++++-- drivers/net/wireless/ti/wlcore/main.c | 47 +++++++--- drivers/net/wireless/ti/wlcore/wlcore.h | 4 +- 9 files changed, 309 insertions(+), 113 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 257745fbdff9..ab486f71eee4 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -701,10 +701,11 @@ static void wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE); } -static u16 wl12xx_top_reg_read(struct wl1271 *wl, int addr) +static int wl12xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) { u32 val; int timeout = OCP_CMD_LOOP; + int ret; /* write address >> 1 + 0x30000 to OCP_POR_CTR */ addr = (addr >> 1) + 0x30000; @@ -715,29 +716,38 @@ static u16 wl12xx_top_reg_read(struct wl1271 *wl, int addr) /* poll for data ready */ do { - val = wl1271_read32(wl, WL12XX_OCP_DATA_READ); + ret = wlcore_read32(wl, WL12XX_OCP_DATA_READ, &val); + if (ret < 0) + return ret; } while (!(val & OCP_READY_MASK) && --timeout); if (!timeout) { wl1271_warning("Top register access timed out."); - return 0xffff; + return -ETIMEDOUT; } /* check data status and return if OK */ - if ((val & OCP_STATUS_MASK) == OCP_STATUS_OK) - return val & 0xffff; - else { + if ((val & OCP_STATUS_MASK) != OCP_STATUS_OK) { wl1271_warning("Top register access returned error."); - return 0xffff; + return -EIO; } + + if (out) + *out = val & 0xffff; + + return 0; } static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) { u16 spare_reg; + int ret; /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ - spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); + ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg); + if (ret < 0) + return ret; + if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= (BIT(3) | BIT(5) | BIT(6)); @@ -756,8 +766,12 @@ static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) static bool wl128x_is_tcxo_valid(struct wl1271 *wl) { u16 tcxo_detection; + int ret; + + ret = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG, &tcxo_detection); + if (ret < 0) + return false; - tcxo_detection = wl12xx_top_reg_read(wl, TCXO_CLK_DETECT_REG); if (tcxo_detection & TCXO_DET_FAILED) return false; @@ -767,8 +781,12 @@ static bool wl128x_is_tcxo_valid(struct wl1271 *wl) static bool wl128x_is_fref_valid(struct wl1271 *wl) { u16 fref_detection; + int ret; + + ret = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG, &fref_detection); + if (ret < 0) + return false; - fref_detection = wl12xx_top_reg_read(wl, FREF_CLK_DETECT_REG); if (fref_detection & FREF_CLK_DETECT_FAIL) return false; @@ -790,9 +808,13 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) u16 pll_config; u8 input_freq; struct wl12xx_priv *priv = wl->priv; + int ret; /* Mask bits [3:1] in the sys_clk_cfg register */ - spare_reg = wl12xx_top_reg_read(wl, WL_SPARE_REG); + ret = wl12xx_top_reg_read(wl, WL_SPARE_REG, &spare_reg); + if (ret < 0) + return ret; + if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= BIT(2); @@ -806,7 +828,10 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) /* Set the input frequency according to the selected clock source */ input_freq = (clk & 1) + 1; - pll_config = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG); + ret = wl12xx_top_reg_read(wl, MCS_PLL_CONFIG_REG, &pll_config); + if (ret < 0) + return ret; + if (pll_config == 0xFFFF) return -EFAULT; pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); @@ -827,6 +852,7 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) { struct wl12xx_priv *priv = wl->priv; u16 sys_clk_cfg; + int ret; /* For XTAL-only modes, FREF will be used after switching from TCXO */ if (priv->ref_clock == WL12XX_REFCLOCK_26_XTAL || @@ -837,7 +863,10 @@ static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) } /* Query the HW, to determine which clock source we should use */ - sys_clk_cfg = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG); + ret = wl12xx_top_reg_read(wl, SYS_CLK_CFG_REG, &sys_clk_cfg); + if (ret < 0) + return ret; + if (sys_clk_cfg == 0xFFFF) return -EINVAL; if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) @@ -872,6 +901,7 @@ static int wl127x_boot_clk(struct wl1271 *wl) struct wl12xx_priv *priv = wl->priv; u32 pause; u32 clk; + int ret; if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3) wl->quirks |= WLCORE_QUIRK_END_OF_TRANSACTION; @@ -892,18 +922,27 @@ static int wl127x_boot_clk(struct wl1271 *wl) if (priv->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ - val = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE); + ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_TYPE, &val); + if (ret < 0) + goto out; + val &= FREF_CLK_TYPE_BITS; wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val); /* Set clock pull mode (no pull) */ - val = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL); + ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val); + if (ret < 0) + goto out; + val |= NO_PULL; wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val); } else { u16 val; /* Set clock polarity */ - val = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY); + ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_POLARITY, &val); + if (ret < 0) + goto out; + val &= FREF_CLK_POLARITY_BITS; val |= CLK_REQ_OUTN_SEL; wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); @@ -911,7 +950,9 @@ static int wl127x_boot_clk(struct wl1271 *wl) wl1271_write32(wl, WL12XX_PLL_PARAMETERS, clk); - pause = wl1271_read32(wl, WL12XX_PLL_PARAMETERS); + ret = wlcore_read32(wl, WL12XX_PLL_PARAMETERS, &pause); + if (ret < 0) + goto out; wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); @@ -919,13 +960,15 @@ static int wl127x_boot_clk(struct wl1271 *wl) pause |= WU_COUNTER_PAUSE_VAL; wl1271_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause); - return 0; +out: + return ret; } static int wl1271_boot_soft_reset(struct wl1271 *wl) { unsigned long timeout; u32 boot_data; + int ret = 0; /* perform soft reset */ wl1271_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); @@ -933,7 +976,10 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); while (1) { - boot_data = wl1271_read32(wl, WL12XX_SLV_SOFT_RESET); + ret = wlcore_read32(wl, WL12XX_SLV_SOFT_RESET, &boot_data); + if (ret < 0) + goto out; + wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) break; @@ -954,7 +1000,8 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) /* disable auto calibration on start*/ wl1271_write32(wl, WL12XX_SPARE_A2, 0xffff); - return 0; +out: + return ret; } static int wl12xx_pre_boot(struct wl1271 *wl) @@ -984,7 +1031,9 @@ static int wl12xx_pre_boot(struct wl1271 *wl) to be used by DRPw FW. The RTRIM value will be added by the FW before taking DRPw out of reset */ - clk = wl1271_read32(wl, WL12XX_DRPW_SCRATCH_START); + ret = wlcore_read32(wl, WL12XX_DRPW_SCRATCH_START, &clk); + if (ret < 0) + goto out; wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); @@ -1008,9 +1057,11 @@ out: return ret; } -static void wl12xx_pre_upload(struct wl1271 *wl) +static int wl12xx_pre_upload(struct wl1271 *wl) { - u32 tmp, polarity; + u32 tmp; + u16 polarity; + int ret; /* write firmware's last address (ie. it's length) to * ACX_EEPROMLESS_IND_REG */ @@ -1018,12 +1069,16 @@ static void wl12xx_pre_upload(struct wl1271 *wl) wl1271_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND); - tmp = wlcore_read_reg(wl, REG_CHIP_ID_B); + ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp); + if (ret < 0) + goto out; wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); /* 6. read the EEPROM parameters */ - tmp = wl1271_read32(wl, WL12XX_SCR_PAD2); + ret = wlcore_read32(wl, WL12XX_SCR_PAD2, &tmp); + if (ret < 0) + goto out; /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ @@ -1032,12 +1087,16 @@ static void wl12xx_pre_upload(struct wl1271 *wl) wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); /* polarity must be set before the firmware is loaded */ - polarity = wl12xx_top_reg_read(wl, OCP_REG_POLARITY); + ret = wl12xx_top_reg_read(wl, OCP_REG_POLARITY, &polarity); + if (ret < 0) + goto out; /* We use HIGH polarity, so unset the LOW bit */ polarity &= ~POLARITY_LOW; wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); +out: + return ret; } static void wl12xx_enable_interrupts(struct wl1271 *wl) @@ -1063,7 +1122,9 @@ static int wl12xx_boot(struct wl1271 *wl) if (ret < 0) goto out; - wl12xx_pre_upload(wl); + ret = wl12xx_pre_upload(wl); + if (ret < 0) + goto out; ret = wlcore_boot_upload_firmware(wl); if (ret < 0) @@ -1282,14 +1343,20 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl) return supported; } -static void wl12xx_get_fuse_mac(struct wl1271 *wl) +static int wl12xx_get_fuse_mac(struct wl1271 *wl) { u32 mac1, mac2; + int ret; wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); - mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1); - mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2); + ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1); + if (ret < 0) + goto out; + + ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2, &mac2); + if (ret < 0) + goto out; /* these are the two parts of the BD_ADDR */ wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + @@ -1297,24 +1364,35 @@ static void wl12xx_get_fuse_mac(struct wl1271 *wl) wl->fuse_nic_addr = mac1 & 0xffffff; wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); + +out: + return ret; } -static s8 wl12xx_get_pg_ver(struct wl1271 *wl) +static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver) { - u32 die_info; + u16 die_info; + int ret; if (wl->chip.id == CHIP_ID_1283_PG20) - die_info = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1); + ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1, + &die_info); else - die_info = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1); + ret = wl12xx_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1, + &die_info); - return (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET; + if (ret >= 0 && ver) + *ver = (s8)((die_info & PG_VER_MASK) >> PG_VER_OFFSET); + + return ret; } -static void wl12xx_get_mac(struct wl1271 *wl) +static int wl12xx_get_mac(struct wl1271 *wl) { if (wl12xx_mac_in_fuse(wl)) - wl12xx_get_fuse_mac(wl); + return wl12xx_get_fuse_mac(wl); + + return 0; } static void wl12xx_set_tx_desc_csum(struct wl1271 *wl, diff --git a/drivers/net/wireless/ti/wl18xx/io.c b/drivers/net/wireless/ti/wl18xx/io.c index 598c057e722b..92c2c03e4cd8 100644 --- a/drivers/net/wireless/ti/wl18xx/io.c +++ b/drivers/net/wireless/ti/wl18xx/io.c @@ -24,37 +24,52 @@ #include "io.h" -void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) +int wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) { u32 tmp; + int ret; if (WARN_ON(addr % 2)) - return; + return -EINVAL; if ((addr % 4) == 0) { - tmp = wl1271_read32(wl, addr); + ret = wlcore_read32(wl, addr, &tmp); + if (ret < 0) + goto out; + tmp = (tmp & 0xffff0000) | val; wl1271_write32(wl, addr, tmp); } else { - tmp = wl1271_read32(wl, addr - 2); + ret = wlcore_read32(wl, addr - 2, &tmp); + if (ret < 0) + goto out; + tmp = (tmp & 0xffff) | (val << 16); wl1271_write32(wl, addr - 2, tmp); } + +out: + return ret; } -u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr) +int wl18xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) { u32 val; + int ret; if (WARN_ON(addr % 2)) - return 0; + return -EINVAL; if ((addr % 4) == 0) { /* address is 4-bytes aligned */ - val = wl1271_read32(wl, addr); - return val & 0xffff; + ret = wlcore_read32(wl, addr, &val); + if (ret >= 0 && out) + *out = val & 0xffff; } else { - val = wl1271_read32(wl, addr - 2); - return (val & 0xffff0000) >> 16; + ret = wlcore_read32(wl, addr - 2, &val); + if (ret >= 0 && out) + *out = (val & 0xffff0000) >> 16; } + + return ret; } diff --git a/drivers/net/wireless/ti/wl18xx/io.h b/drivers/net/wireless/ti/wl18xx/io.h index be4e126ff617..0e1b8d28edb2 100644 --- a/drivers/net/wireless/ti/wl18xx/io.h +++ b/drivers/net/wireless/ti/wl18xx/io.h @@ -22,7 +22,7 @@ #ifndef __WL18XX_IO_H__ #define __WL18XX_IO_H__ -void wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val); -u16 wl18xx_top_reg_read(struct wl1271 *wl, int addr); +int wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val); +int wl18xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out); #endif /* __WL18XX_IO_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 974a6ff11f6d..f99f003ab182 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -636,45 +636,67 @@ out: return ret; } -static void wl18xx_set_clk(struct wl1271 *wl) +static int wl18xx_set_clk(struct wl1271 *wl) { - u32 clk_freq; + u16 clk_freq; + int ret; wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); /* TODO: PG2: apparently we need to read the clk type */ - clk_freq = wl18xx_top_reg_read(wl, PRIMARY_CLK_DETECT); + ret = wl18xx_top_reg_read(wl, PRIMARY_CLK_DETECT, &clk_freq); + if (ret < 0) + goto out; + wl1271_debug(DEBUG_BOOT, "clock freq %d (%d, %d, %d, %d, %s)", clk_freq, wl18xx_clk_table[clk_freq].n, wl18xx_clk_table[clk_freq].m, wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q, wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit"); - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n); - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_M, wl18xx_clk_table[clk_freq].m); + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, + wl18xx_clk_table[clk_freq].n); + if (ret < 0) + goto out; + + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_M, + wl18xx_clk_table[clk_freq].m); + if (ret < 0) + goto out; if (wl18xx_clk_table[clk_freq].swallow) { /* first the 16 lower bits */ - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_1, - wl18xx_clk_table[clk_freq].q & - PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK); + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_1, + wl18xx_clk_table[clk_freq].q & + PLLSH_WCS_PLL_Q_FACTOR_CFG_1_MASK); + if (ret < 0) + goto out; + /* then the 16 higher bits, masked out */ - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_2, - (wl18xx_clk_table[clk_freq].q >> 16) & - PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK); + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_Q_FACTOR_CFG_2, + (wl18xx_clk_table[clk_freq].q >> 16) & + PLLSH_WCS_PLL_Q_FACTOR_CFG_2_MASK); + if (ret < 0) + goto out; /* first the 16 lower bits */ - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_1, - wl18xx_clk_table[clk_freq].p & - PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK); + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_1, + wl18xx_clk_table[clk_freq].p & + PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK); + if (ret < 0) + goto out; + /* then the 16 higher bits, masked out */ - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_2, - (wl18xx_clk_table[clk_freq].p >> 16) & - PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK); + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_P_FACTOR_CFG_2, + (wl18xx_clk_table[clk_freq].p >> 16) & + PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK); } else { - wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_SWALLOW_EN, - PLLSH_WCS_PLL_SWALLOW_EN_VAL2); + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_SWALLOW_EN, + PLLSH_WCS_PLL_SWALLOW_EN_VAL2); } + +out: + return ret; } static void wl18xx_boot_soft_reset(struct wl1271 *wl) @@ -688,7 +710,11 @@ static void wl18xx_boot_soft_reset(struct wl1271 *wl) static int wl18xx_pre_boot(struct wl1271 *wl) { - wl18xx_set_clk(wl); + int ret; + + ret = wl18xx_set_clk(wl); + if (ret < 0) + goto out; /* Continue the ELP wake up sequence */ wl1271_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); @@ -701,23 +727,30 @@ static int wl18xx_pre_boot(struct wl1271 *wl) wl18xx_boot_soft_reset(wl); - return 0; +out: + return ret; } -static void wl18xx_pre_upload(struct wl1271 *wl) +static int wl18xx_pre_upload(struct wl1271 *wl) { u32 tmp; + int ret; wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); /* TODO: check if this is all needed */ wl1271_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND); - tmp = wlcore_read_reg(wl, REG_CHIP_ID_B); + ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp); + if (ret < 0) + goto out; wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); - tmp = wl1271_read32(wl, WL18XX_SCR_PAD2); + ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp); + +out: + return ret; } static int wl18xx_set_mac_and_phy(struct wl1271 *wl) @@ -766,7 +799,9 @@ static int wl18xx_boot(struct wl1271 *wl) if (ret < 0) goto out; - wl18xx_pre_upload(wl); + ret = wl18xx_pre_upload(wl); + if (ret < 0) + goto out; ret = wlcore_boot_upload_firmware(wl); if (ret < 0) @@ -998,18 +1033,24 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, } } -static s8 wl18xx_get_pg_ver(struct wl1271 *wl) +static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) { u32 fuse; + int ret; wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); - fuse = wl1271_read32(wl, WL18XX_REG_FUSE_DATA_1_3); - fuse = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; + ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_1_3, &fuse); + if (ret < 0) + goto out; + + if (ver) + *ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); - return (s8)fuse; +out: + return ret; } #define WL18XX_CONF_FILE_NAME "ti-connectivity/wl18xx-conf.bin" @@ -1080,14 +1121,20 @@ static int wl18xx_plt_init(struct wl1271 *wl) return wl->ops->boot(wl); } -static void wl18xx_get_mac(struct wl1271 *wl) +static int wl18xx_get_mac(struct wl1271 *wl) { u32 mac1, mac2; + int ret; wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); - mac1 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1); - mac2 = wl1271_read32(wl, WL18XX_REG_FUSE_BD_ADDR_2); + ret = wlcore_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1, &mac1); + if (ret < 0) + goto out; + + ret = wlcore_read32(wl, WL18XX_REG_FUSE_BD_ADDR_2, &mac2); + if (ret < 0) + goto out; /* these are the two parts of the BD_ADDR */ wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + @@ -1095,6 +1142,9 @@ static void wl18xx_get_mac(struct wl1271 *wl) wl->fuse_nic_addr = (mac1 & 0xffffff); wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); + +out: + return ret; } static int wl18xx_handle_static_data(struct wl1271 *wl, diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index ee7a401478a9..0aa0e29b8d98 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -33,16 +33,22 @@ #include "rx.h" #include "hw_ops.h" -static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) +static int wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) { u32 cpu_ctrl; + int ret; /* 10.5.0 run the firmware (I) */ - cpu_ctrl = wlcore_read_reg(wl, REG_ECPU_CONTROL); + ret = wlcore_read_reg(wl, REG_ECPU_CONTROL, &cpu_ctrl); + if (ret < 0) + goto out; /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl); + +out: + return ret; } static int wlcore_boot_parse_fw_ver(struct wl1271 *wl, @@ -368,9 +374,13 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) /* Make sure we have the boot partition */ wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); - wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); + ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); + if (ret < 0) + return ret; - chip_id = wlcore_read_reg(wl, REG_CHIP_ID_B); + ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &chip_id); + if (ret < 0) + return ret; wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); @@ -383,7 +393,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) loop = 0; while (loop++ < INIT_LOOP) { udelay(INIT_LOOP_DELAY); - intr = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR); + ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); + if (ret < 0) + return ret; if (intr == 0xffffffff) { wl1271_error("error reading hardware complete " @@ -405,12 +417,17 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) } /* get hardware config command mail box */ - wl->cmd_box_addr = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR); + ret = wlcore_read_reg(wl, REG_COMMAND_MAILBOX_PTR, &wl->cmd_box_addr); + if (ret < 0) + return ret; wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x", wl->cmd_box_addr); /* get hardware config event mail box */ - wl->mbox_ptr[0] = wlcore_read_reg(wl, REG_EVENT_MAILBOX_PTR); + ret = wlcore_read_reg(wl, REG_EVENT_MAILBOX_PTR, &wl->mbox_ptr[0]); + if (ret < 0) + return ret; + wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x", diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 658dccbfd7ed..f2ac982a5cf5 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -79,7 +79,10 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); - intr = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR); + ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); + if (ret < 0) + goto fail; + while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); @@ -93,7 +96,9 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, else msleep(1); - intr = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR); + ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr); + if (ret < 0) + goto fail; } /* read back the status code of the command */ diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 2cbf7623ddbb..0395b030a4d6 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -77,12 +77,19 @@ static inline int wlcore_raw_write_data(struct wl1271 *wl, int reg, void *buf, return wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed); } -static inline u32 wl1271_raw_read32(struct wl1271 *wl, int addr) +static inline int wlcore_raw_read32(struct wl1271 *wl, int addr, u32 *val) { - wlcore_raw_read(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + int ret; + + ret = wlcore_raw_read(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); + if (ret < 0) + return ret; + + if (val) + *val = le32_to_cpu(wl->buffer_32); - return le32_to_cpu(wl->buffer_32); + return 0; } static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) @@ -138,9 +145,9 @@ static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr, wlcore_raw_read(wl, physical, buf, len, fixed); } -static inline u32 wl1271_read32(struct wl1271 *wl, int addr) +static inline int wlcore_read32(struct wl1271 *wl, int addr, u32 *val) { - return wl1271_raw_read32(wl, wlcore_translate_addr(wl, addr)); + return wlcore_raw_read32(wl, wlcore_translate_addr(wl, addr), val); } static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) @@ -148,10 +155,11 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) wl1271_raw_write32(wl, wlcore_translate_addr(wl, addr), val); } -static inline u32 wlcore_read_reg(struct wl1271 *wl, int reg) +static inline int wlcore_read_reg(struct wl1271 *wl, int reg, u32 *val) { - return wl1271_raw_read32(wl, - wlcore_translate_addr(wl, wl->rtable[reg])); + return wlcore_raw_read32(wl, + wlcore_translate_addr(wl, wl->rtable[reg]), + val); } static inline void wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0461d4eecfd2..c16d266ea6a2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -872,6 +872,32 @@ out: kfree(block); } +static void wlcore_print_recovery(struct wl1271 *wl) +{ + u32 pc = 0; + u32 hint_sts = 0; + int ret; + + wl1271_info("Hardware recovery in progress. FW ver: %s", + wl->chip.fw_ver_str); + + /* change partitions momentarily so we can read the FW pc */ + wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + + ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc); + if (ret < 0) + return; + + ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &hint_sts); + if (ret < 0) + return; + + wl1271_info("pc: 0x%x, hint_sts: 0x%08x", pc, hint_sts); + + wlcore_set_partition(wl, &wl->ptable[PART_WORK]); +} + + static void wl1271_recovery_work(struct work_struct *work) { struct wl1271 *wl = @@ -886,14 +912,7 @@ static void wl1271_recovery_work(struct work_struct *work) wl12xx_read_fwlog_panic(wl); - /* change partitions momentarily so we can read the FW pc */ - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); - wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x " - "hint_sts: 0x%08x", - wl->chip.fw_ver_str, - wlcore_read_reg(wl, REG_PC_ON_RECOVERY), - wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR)); - wlcore_set_partition(wl, &wl->ptable[PART_WORK]); + wlcore_print_recovery(wl); BUG_ON(bug_on_recovery && !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); @@ -4979,18 +4998,22 @@ static int wl12xx_get_hw_info(struct wl1271 *wl) if (ret < 0) goto out; - wl->chip.id = wlcore_read_reg(wl, REG_CHIP_ID_B); + ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &wl->chip.id); + if (ret < 0) + goto out; wl->fuse_oui_addr = 0; wl->fuse_nic_addr = 0; - wl->hw_pg_ver = wl->ops->get_pg_ver(wl); + ret = wl->ops->get_pg_ver(wl, &wl->hw_pg_ver); + if (ret < 0) + goto out; if (wl->ops->get_mac) - wl->ops->get_mac(wl); + ret = wl->ops->get_mac(wl); - wl1271_power_off(wl); out: + wl1271_power_off(wl); return ret; } diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 5d51647e6154..2fb537478ba4 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -62,8 +62,8 @@ struct wlcore_ops { int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif); u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl, struct wl12xx_vif *wlvif); - s8 (*get_pg_ver)(struct wl1271 *wl); - void (*get_mac)(struct wl1271 *wl); + int (*get_pg_ver)(struct wl1271 *wl, s8 *ver); + int (*get_mac)(struct wl1271 *wl); void (*set_tx_desc_csum)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb); -- cgit v1.2.3 From b0f0ad39e3d2716fe9ca6e50ce4cda87eb409ee0 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 20 Jun 2012 00:48:23 +0300 Subject: wlcore: Propagate errors from wl1271_raw_write32 Propagate errors from wl1271_raw_write32 and request for recovery when appropriate. Also rename prefixes of wlcore functions which their prototypes had to be changed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 160 +++++++++++++++++++++++--------- drivers/net/wireless/ti/wl18xx/io.c | 4 +- drivers/net/wireless/ti/wl18xx/main.c | 86 ++++++++++++----- drivers/net/wireless/ti/wlcore/boot.c | 32 +++++-- drivers/net/wireless/ti/wlcore/cmd.c | 6 +- drivers/net/wireless/ti/wlcore/event.c | 4 +- drivers/net/wireless/ti/wlcore/io.c | 49 ++++++---- drivers/net/wireless/ti/wlcore/io.h | 22 ++--- drivers/net/wireless/ti/wlcore/main.c | 20 +++- drivers/net/wireless/ti/wlcore/ps.c | 14 ++- drivers/net/wireless/ti/wlcore/rx.c | 9 +- drivers/net/wireless/ti/wlcore/tx.c | 17 ++-- drivers/net/wireless/ti/wlcore/wlcore.h | 2 +- 13 files changed, 298 insertions(+), 127 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index ab486f71eee4..3a4ab65db0ec 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -688,17 +688,28 @@ out: return ret; } -static void wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) +static int wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) { + int ret; + /* write address >> 1 + 0x30000 to OCP_POR_CTR */ addr = (addr >> 1) + 0x30000; - wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr); + ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr); + if (ret < 0) + goto out; /* write value to OCP_POR_WDATA */ - wl1271_write32(wl, WL12XX_OCP_DATA_WRITE, val); + ret = wlcore_write32(wl, WL12XX_OCP_DATA_WRITE, val); + if (ret < 0) + goto out; /* write 1 to OCP_CMD */ - wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE); + ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_WRITE); + if (ret < 0) + goto out; + +out: + return ret; } static int wl12xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) @@ -709,10 +720,14 @@ static int wl12xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) /* write address >> 1 + 0x30000 to OCP_POR_CTR */ addr = (addr >> 1) + 0x30000; - wl1271_write32(wl, WL12XX_OCP_POR_CTR, addr); + ret = wlcore_write32(wl, WL12XX_OCP_POR_CTR, addr); + if (ret < 0) + return ret; /* write 2 to OCP_CMD */ - wl1271_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ); + ret = wlcore_write32(wl, WL12XX_OCP_CMD, OCP_CMD_READ); + if (ret < 0) + return ret; /* poll for data ready */ do { @@ -751,11 +766,15 @@ static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= (BIT(3) | BIT(5) | BIT(6)); - wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); + ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); + if (ret < 0) + return ret; /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ - wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG, - WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); + ret = wl12xx_top_reg_write(wl, SYS_CLK_CFG_REG, + WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); + if (ret < 0) + return ret; /* Delay execution for 15msec, to let the HW settle */ mdelay(15); @@ -795,11 +814,21 @@ static bool wl128x_is_fref_valid(struct wl1271 *wl) static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) { - wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); - wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); - wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); + int ret; - return 0; + ret = wl12xx_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); + if (ret < 0) + goto out; + + ret = wl12xx_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); + if (ret < 0) + goto out; + + ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, + MCS_PLL_CONFIG_REG_VAL); + +out: + return ret; } static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) @@ -818,7 +847,9 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) if (spare_reg == 0xFFFF) return -EFAULT; spare_reg |= BIT(2); - wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); + ret = wl12xx_top_reg_write(wl, WL_SPARE_REG, spare_reg); + if (ret < 0) + return ret; /* Handle special cases of the TCXO clock */ if (priv->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || @@ -836,9 +867,9 @@ static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) return -EFAULT; pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); pll_config |= MCS_PLL_ENABLE_HP; - wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); + ret = wl12xx_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); - return 0; + return ret; } /* @@ -927,7 +958,9 @@ static int wl127x_boot_clk(struct wl1271 *wl) goto out; val &= FREF_CLK_TYPE_BITS; - wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val); + ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_TYPE, val); + if (ret < 0) + goto out; /* Set clock pull mode (no pull) */ ret = wl12xx_top_reg_read(wl, OCP_REG_CLK_PULL, &val); @@ -935,7 +968,9 @@ static int wl127x_boot_clk(struct wl1271 *wl) goto out; val |= NO_PULL; - wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val); + ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_PULL, val); + if (ret < 0) + goto out; } else { u16 val; /* Set clock polarity */ @@ -945,10 +980,14 @@ static int wl127x_boot_clk(struct wl1271 *wl) val &= FREF_CLK_POLARITY_BITS; val |= CLK_REQ_OUTN_SEL; - wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); + ret = wl12xx_top_reg_write(wl, OCP_REG_CLK_POLARITY, val); + if (ret < 0) + goto out; } - wl1271_write32(wl, WL12XX_PLL_PARAMETERS, clk); + ret = wlcore_write32(wl, WL12XX_PLL_PARAMETERS, clk); + if (ret < 0) + goto out; ret = wlcore_read32(wl, WL12XX_PLL_PARAMETERS, &pause); if (ret < 0) @@ -958,7 +997,7 @@ static int wl127x_boot_clk(struct wl1271 *wl) pause &= ~(WU_COUNTER_PAUSE_VAL); pause |= WU_COUNTER_PAUSE_VAL; - wl1271_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause); + ret = wlcore_write32(wl, WL12XX_WU_COUNTER_PAUSE, pause); out: return ret; @@ -971,7 +1010,9 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) int ret = 0; /* perform soft reset */ - wl1271_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); + ret = wlcore_write32(wl, WL12XX_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); + if (ret < 0) + goto out; /* SOFT_RESET is self clearing */ timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); @@ -995,10 +1036,12 @@ static int wl1271_boot_soft_reset(struct wl1271 *wl) } /* disable Rx/Tx */ - wl1271_write32(wl, WL12XX_ENABLE, 0x0); + ret = wlcore_write32(wl, WL12XX_ENABLE, 0x0); + if (ret < 0) + goto out; /* disable auto calibration on start*/ - wl1271_write32(wl, WL12XX_SPARE_A2, 0xffff); + ret = wlcore_write32(wl, WL12XX_SPARE_A2, 0xffff); out: return ret; @@ -1022,10 +1065,15 @@ static int wl12xx_pre_boot(struct wl1271 *wl) } /* Continue the ELP wake up sequence */ - wl1271_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + ret = wlcore_write32(wl, WL12XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + if (ret < 0) + goto out; + udelay(500); - wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); + if (ret < 0) + goto out; /* Read-modify-write DRPW_SCRATCH_START register (see next state) to be used by DRPw FW. The RTRIM value will be added by the FW @@ -1042,12 +1090,18 @@ static int wl12xx_pre_boot(struct wl1271 *wl) else clk |= (priv->ref_clock << 1) << 4; - wl1271_write32(wl, WL12XX_DRPW_SCRATCH_START, clk); + ret = wlcore_write32(wl, WL12XX_DRPW_SCRATCH_START, clk); + if (ret < 0) + goto out; - wlcore_set_partition(wl, &wl->ptable[PART_WORK]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); + if (ret < 0) + goto out; /* Disable interrupts */ - wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + if (ret < 0) + goto out; ret = wl1271_boot_soft_reset(wl); if (ret < 0) @@ -1067,7 +1121,9 @@ static int wl12xx_pre_upload(struct wl1271 *wl) * ACX_EEPROMLESS_IND_REG */ wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); - wl1271_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND); + ret = wlcore_write32(wl, WL12XX_EEPROMLESS_IND, WL12XX_EEPROMLESS_IND); + if (ret < 0) + goto out; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp); if (ret < 0) @@ -1083,8 +1139,11 @@ static int wl12xx_pre_upload(struct wl1271 *wl) /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ - if (wl->chip.id == CHIP_ID_1283_PG20) - wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); + if (wl->chip.id == CHIP_ID_1283_PG20) { + ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); + if (ret < 0) + goto out; + } /* polarity must be set before the firmware is loaded */ ret = wl12xx_top_reg_read(wl, OCP_REG_POLARITY, &polarity); @@ -1093,21 +1152,31 @@ static int wl12xx_pre_upload(struct wl1271 *wl) /* We use HIGH polarity, so unset the LOW bit */ polarity &= ~POLARITY_LOW; - wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); + ret = wl12xx_top_reg_write(wl, OCP_REG_POLARITY, polarity); out: return ret; } -static void wl12xx_enable_interrupts(struct wl1271 *wl) +static int wl12xx_enable_interrupts(struct wl1271 *wl) { - wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL12XX_ACX_ALL_EVENTS_VECTOR); + int ret; + + ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, + WL12XX_ACX_ALL_EVENTS_VECTOR); + if (ret < 0) + goto out; wlcore_enable_interrupts(wl); - wlcore_write_reg(wl, REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK)); + ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL12XX_INTR_MASK)); + if (ret < 0) + goto out; + + ret = wlcore_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL); - wl1271_write32(wl, WL12XX_HI_CFG, HI_CFG_DEF_VAL); +out: + return ret; } static int wl12xx_boot(struct wl1271 *wl) @@ -1134,7 +1203,7 @@ static int wl12xx_boot(struct wl1271 *wl) if (ret < 0) goto out; - wl12xx_enable_interrupts(wl); + ret = wl12xx_enable_interrupts(wl); out: return ret; @@ -1149,14 +1218,15 @@ static int wl12xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, if (ret < 0) return ret; - wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD); + ret = wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_CMD); return ret; } -static void wl12xx_ack_event(struct wl1271 *wl) +static int wl12xx_ack_event(struct wl1271 *wl) { - wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL12XX_INTR_TRIG_EVENT_ACK); + return wlcore_write_reg(wl, REG_INTERRUPT_TRIG, + WL12XX_INTR_TRIG_EVENT_ACK); } static u32 wl12xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) @@ -1348,7 +1418,9 @@ static int wl12xx_get_fuse_mac(struct wl1271 *wl) u32 mac1, mac2; int ret; - wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_DRPW]); + if (ret < 0) + goto out; ret = wlcore_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1, &mac1); if (ret < 0) @@ -1363,7 +1435,7 @@ static int wl12xx_get_fuse_mac(struct wl1271 *wl) ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = mac1 & 0xffffff; - wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: return ret; diff --git a/drivers/net/wireless/ti/wl18xx/io.c b/drivers/net/wireless/ti/wl18xx/io.c index 92c2c03e4cd8..0c06ccfd1b8c 100644 --- a/drivers/net/wireless/ti/wl18xx/io.c +++ b/drivers/net/wireless/ti/wl18xx/io.c @@ -38,14 +38,14 @@ int wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) goto out; tmp = (tmp & 0xffff0000) | val; - wl1271_write32(wl, addr, tmp); + ret = wlcore_write32(wl, addr, tmp); } else { ret = wlcore_read32(wl, addr - 2, &tmp); if (ret < 0) goto out; tmp = (tmp & 0xffff) | (val << 16); - wl1271_write32(wl, addr - 2, tmp); + ret = wlcore_write32(wl, addr - 2, tmp); } out: diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index f99f003ab182..c25b960faa29 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -641,7 +641,9 @@ static int wl18xx_set_clk(struct wl1271 *wl) u16 clk_freq; int ret; - wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + if (ret < 0) + goto out; /* TODO: PG2: apparently we need to read the clk type */ @@ -699,13 +701,20 @@ out: return ret; } -static void wl18xx_boot_soft_reset(struct wl1271 *wl) +static int wl18xx_boot_soft_reset(struct wl1271 *wl) { + int ret; + /* disable Rx/Tx */ - wl1271_write32(wl, WL18XX_ENABLE, 0x0); + ret = wlcore_write32(wl, WL18XX_ENABLE, 0x0); + if (ret < 0) + goto out; /* disable auto calibration on start*/ - wl1271_write32(wl, WL18XX_SPARE_A2, 0xffff); + ret = wlcore_write32(wl, WL18XX_SPARE_A2, 0xffff); + +out: + return ret; } static int wl18xx_pre_boot(struct wl1271 *wl) @@ -717,15 +726,22 @@ static int wl18xx_pre_boot(struct wl1271 *wl) goto out; /* Continue the ELP wake up sequence */ - wl1271_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + ret = wlcore_write32(wl, WL18XX_WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + if (ret < 0) + goto out; + udelay(500); - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + if (ret < 0) + goto out; /* Disable interrupts */ - wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + if (ret < 0) + goto out; - wl18xx_boot_soft_reset(wl); + ret = wl18xx_boot_soft_reset(wl); out: return ret; @@ -736,10 +752,14 @@ static int wl18xx_pre_upload(struct wl1271 *wl) u32 tmp; int ret; - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + if (ret < 0) + goto out; /* TODO: check if this is all needed */ - wl1271_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND); + ret = wlcore_write32(wl, WL18XX_EEPROMLESS_IND, WL18XX_EEPROMLESS_IND); + if (ret < 0) + goto out; ret = wlcore_read_reg(wl, REG_CHIP_ID_B, &tmp); if (ret < 0) @@ -765,16 +785,21 @@ static int wl18xx_set_mac_and_phy(struct wl1271 *wl) else len = sizeof(struct wl18xx_mac_and_phy_params); - wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); + if (ret < 0) + goto out; + ret = wlcore_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, len, false); +out: return ret; } -static void wl18xx_enable_interrupts(struct wl1271 *wl) +static int wl18xx_enable_interrupts(struct wl1271 *wl) { u32 event_mask, intr_mask; + int ret; if (wl->chip.id == CHIP_ID_185x_PG10) { event_mask = WL18XX_ACX_EVENTS_VECTOR_PG1; @@ -784,11 +809,17 @@ static void wl18xx_enable_interrupts(struct wl1271 *wl) intr_mask = WL18XX_INTR_MASK_PG2; } - wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask); + ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask); + if (ret < 0) + goto out; wlcore_enable_interrupts(wl); - wlcore_write_reg(wl, REG_INTERRUPT_MASK, - WL1271_ACX_INTR_ALL & ~intr_mask); + + ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~intr_mask); + +out: + return ret; } static int wl18xx_boot(struct wl1271 *wl) @@ -815,7 +846,7 @@ static int wl18xx_boot(struct wl1271 *wl) if (ret < 0) goto out; - wl18xx_enable_interrupts(wl); + ret = wl18xx_enable_interrupts(wl); out: return ret; @@ -833,9 +864,10 @@ static int wl18xx_trigger_cmd(struct wl1271 *wl, int cmd_box_addr, WL18XX_CMD_MAX_SIZE, false); } -static void wl18xx_ack_event(struct wl1271 *wl) +static int wl18xx_ack_event(struct wl1271 *wl) { - wlcore_write_reg(wl, REG_INTERRUPT_TRIG, WL18XX_INTR_TRIG_EVENT_ACK); + return wlcore_write_reg(wl, REG_INTERRUPT_TRIG, + WL18XX_INTR_TRIG_EVENT_ACK); } static u32 wl18xx_calc_tx_blocks(struct wl1271 *wl, u32 len, u32 spare_blks) @@ -1038,7 +1070,9 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) u32 fuse; int ret; - wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + if (ret < 0) + goto out; ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_1_3, &fuse); if (ret < 0) @@ -1047,7 +1081,7 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) if (ver) *ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); out: return ret; @@ -1116,7 +1150,11 @@ out: static int wl18xx_plt_init(struct wl1271 *wl) { - wl1271_write32(wl, WL18XX_SCR_PAD8, WL18XX_SCR_PAD8_PLT); + int ret; + + ret = wlcore_write32(wl, WL18XX_SCR_PAD8, WL18XX_SCR_PAD8_PLT); + if (ret < 0) + return ret; return wl->ops->boot(wl); } @@ -1126,7 +1164,9 @@ static int wl18xx_get_mac(struct wl1271 *wl) u32 mac1, mac2; int ret; - wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); + if (ret < 0) + goto out; ret = wlcore_read32(wl, WL18XX_REG_FUSE_BD_ADDR_1, &mac1); if (ret < 0) @@ -1141,7 +1181,7 @@ static int wl18xx_get_mac(struct wl1271 *wl) ((mac1 & 0xff000000) >> 24); wl->fuse_nic_addr = (mac1 & 0xffffff); - wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_DOWN]); out: return ret; diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 0aa0e29b8d98..8965960b841a 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -45,7 +45,7 @@ static int wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) /* 10.5.1 run the firmware (II) */ cpu_ctrl |= flag; - wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl); + ret = wlcore_write_reg(wl, REG_ECPU_CONTROL, cpu_ctrl); out: return ret; @@ -139,7 +139,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, memcpy(&partition, &wl->ptable[PART_DOWN], sizeof(partition)); partition.mem.start = dest; - wlcore_set_partition(wl, &partition); + ret = wlcore_set_partition(wl, &partition); + if (ret < 0) + return ret; /* 10.1 set partition limit and chunk num */ chunk_num = 0; @@ -153,7 +155,9 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, partition_limit = chunk_num * CHUNK_SIZE + wl->ptable[PART_DOWN].mem.size; partition.mem.start = addr; - wlcore_set_partition(wl, &partition); + ret = wlcore_set_partition(wl, &partition); + if (ret < 0) + return ret; } /* 10.3 upload the chunk */ @@ -320,7 +324,9 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "nvs burst write 0x%x: 0x%x", dest_addr, val); - wl1271_write32(wl, dest_addr, val); + ret = wlcore_write32(wl, dest_addr, val); + if (ret < 0) + return ret; nvs_ptr += 4; dest_addr += 4; @@ -346,7 +352,9 @@ int wlcore_boot_upload_nvs(struct wl1271 *wl) nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ - wlcore_set_partition(wl, &wl->ptable[PART_WORK]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); + if (ret < 0) + return ret; /* Copy the NVS tables to a new block to ensure alignment */ nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); @@ -372,7 +380,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) u32 chip_id, intr; /* Make sure we have the boot partition */ - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + if (ret < 0) + return ret; ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); if (ret < 0) @@ -404,8 +414,10 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) } /* check that ACX_INTR_INIT_COMPLETE is enabled */ else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) { - wlcore_write_reg(wl, REG_INTERRUPT_ACK, - WL1271_ACX_INTR_INIT_COMPLETE); + ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK, + WL1271_ACX_INTR_INIT_COMPLETE); + if (ret < 0) + return ret; break; } } @@ -469,9 +481,9 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) } /* set the working partition to its "running" mode offset */ - wlcore_set_partition(wl, &wl->ptable[PART_WORK]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_WORK]); /* firmware startup completed */ - return 0; + return ret; } EXPORT_SYMBOL_GPL(wlcore_boot_run_firmware); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index f2ac982a5cf5..84dd808f65fa 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -116,7 +116,11 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, goto fail; } - wlcore_write_reg(wl, REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); + ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK, + WL1271_ACX_INTR_CMD_COMPLETE); + if (ret < 0) + goto fail; + return 0; fail: diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 123d26d17ba4..48907054d493 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -318,7 +318,7 @@ int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) * TODO: we just need this because one bit is in a different * place. Is there any better way? */ - wl->ops->ack_event(wl); + ret = wl->ops->ack_event(wl); - return 0; + return ret; } diff --git a/drivers/net/wireless/ti/wlcore/io.c b/drivers/net/wireless/ti/wlcore/io.c index 62d657389996..9976219c4e49 100644 --- a/drivers/net/wireless/ti/wlcore/io.c +++ b/drivers/net/wireless/ti/wlcore/io.c @@ -128,9 +128,11 @@ EXPORT_SYMBOL_GPL(wlcore_translate_addr); * | | * */ -void wlcore_set_partition(struct wl1271 *wl, - const struct wlcore_partition_set *p) +int wlcore_set_partition(struct wl1271 *wl, + const struct wlcore_partition_set *p) { + int ret; + /* copy partition info */ memcpy(&wl->curr_part, p, sizeof(*p)); @@ -143,28 +145,41 @@ void wlcore_set_partition(struct wl1271 *wl, wl1271_debug(DEBUG_IO, "mem3_start %08X mem3_size %08X", p->mem3.start, p->mem3.size); - wl1271_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); - wl1271_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); - wl1271_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); - wl1271_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); - wl1271_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); - wl1271_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); + ret = wlcore_raw_write32(wl, HW_PART0_START_ADDR, p->mem.start); + if (ret < 0) + goto out; + + ret = wlcore_raw_write32(wl, HW_PART0_SIZE_ADDR, p->mem.size); + if (ret < 0) + goto out; + + ret = wlcore_raw_write32(wl, HW_PART1_START_ADDR, p->reg.start); + if (ret < 0) + goto out; + + ret = wlcore_raw_write32(wl, HW_PART1_SIZE_ADDR, p->reg.size); + if (ret < 0) + goto out; + + ret = wlcore_raw_write32(wl, HW_PART2_START_ADDR, p->mem2.start); + if (ret < 0) + goto out; + + ret = wlcore_raw_write32(wl, HW_PART2_SIZE_ADDR, p->mem2.size); + if (ret < 0) + goto out; + /* * We don't need the size of the last partition, as it is * automatically calculated based on the total memory size and * the sizes of the previous partitions. */ - wl1271_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); -} -EXPORT_SYMBOL_GPL(wlcore_set_partition); + ret = wlcore_raw_write32(wl, HW_PART3_START_ADDR, p->mem3.start); -void wlcore_select_partition(struct wl1271 *wl, u8 part) -{ - wl1271_debug(DEBUG_IO, "setting partition %d", part); - - wlcore_set_partition(wl, &wl->ptable[part]); +out: + return ret; } -EXPORT_SYMBOL_GPL(wlcore_select_partition); +EXPORT_SYMBOL_GPL(wlcore_set_partition); void wl1271_io_reset(struct wl1271 *wl) { diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 0395b030a4d6..5e4a3d174004 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -92,11 +92,11 @@ static inline int wlcore_raw_read32(struct wl1271 *wl, int addr, u32 *val) return 0; } -static inline void wl1271_raw_write32(struct wl1271 *wl, int addr, u32 val) +static inline int wlcore_raw_write32(struct wl1271 *wl, int addr, u32 val) { wl->buffer_32 = cpu_to_le32(val); - wlcore_raw_write(wl, addr, &wl->buffer_32, - sizeof(wl->buffer_32), false); + return wlcore_raw_write(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); } static inline int wlcore_read(struct wl1271 *wl, int addr, void *buf, @@ -150,9 +150,9 @@ static inline int wlcore_read32(struct wl1271 *wl, int addr, u32 *val) return wlcore_raw_read32(wl, wlcore_translate_addr(wl, addr), val); } -static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) +static inline int wlcore_write32(struct wl1271 *wl, int addr, u32 val) { - wl1271_raw_write32(wl, wlcore_translate_addr(wl, addr), val); + return wlcore_raw_write32(wl, wlcore_translate_addr(wl, addr), val); } static inline int wlcore_read_reg(struct wl1271 *wl, int reg, u32 *val) @@ -162,9 +162,11 @@ static inline int wlcore_read_reg(struct wl1271 *wl, int reg, u32 *val) val); } -static inline void wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) +static inline int wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) { - wl1271_raw_write32(wl, wlcore_translate_addr(wl, wl->rtable[reg]), val); + return wlcore_raw_write32(wl, + wlcore_translate_addr(wl, wl->rtable[reg]), + val); } static inline void wl1271_power_off(struct wl1271 *wl) @@ -188,8 +190,8 @@ static inline int wl1271_power_on(struct wl1271 *wl) return ret; } -void wlcore_set_partition(struct wl1271 *wl, - const struct wlcore_partition_set *p); +int wlcore_set_partition(struct wl1271 *wl, + const struct wlcore_partition_set *p); bool wl1271_set_block_size(struct wl1271 *wl); @@ -197,6 +199,4 @@ bool wl1271_set_block_size(struct wl1271 *wl); int wl1271_tx_dummy_packet(struct wl1271 *wl); -void wlcore_select_partition(struct wl1271 *wl, u8 part); - #endif diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index c16d266ea6a2..546fcb074c6e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -882,7 +882,9 @@ static void wlcore_print_recovery(struct wl1271 *wl) wl->chip.fw_ver_str); /* change partitions momentarily so we can read the FW pc */ - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + if (ret < 0) + return; ret = wlcore_read_reg(wl, REG_PC_ON_RECOVERY, &pc); if (ret < 0) @@ -967,9 +969,9 @@ out_unlock: mutex_unlock(&wl->mutex); } -static void wl1271_fw_wakeup(struct wl1271 *wl) +static int wlcore_fw_wakeup(struct wl1271 *wl) { - wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); + return wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); } static int wl1271_setup(struct wl1271 *wl) @@ -1005,13 +1007,21 @@ static int wl12xx_set_power_on(struct wl1271 *wl) wl1271_io_reset(wl); wl1271_io_init(wl); - wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]); + if (ret < 0) + goto fail; /* ELP module wake up */ - wl1271_fw_wakeup(wl); + ret = wlcore_fw_wakeup(wl); + if (ret < 0) + goto fail; out: return ret; + +fail: + wl1271_power_off(wl); + return ret; } static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt) diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 95d8797cfa28..46d36fd30eba 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -35,6 +35,7 @@ void wl1271_elp_work(struct work_struct *work) struct delayed_work *dwork; struct wl1271 *wl; struct wl12xx_vif *wlvif; + int ret; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, elp_work); @@ -63,7 +64,12 @@ void wl1271_elp_work(struct work_struct *work) } wl1271_debug(DEBUG_PSM, "chip to elp"); - wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_SLEEP); + ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_SLEEP); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto out; + } + set_bit(WL1271_FLAG_IN_ELP, &wl->flags); out: @@ -135,7 +141,11 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) wl->elp_compl = &compl; spin_unlock_irqrestore(&wl->wl_lock, flags); - wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); + ret = wlcore_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG, ELPCTRL_WAKE_UP); + if (ret < 0) { + wl12xx_queue_recovery_work(wl); + goto err; + } if (!pending) { ret = wait_for_completion_timeout( diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index be24b3030f92..f42b969c1de9 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -279,9 +279,12 @@ int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) * Write the driver's packet counter to the FW. This is only required * for older hardware revisions */ - if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) - wl1271_write32(wl, WL12XX_REG_RX_DRIVER_COUNTER, - wl->rx_counter); + if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { + ret = wlcore_write32(wl, WL12XX_REG_RX_DRIVER_COUNTER, + wl->rx_counter); + if (ret < 0) + goto out; + } wl12xx_rearm_rx_streaming(wl, active_hlids); diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 90bddf56f8ed..b5211be229d9 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -746,9 +746,12 @@ out_ack: * Interrupt the firmware with the new packets. This is only * required for older hardware revisions */ - if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) - wl1271_write32(wl, WL12XX_HOST_WR_ACCESS, - wl->tx_packets_count); + if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { + ret = wlcore_write32(wl, WL12XX_HOST_WR_ACCESS, + wl->tx_packets_count); + if (ret < 0) + goto out; + } wl1271_handle_tx_low_watermark(wl); } @@ -911,9 +914,11 @@ int wlcore_tx_complete(struct wl1271 *wl) fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter); /* write host counter to chipset (to ack) */ - wl1271_write32(wl, le32_to_cpu(memmap->tx_result) + - offsetof(struct wl1271_tx_hw_res_if, - tx_result_host_counter), fw_counter); + ret = wlcore_write32(wl, le32_to_cpu(memmap->tx_result) + + offsetof(struct wl1271_tx_hw_res_if, + tx_result_host_counter), fw_counter); + if (ret < 0) + goto out; count = fw_counter - wl->tx_results_count; wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 2fb537478ba4..e796974df59b 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -43,7 +43,7 @@ struct wlcore_ops { int (*plt_init)(struct wl1271 *wl); int (*trigger_cmd)(struct wl1271 *wl, int cmd_box_addr, void *buf, size_t len); - void (*ack_event)(struct wl1271 *wl); + int (*ack_event)(struct wl1271 *wl); u32 (*calc_tx_blocks)(struct wl1271 *wl, u32 len, u32 spare_blks); void (*set_tx_desc_blocks)(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, -- cgit v1.2.3 From 2b80040782af56e1b13ad451f593dd4e1875b2b8 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Jun 2012 18:15:50 +0300 Subject: wlcore: Propagate errors from wl1271_read_hwaddr Propagate errors from wl1271_read_hwaddr. This function is only used when reading the FW log (following a recovery), so don't read the FW log in case of a bus error. Also rename prefixes of wlcore functions which their prototypes had to be changed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/io.h | 4 ++-- drivers/net/wireless/ti/wlcore/main.c | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 5e4a3d174004..4a6688b03aea 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -131,7 +131,7 @@ static inline int wlcore_read_data(struct wl1271 *wl, int reg, void *buf, return wlcore_read(wl, wl->rtable[reg], buf, len, fixed); } -static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr, +static inline int wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, void *buf, size_t len, bool fixed) { int physical; @@ -142,7 +142,7 @@ static inline void wl1271_read_hwaddr(struct wl1271 *wl, int hwaddr, physical = wlcore_translate_addr(wl, addr); - wlcore_raw_read(wl, physical, buf, len, fixed); + return wlcore_raw_read(wl, physical, buf, len, fixed); } static inline int wlcore_read32(struct wl1271 *wl, int addr, u32 *val) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 546fcb074c6e..b0795aac4bac 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -851,8 +851,10 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) /* Traverse the memory blocks linked list */ do { memset(block, 0, WL12XX_HW_BLOCK_SIZE); - wl1271_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, - false); + ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, + false); + if (ret < 0) + goto out; /* * Memory blocks are linked to one another. The first 4 bytes -- cgit v1.2.3 From f1a26e638e646d971f77c5a5186ee254b3f4e818 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 20 Jun 2012 00:03:46 +0300 Subject: wlcore: Force checking of io functions' return values All io functions' return values should be propagated and handled. Add a __must_check annotation to verify that the return values are checked and to avoid future mistakes. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl12xx/main.c | 6 ++- drivers/net/wireless/ti/wl18xx/io.h | 4 +- drivers/net/wireless/ti/wlcore/io.h | 61 +++++++++++++++++++------------ drivers/net/wireless/ti/wlcore/sdio.c | 8 ++-- drivers/net/wireless/ti/wlcore/spi.c | 8 ++-- drivers/net/wireless/ti/wlcore/wlcore_i.h | 8 ++-- 6 files changed, 55 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 3a4ab65db0ec..47ba2e0017f4 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -688,7 +688,8 @@ out: return ret; } -static int wl12xx_top_reg_write(struct wl1271 *wl, int addr, u16 val) +static int __must_check wl12xx_top_reg_write(struct wl1271 *wl, int addr, + u16 val) { int ret; @@ -712,7 +713,8 @@ out: return ret; } -static int wl12xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out) +static int __must_check wl12xx_top_reg_read(struct wl1271 *wl, int addr, + u16 *out) { u32 val; int timeout = OCP_CMD_LOOP; diff --git a/drivers/net/wireless/ti/wl18xx/io.h b/drivers/net/wireless/ti/wl18xx/io.h index 0e1b8d28edb2..c32ae30277df 100644 --- a/drivers/net/wireless/ti/wl18xx/io.h +++ b/drivers/net/wireless/ti/wl18xx/io.h @@ -22,7 +22,7 @@ #ifndef __WL18XX_IO_H__ #define __WL18XX_IO_H__ -int wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val); -int wl18xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out); +int __must_check wl18xx_top_reg_write(struct wl1271 *wl, int addr, u16 val); +int __must_check wl18xx_top_reg_read(struct wl1271 *wl, int addr, u16 *out); #endif /* __WL18XX_IO_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 4a6688b03aea..1cd545b0ed1e 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -53,31 +53,36 @@ void wl1271_io_init(struct wl1271 *wl); int wlcore_translate_addr(struct wl1271 *wl, int addr); /* Raw target IO, address is not translated */ -static inline int wlcore_raw_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr, + void *buf, size_t len, + bool fixed) { return wl->if_ops->write(wl->dev, addr, buf, len, fixed); } -static inline int wlcore_raw_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr, + void *buf, size_t len, + bool fixed) { return wl->if_ops->read(wl->dev, addr, buf, len, fixed); } -static inline int wlcore_raw_read_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_raw_read_data(struct wl1271 *wl, int reg, + void *buf, size_t len, + bool fixed) { return wlcore_raw_read(wl, wl->rtable[reg], buf, len, fixed); } -static inline int wlcore_raw_write_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_raw_write_data(struct wl1271 *wl, int reg, + void *buf, size_t len, + bool fixed) { return wlcore_raw_write(wl, wl->rtable[reg], buf, len, fixed); } -static inline int wlcore_raw_read32(struct wl1271 *wl, int addr, u32 *val) +static inline int __must_check wlcore_raw_read32(struct wl1271 *wl, int addr, + u32 *val) { int ret; @@ -92,15 +97,16 @@ static inline int wlcore_raw_read32(struct wl1271 *wl, int addr, u32 *val) return 0; } -static inline int wlcore_raw_write32(struct wl1271 *wl, int addr, u32 val) +static inline int __must_check wlcore_raw_write32(struct wl1271 *wl, int addr, + u32 val) { wl->buffer_32 = cpu_to_le32(val); return wlcore_raw_write(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32), false); } -static inline int wlcore_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_read(struct wl1271 *wl, int addr, + void *buf, size_t len, bool fixed) { int physical; @@ -109,8 +115,8 @@ static inline int wlcore_read(struct wl1271 *wl, int addr, void *buf, return wlcore_raw_read(wl, physical, buf, len, fixed); } -static inline int wlcore_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_write(struct wl1271 *wl, int addr, + void *buf, size_t len, bool fixed) { int physical; @@ -119,20 +125,23 @@ static inline int wlcore_write(struct wl1271 *wl, int addr, void *buf, return wlcore_raw_write(wl, physical, buf, len, fixed); } -static inline int wlcore_write_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_write_data(struct wl1271 *wl, int reg, + void *buf, size_t len, + bool fixed) { return wlcore_write(wl, wl->rtable[reg], buf, len, fixed); } -static inline int wlcore_read_data(struct wl1271 *wl, int reg, void *buf, - size_t len, bool fixed) +static inline int __must_check wlcore_read_data(struct wl1271 *wl, int reg, + void *buf, size_t len, + bool fixed) { return wlcore_read(wl, wl->rtable[reg], buf, len, fixed); } -static inline int wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, - void *buf, size_t len, bool fixed) +static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, + void *buf, size_t len, + bool fixed) { int physical; int addr; @@ -145,24 +154,28 @@ static inline int wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, return wlcore_raw_read(wl, physical, buf, len, fixed); } -static inline int wlcore_read32(struct wl1271 *wl, int addr, u32 *val) +static inline int __must_check wlcore_read32(struct wl1271 *wl, int addr, + u32 *val) { return wlcore_raw_read32(wl, wlcore_translate_addr(wl, addr), val); } -static inline int wlcore_write32(struct wl1271 *wl, int addr, u32 val) +static inline int __must_check wlcore_write32(struct wl1271 *wl, int addr, + u32 val) { return wlcore_raw_write32(wl, wlcore_translate_addr(wl, addr), val); } -static inline int wlcore_read_reg(struct wl1271 *wl, int reg, u32 *val) +static inline int __must_check wlcore_read_reg(struct wl1271 *wl, int reg, + u32 *val) { return wlcore_raw_read32(wl, wlcore_translate_addr(wl, wl->rtable[reg]), val); } -static inline int wlcore_write_reg(struct wl1271 *wl, int reg, u32 val) +static inline int __must_check wlcore_write_reg(struct wl1271 *wl, int reg, + u32 val) { return wlcore_raw_write32(wl, wlcore_translate_addr(wl, wl->rtable[reg]), diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 9069dc93b1bc..204e69fa9327 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -71,8 +71,8 @@ static void wl1271_sdio_set_block_size(struct device *child, sdio_release_host(func); } -static int wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int __must_check wl12xx_sdio_raw_read(struct device *child, int addr, + void *buf, size_t len, bool fixed) { int ret; struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); @@ -109,8 +109,8 @@ static int wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, return ret; } -static int wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int __must_check wl12xx_sdio_raw_write(struct device *child, int addr, + void *buf, size_t len, bool fixed) { int ret; struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index d6768e9d7dab..6420abae40ee 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -193,8 +193,8 @@ static int wl12xx_spi_read_busy(struct device *child) return -ETIMEDOUT; } -static int wl12xx_spi_raw_read(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int __must_check wl12xx_spi_raw_read(struct device *child, int addr, + void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct wl1271 *wl = dev_get_drvdata(child); @@ -260,8 +260,8 @@ static int wl12xx_spi_raw_read(struct device *child, int addr, void *buf, return 0; } -static int wl12xx_spi_raw_write(struct device *child, int addr, void *buf, - size_t len, bool fixed) +static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, + void *buf, size_t len, bool fixed) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); struct spi_transfer t[2 * WSPI_MAX_NUM_OF_CHUNKS]; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 5ab31ff4080e..e5a34dd34baf 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -209,10 +209,10 @@ struct wl1271_scan { }; struct wl1271_if_operations { - int (*read)(struct device *child, int addr, void *buf, size_t len, - bool fixed); - int (*write)(struct device *child, int addr, void *buf, size_t len, - bool fixed); + int __must_check (*read)(struct device *child, int addr, void *buf, + size_t len, bool fixed); + int __must_check (*write)(struct device *child, int addr, void *buf, + size_t len, bool fixed); void (*reset)(struct device *child); void (*init)(struct device *child); int (*power)(struct device *child, bool enable); -- cgit v1.2.3 From f6442aa0b503f944907b869659dd27756c809488 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 21 Jun 2012 18:45:46 +0800 Subject: regulator: tps65910: Remove unused min_uV and max_uV from struct tps_info The min_uV and max_uV fields of struct tps_info are not used in the code. For the case voltage_table is provided, the min_uV and max_uV are the same values as volt_table[0] and volt_table[n_voltages -1]. For the case voltage_table is not available, having the min_uV and max_uV seems misleading. Current code uses equations to get the voltage value in this case, but these equations do not use the min_uV and max_uV fields of struct tps_info. Thus this patch removes the min_uV and max_uV fields from struct tps_info. Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 50 ---------------------------------- 1 file changed, 50 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 373c529b379d..edeaa2c770b9 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -85,8 +85,6 @@ static const u16 VMMC_VSEL_table[] = { struct tps_info { const char *name; - unsigned min_uV; - unsigned max_uV; u8 n_voltages; const u16 *voltage_table; int enable_time_us; @@ -99,92 +97,68 @@ static struct tps_info tps65910_regs[] = { }, { .name = "vio", - .min_uV = 1500000, - .max_uV = 3300000, .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", - .min_uV = 600000, - .max_uV = 4500000, .enable_time_us = 350, }, { .name = "vdd2", - .min_uV = 600000, - .max_uV = 4500000, .enable_time_us = 350, }, { .name = "vdd3", - .min_uV = 5000000, - .max_uV = 5000000, .n_voltages = ARRAY_SIZE(VDD3_VSEL_table), .voltage_table = VDD3_VSEL_table, .enable_time_us = 200, }, { .name = "vdig1", - .min_uV = 1200000, - .max_uV = 2700000, .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table), .voltage_table = VDIG1_VSEL_table, .enable_time_us = 100, }, { .name = "vdig2", - .min_uV = 1000000, - .max_uV = 1800000, .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table), .voltage_table = VDIG2_VSEL_table, .enable_time_us = 100, }, { .name = "vpll", - .min_uV = 1000000, - .max_uV = 2500000, .n_voltages = ARRAY_SIZE(VPLL_VSEL_table), .voltage_table = VPLL_VSEL_table, .enable_time_us = 100, }, { .name = "vdac", - .min_uV = 1800000, - .max_uV = 2850000, .n_voltages = ARRAY_SIZE(VDAC_VSEL_table), .voltage_table = VDAC_VSEL_table, .enable_time_us = 100, }, { .name = "vaux1", - .min_uV = 1800000, - .max_uV = 2850000, .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table), .voltage_table = VAUX1_VSEL_table, .enable_time_us = 100, }, { .name = "vaux2", - .min_uV = 1800000, - .max_uV = 3300000, .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table), .voltage_table = VAUX2_VSEL_table, .enable_time_us = 100, }, { .name = "vaux33", - .min_uV = 1800000, - .max_uV = 3300000, .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table), .voltage_table = VAUX33_VSEL_table, .enable_time_us = 100, }, { .name = "vmmc", - .min_uV = 1800000, - .max_uV = 3300000, .n_voltages = ARRAY_SIZE(VMMC_VSEL_table), .voltage_table = VMMC_VSEL_table, .enable_time_us = 100, @@ -198,86 +172,62 @@ static struct tps_info tps65911_regs[] = { }, { .name = "vio", - .min_uV = 1500000, - .max_uV = 3300000, .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", - .min_uV = 600000, - .max_uV = 4500000, .n_voltages = 73, .enable_time_us = 350, }, { .name = "vdd2", - .min_uV = 600000, - .max_uV = 4500000, .n_voltages = 73, .enable_time_us = 350, }, { .name = "vddctrl", - .min_uV = 600000, - .max_uV = 1400000, .n_voltages = 65, .enable_time_us = 900, }, { .name = "ldo1", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo2", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo3", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo4", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 47, .enable_time_us = 230, }, { .name = "ldo5", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo6", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo7", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo8", - .min_uV = 1000000, - .max_uV = 3300000, .n_voltages = 24, .enable_time_us = 230, }, -- cgit v1.2.3 From d9fe28f9621be70514aeacd2b4b3640e986d4cb1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 21 Jun 2012 18:48:00 +0800 Subject: regulator: tps65910: Convert to regulator_list_voltage_table Convert tps65910_ops and tps65910_ops_vdd3 to regulator_list_voltage_table. Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 94 +++++++++++++++------------------- 1 file changed, 40 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index edeaa2c770b9..66da87cc0879 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -31,62 +31,62 @@ TPS65910_SLEEP_CONTROL_EXT_INPUT_EN3 | \ TPS65911_SLEEP_CONTROL_EXT_INPUT_SLEEP) -/* supported VIO voltages in millivolts */ -static const u16 VIO_VSEL_table[] = { - 1500, 1800, 2500, 3300, +/* supported VIO voltages in microvolts */ +static const unsigned int VIO_VSEL_table[] = { + 1500000, 1800000, 2500000, 3300000, }; /* VSEL tables for TPS65910 specific LDOs and dcdc's */ -/* supported VDD3 voltages in millivolts */ -static const u16 VDD3_VSEL_table[] = { - 5000, +/* supported VDD3 voltages in microvolts */ +static const unsigned int VDD3_VSEL_table[] = { + 5000000, }; -/* supported VDIG1 voltages in millivolts */ -static const u16 VDIG1_VSEL_table[] = { - 1200, 1500, 1800, 2700, +/* supported VDIG1 voltages in microvolts */ +static const unsigned int VDIG1_VSEL_table[] = { + 1200000, 1500000, 1800000, 2700000, }; -/* supported VDIG2 voltages in millivolts */ -static const u16 VDIG2_VSEL_table[] = { - 1000, 1100, 1200, 1800, +/* supported VDIG2 voltages in microvolts */ +static const unsigned int VDIG2_VSEL_table[] = { + 1000000, 1100000, 1200000, 1800000, }; -/* supported VPLL voltages in millivolts */ -static const u16 VPLL_VSEL_table[] = { - 1000, 1100, 1800, 2500, +/* supported VPLL voltages in microvolts */ +static const unsigned int VPLL_VSEL_table[] = { + 1000000, 1100000, 1800000, 2500000, }; -/* supported VDAC voltages in millivolts */ -static const u16 VDAC_VSEL_table[] = { - 1800, 2600, 2800, 2850, +/* supported VDAC voltages in microvolts */ +static const unsigned int VDAC_VSEL_table[] = { + 1800000, 2600000, 2800000, 2850000, }; -/* supported VAUX1 voltages in millivolts */ -static const u16 VAUX1_VSEL_table[] = { - 1800, 2500, 2800, 2850, +/* supported VAUX1 voltages in microvolts */ +static const unsigned int VAUX1_VSEL_table[] = { + 1800000, 2500000, 2800000, 2850000, }; -/* supported VAUX2 voltages in millivolts */ -static const u16 VAUX2_VSEL_table[] = { - 1800, 2800, 2900, 3300, +/* supported VAUX2 voltages in microvolts */ +static const unsigned int VAUX2_VSEL_table[] = { + 1800000, 2800000, 2900000, 3300000, }; -/* supported VAUX33 voltages in millivolts */ -static const u16 VAUX33_VSEL_table[] = { - 1800, 2000, 2800, 3300, +/* supported VAUX33 voltages in microvolts */ +static const unsigned int VAUX33_VSEL_table[] = { + 1800000, 2000000, 2800000, 3300000, }; -/* supported VMMC voltages in millivolts */ -static const u16 VMMC_VSEL_table[] = { - 1800, 2800, 3000, 3300, +/* supported VMMC voltages in microvolts */ +static const unsigned int VMMC_VSEL_table[] = { + 1800000, 2800000, 3000000, 3300000, }; struct tps_info { const char *name; u8 n_voltages; - const u16 *voltage_table; + const unsigned int *voltage_table; int enable_time_us; }; @@ -559,7 +559,7 @@ static int tps65910_get_voltage_sel(struct regulator_dev *dev) static int tps65910_get_voltage_vdd3(struct regulator_dev *dev) { - return 5 * 1000 * 1000; + return dev->desc->volt_table[0]; } static int tps65911_get_voltage_sel(struct regulator_dev *dev) @@ -718,23 +718,6 @@ static int tps65910_list_voltage_dcdc(struct regulator_dev *dev, return volt * 100 * mult; } -static int tps65910_list_voltage(struct regulator_dev *dev, - unsigned selector) -{ - struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev), voltage; - - if (id < TPS65910_REG_VIO || id > TPS65910_REG_VMMC) - return -EINVAL; - - if (selector >= pmic->info[id]->n_voltages) - return -EINVAL; - else - voltage = pmic->info[id]->voltage_table[selector] * 1000; - - return voltage; -} - static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); @@ -766,7 +749,7 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector) step_mv = 100; break; case TPS65910_REG_VIO: - return pmic->info[id]->voltage_table[selector] * 1000; + return pmic->info[id]->voltage_table[selector]; default: return -EINVAL; } @@ -796,7 +779,7 @@ static struct regulator_ops tps65910_ops_vdd3 = { .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage = tps65910_get_voltage_vdd3, - .list_voltage = tps65910_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static struct regulator_ops tps65910_ops = { @@ -808,7 +791,7 @@ static struct regulator_ops tps65910_ops = { .get_mode = tps65910_get_mode, .get_voltage_sel = tps65910_get_voltage_sel, .set_voltage_sel = tps65910_set_voltage_sel, - .list_voltage = tps65910_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static struct regulator_ops tps65911_ops = { @@ -1165,15 +1148,18 @@ static __devinit int tps65910_probe(struct platform_device *pdev) } else if (i == TPS65910_REG_VDD3) { if (tps65910_chip_id(tps65910) == TPS65910) { pmic->desc[i].ops = &tps65910_ops_vdd3; + pmic->desc[i].volt_table = info->voltage_table; } else { pmic->desc[i].ops = &tps65910_ops_dcdc; pmic->desc[i].ramp_delay = 5000; } } else { - if (tps65910_chip_id(tps65910) == TPS65910) + if (tps65910_chip_id(tps65910) == TPS65910) { pmic->desc[i].ops = &tps65910_ops; - else + pmic->desc[i].volt_table = info->voltage_table; + } else { pmic->desc[i].ops = &tps65911_ops; + } } err = tps65910_set_ext_sleep_config(pmic, i, -- cgit v1.2.3 From f8bbfd7d28303967ca4e8597de9bdc9bf8b197e7 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Feb 2012 17:07:06 +0100 Subject: oprofile, perf: Use per-cpu framework This changes oprofile_perf.c to use the per-cpu framework. Using the per-cpu framework should avoid error like the following: arch/arm/oprofile/../../../drivers/oprofile/oprofile_perf.c:28:28: error: variably modified 'perf_events' at file scope Reported-by: William Cohen Cc: Will Deacon Signed-off-by: Robert Richter --- drivers/oprofile/oprofile_perf.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/oprofile/oprofile_perf.c b/drivers/oprofile/oprofile_perf.c index efc4b7f308cf..f3cfa0b9adfa 100644 --- a/drivers/oprofile/oprofile_perf.c +++ b/drivers/oprofile/oprofile_perf.c @@ -1,5 +1,6 @@ /* * Copyright 2010 ARM Ltd. + * Copyright 2012 Advanced Micro Devices, Inc., Robert Richter * * Perf-events backend for OProfile. */ @@ -25,7 +26,7 @@ static int oprofile_perf_enabled; static DEFINE_MUTEX(oprofile_perf_mutex); static struct op_counter_config *counter_config; -static struct perf_event **perf_events[NR_CPUS]; +static DEFINE_PER_CPU(struct perf_event **, perf_events); static int num_counters; /* @@ -38,7 +39,7 @@ static void op_overflow_handler(struct perf_event *event, u32 cpu = smp_processor_id(); for (id = 0; id < num_counters; ++id) - if (perf_events[cpu][id] == event) + if (per_cpu(perf_events, cpu)[id] == event) break; if (id != num_counters) @@ -74,7 +75,7 @@ static int op_create_counter(int cpu, int event) { struct perf_event *pevent; - if (!counter_config[event].enabled || perf_events[cpu][event]) + if (!counter_config[event].enabled || per_cpu(perf_events, cpu)[event]) return 0; pevent = perf_event_create_kernel_counter(&counter_config[event].attr, @@ -91,18 +92,18 @@ static int op_create_counter(int cpu, int event) return -EBUSY; } - perf_events[cpu][event] = pevent; + per_cpu(perf_events, cpu)[event] = pevent; return 0; } static void op_destroy_counter(int cpu, int event) { - struct perf_event *pevent = perf_events[cpu][event]; + struct perf_event *pevent = per_cpu(perf_events, cpu)[event]; if (pevent) { perf_event_release_kernel(pevent); - perf_events[cpu][event] = NULL; + per_cpu(perf_events, cpu)[event] = NULL; } } @@ -257,12 +258,12 @@ void oprofile_perf_exit(void) for_each_possible_cpu(cpu) { for (id = 0; id < num_counters; ++id) { - event = perf_events[cpu][id]; + event = per_cpu(perf_events, cpu)[id]; if (event) perf_event_release_kernel(event); } - kfree(perf_events[cpu]); + kfree(per_cpu(perf_events, cpu)); } kfree(counter_config); @@ -277,8 +278,6 @@ int __init oprofile_perf_init(struct oprofile_operations *ops) if (ret) return ret; - memset(&perf_events, 0, sizeof(perf_events)); - num_counters = perf_num_counters(); if (num_counters <= 0) { pr_info("oprofile: no performance counters\n"); @@ -298,9 +297,9 @@ int __init oprofile_perf_init(struct oprofile_operations *ops) } for_each_possible_cpu(cpu) { - perf_events[cpu] = kcalloc(num_counters, + per_cpu(perf_events, cpu) = kcalloc(num_counters, sizeof(struct perf_event *), GFP_KERNEL); - if (!perf_events[cpu]) { + if (!per_cpu(perf_events, cpu)) { pr_info("oprofile: failed to allocate %d perf events " "for cpu %d\n", num_counters, cpu); ret = -ENOMEM; -- cgit v1.2.3 From 925839243dc9aa4ef25305f5afd10ed18258a4ac Mon Sep 17 00:00:00 2001 From: Stone Piao Date: Wed, 20 Jun 2012 20:21:10 -0700 Subject: mwifiex: fix 11n rx packet drop issue Currently we check the sequence number of last packet received against start_win. If a sequence hole is detected, start_win is updated to next sequence number. Since the rx sequence number is initialized to 0, a corner case exists when BA setup happens immediately after association. As 0 is a valid sequence number, start_win gets increased to 1 incorrectly. This causes the first packet with sequence number 0 being dropped. Initialize rx sequence number as 0xffff and skip adjusting start_win if the sequence number remains 0xffff. The sequence number will be updated once the first packet is received. Cc: "3.0.y, 3.1.y, 3.2.y, 3.3.y, 3.4.y" Signed-off-by: Stone Piao Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n_rxreorder.c | 5 +++-- drivers/net/wireless/mwifiex/11n_rxreorder.h | 7 +++++++ drivers/net/wireless/mwifiex/wmm.c | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index 9c44088054dd..900ee129e825 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -256,7 +256,8 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, else last_seq = priv->rx_seq[tid]; - if (last_seq >= new_node->start_win) + if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && + last_seq >= new_node->start_win) new_node->start_win = last_seq + 1; new_node->win_size = win_size; @@ -596,5 +597,5 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); - memset(priv->rx_seq, 0, sizeof(priv->rx_seq)); + mwifiex_reset_11n_rx_seq_num(priv); } diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h index f1bffebabc60..6c9815a0f5d8 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.h +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h @@ -37,6 +37,13 @@ #define ADDBA_RSP_STATUS_ACCEPT 0 +#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff + +static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) +{ + memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); +} + int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, u16 seqNum, u16 tid, u8 *ta, diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index f3fc65515857..8c2b5c0aa754 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -404,6 +404,8 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) priv->add_ba_param.tx_win_size = MWIFIEX_AMPDU_DEF_TXWINSIZE; priv->add_ba_param.rx_win_size = MWIFIEX_AMPDU_DEF_RXWINSIZE; + mwifiex_reset_11n_rx_seq_num(priv); + atomic_set(&priv->wmm.tx_pkts_queued, 0); atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); } -- cgit v1.2.3 From f03ba7e9a24e5e9efaad56bd1713b994ea556b16 Mon Sep 17 00:00:00 2001 From: Stone Piao Date: Wed, 20 Jun 2012 20:21:11 -0700 Subject: mwifiex: fix WPS eapol handshake failure After association, STA will go through eapol handshake with WPS enabled AP. It's observed that WPS handshake fails with some 11n AP. The reason for the failure is that the eapol packet is sent via 11n frame aggregation. The eapol packet should be sent directly without 11n aggregation. This patch fixes the problem by adding WPS session control while dequeuing Tx packets for transmission. Cc: "3.4.y" Signed-off-by: Stone Piao Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/wmm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 8c2b5c0aa754..3fa4d4176993 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1223,6 +1223,7 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) if (!ptr->is_11n_enabled || mwifiex_is_ba_stream_setup(priv, ptr, tid) || + priv->wps.session_enable || ((priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) && !priv->wpa_is_gtk_set)) { -- cgit v1.2.3 From e80c81dc1416e326482c601af3a19d0f9989638e Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 20 Jun 2012 20:21:12 -0700 Subject: mwifiex: fix bugs in event handling code This patch ensures uniformity in event skb sent by interface code (USB/PCIe/SDIO) which automatically fixes following bugs. 1) For USB interface, same buffer is reused for receiving cmd and events from firmware. While handling events, we perform skb_pull(skb, 4) to remove event header. Corresponding skb_push() call is missing while submitting the buffer. 2) For PCIe interface, event skb is passed with event header. Recently added uAP events EVENT_UAP_STA_ASSOC, EVENT_UAP_STA_DEAUTH will not work for PCIe, as they assume event skb points to event body. Signed-off-by: Amitkumar Karwar Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sdio.c | 6 +++--- drivers/net/wireless/mwifiex/sta_event.c | 9 ++++----- drivers/net/wireless/mwifiex/usb.c | 6 +++--- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index e0377473282f..fc8a9bfa1248 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -978,10 +978,10 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, dev_dbg(adapter->dev, "info: --- Rx: Event ---\n"); adapter->event_cause = *(u32 *) skb->data; - skb_pull(skb, MWIFIEX_EVENT_HEADER_LEN); - if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) - memcpy(adapter->event_body, skb->data, skb->len); + memcpy(adapter->event_body, + skb->data + MWIFIEX_EVENT_HEADER_LEN, + skb->len); /* event cause has been saved to adapter->event_cause */ adapter->event_received = true; diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 4ace5a3dcd23..11e731f3581c 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -406,9 +406,9 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) break; case EVENT_UAP_STA_ASSOC: - skb_pull(adapter->event_skb, MWIFIEX_UAP_EVENT_EXTRA_HEADER); memset(&sinfo, 0, sizeof(sinfo)); - event = (struct mwifiex_assoc_event *)adapter->event_skb->data; + event = (struct mwifiex_assoc_event *) + (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER); if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { len = -1; @@ -433,9 +433,8 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) GFP_KERNEL); break; case EVENT_UAP_STA_DEAUTH: - skb_pull(adapter->event_skb, MWIFIEX_UAP_EVENT_EXTRA_HEADER); - cfg80211_del_sta(priv->netdev, adapter->event_skb->data, - GFP_KERNEL); + cfg80211_del_sta(priv->netdev, adapter->event_body + + MWIFIEX_UAP_EVENT_EXTRA_HEADER, GFP_KERNEL); break; case EVENT_UAP_BSS_IDLE: priv->media_connected = false; diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 49ebf20c56eb..e6d796fabab5 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -91,7 +91,6 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, } skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); adapter->event_cause = le32_to_cpu(tmp); - skb_pull(skb, sizeof(u32)); dev_dbg(dev, "event_cause %#x\n", adapter->event_cause); if (skb->len > MAX_EVENT_SIZE) { @@ -99,8 +98,9 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, return -1; } - skb_copy_from_linear_data(skb, adapter->event_body, - skb->len); + memcpy(adapter->event_body, skb->data + + MWIFIEX_EVENT_HEADER_LEN, skb->len); + adapter->event_received = true; adapter->event_skb = skb; break; -- cgit v1.2.3 From 8311f0da95d483ceb76bafae6e0a8c90531fb577 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 20 Jun 2012 20:21:13 -0700 Subject: mwifiex: improve error path handling in usb.c skb allocated during initialisation is reused for receiving commands/events by USB interface. We miss to reset skb->data in failure cases. This patch takes care of it. Signed-off-by: Amitkumar Karwar Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/usb.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index e6d796fabab5..22a5916564b8 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -49,6 +49,7 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, struct device *dev = adapter->dev; u32 recv_type; __le32 tmp; + int ret; if (adapter->hs_activated) mwifiex_process_hs_config(adapter); @@ -69,16 +70,19 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, case MWIFIEX_USB_TYPE_CMD: if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { dev_err(dev, "CMD: skb->len too large\n"); - return -1; + ret = -1; + goto exit_restore_skb; } else if (!adapter->curr_cmd) { dev_dbg(dev, "CMD: no curr_cmd\n"); if (adapter->ps_state == PS_STATE_SLEEP_CFM) { mwifiex_process_sleep_confirm_resp( adapter, skb->data, skb->len); - return 0; + ret = 0; + goto exit_restore_skb; } - return -1; + ret = -1; + goto exit_restore_skb; } adapter->curr_cmd->resp_skb = skb; @@ -87,7 +91,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, case MWIFIEX_USB_TYPE_EVENT: if (skb->len < sizeof(u32)) { dev_err(dev, "EVENT: skb->len too small\n"); - return -1; + ret = -1; + goto exit_restore_skb; } skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); adapter->event_cause = le32_to_cpu(tmp); @@ -95,7 +100,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, if (skb->len > MAX_EVENT_SIZE) { dev_err(dev, "EVENT: event body too large\n"); - return -1; + ret = -1; + goto exit_restore_skb; } memcpy(adapter->event_body, skb->data + @@ -124,6 +130,12 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, } return -EINPROGRESS; + +exit_restore_skb: + /* The buffer will be reused for further cmds/events */ + skb_push(skb, INTF_HEADER_LEN); + + return ret; } static void mwifiex_usb_rx_complete(struct urb *urb) -- cgit v1.2.3 From 0fd66be4a369e4a93bfd559c931e689539cc8e5f Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Fri, 15 Jun 2012 12:21:52 -0700 Subject: mwifiex: parse WPA IE and support WPA/WPA2 mixed mode for uAP Add support for parsing WPA IE from beacon parameter of cfg80211_ap_settings and set it to FW. WPA/WPA2 mixed mode is supported with this patch. Signed-off-by: Avinash Patil Signed-off-by: Kiran Divekar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/ie.c | 55 ++++++++++++++++++++++------------ drivers/net/wireless/mwifiex/uap_cmd.c | 17 ++++++++--- 2 files changed, 49 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index ceb82cd749cc..328fb14d9e14 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -224,29 +224,46 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_ap_settings *params) { struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; - struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL; - struct ieee_types_header *ie = NULL; + struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL; + struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL; u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK; u16 ar_idx = MWIFIEX_AUTO_IDX_MASK, rsn_idx = MWIFIEX_AUTO_IDX_MASK; - u16 mask; + u16 mask, ie_len = 0; + const u8 *vendor_ie; int ret = 0; if (params->beacon.tail && params->beacon.tail_len) { - ie = (void *)cfg80211_find_ie(WLAN_EID_RSN, params->beacon.tail, - params->beacon.tail_len); - if (ie) { - rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!rsn_ie) - return -ENOMEM; - - rsn_ie->ie_index = cpu_to_le16(rsn_idx); - mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | - MGMT_MASK_ASSOC_RESP; - rsn_ie->mgmt_subtype_mask = cpu_to_le16(mask); - rsn_ie->ie_length = cpu_to_le16(ie->len + 2); - memcpy(rsn_ie->ie_buffer, ie, ie->len + 2); - - if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &rsn_idx, + gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + gen_ie->ie_index = cpu_to_le16(rsn_idx); + mask = MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP; + gen_ie->mgmt_subtype_mask = cpu_to_le16(mask); + + rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN, + params->beacon.tail, + params->beacon.tail_len); + if (rsn_ie) { + memcpy(gen_ie->ie_buffer, rsn_ie, rsn_ie->len + 2); + ie_len = rsn_ie->len + 2; + gen_ie->ie_length = cpu_to_le16(ie_len); + } + + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + params->beacon.tail, + params->beacon.tail_len); + if (vendor_ie) { + wpa_ie = (struct ieee_types_header *)vendor_ie; + memcpy(gen_ie->ie_buffer + ie_len, + wpa_ie, wpa_ie->len + 2); + ie_len += wpa_ie->len + 2; + gen_ie->ie_length = cpu_to_le16(ie_len); + } + + if (rsn_ie || wpa_ie) { + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx, NULL, NULL, NULL, NULL)) { ret = -1; @@ -319,7 +336,7 @@ done: kfree(beacon_ie); kfree(pr_ie); kfree(ar_ie); - kfree(rsn_ie); + kfree(gen_ie); return ret; } diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index abb1322a8ceb..f40e93fe894a 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -66,7 +66,7 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, } if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) { - bss_config->protocol = PROTOCOL_WPA2; + bss_config->protocol |= PROTOCOL_WPA2; bss_config->key_mgmt = KEY_MGMT_EAP; } break; @@ -78,7 +78,7 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, } if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) { - bss_config->protocol = PROTOCOL_WPA2; + bss_config->protocol |= PROTOCOL_WPA2; bss_config->key_mgmt = KEY_MGMT_PSK; } break; @@ -92,10 +92,19 @@ int mwifiex_set_secure_params(struct mwifiex_private *priv, case WLAN_CIPHER_SUITE_WEP104: break; case WLAN_CIPHER_SUITE_TKIP: - bss_config->wpa_cfg.pairwise_cipher_wpa = CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_TKIP; break; case WLAN_CIPHER_SUITE_CCMP: - bss_config->wpa_cfg.pairwise_cipher_wpa2 = + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_AES_CCMP; default: break; -- cgit v1.2.3 From f931c7705bb3e21edd345a0fa748462fd3df4122 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 20 Jun 2012 19:58:36 -0700 Subject: mwifiex: wake up main thread to handle Tx traffic if scan is delayed/aborted This is a flaw in recently implemented logic to handle Tx traffic and scan operation simultaneously. We missed to wakeup main thread to handle Tx traffic if scan is delayed/aborted. For some cards (SD8797, for example), firmware will send SLEEP event if there is no activity for 50msec. While handling the SLEEP event, main thread will be woken up and Tx packet gets sent hence. In worst case Tx traffic will be delayed for 50msec. For other cards, such as USB8797, firmware won't send SLEEP event. So, Tx traffic gets stuck if no other event triggers the wakeup of main thread. This patch fixes above issues. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/init.c | 1 + drivers/net/wireless/mwifiex/main.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 0f18ef6a30c8..b543a4d82ff3 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -103,6 +103,7 @@ static void scan_delay_timer_fn(unsigned long data) msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); adapter->scan_delay_cnt++; } + queue_work(priv->adapter->workqueue, &priv->adapter->main_work); } else { /* * Tx data queue is empty. Get scan command from scan_pending_q diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 0f06f07a70e6..f0219efc8953 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -190,7 +190,8 @@ process_start: adapter->tx_lock_flag) break; - if (adapter->scan_processing || adapter->data_sent || + if ((adapter->scan_processing && + !adapter->scan_delay_cnt) || adapter->data_sent || mwifiex_wmm_lists_empty(adapter)) { if (adapter->cmd_sent || adapter->curr_cmd || (!is_command_pending(adapter))) -- cgit v1.2.3 From a4186ea6ec6c903e29294141d4861e8327fd113c Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 20 Jun 2012 19:58:37 -0700 Subject: mwifiex: enhance power save for USB and PCIe chipsets FW will not explicitly notify about host sleep activation to the host for USB and PCIe chipsets. Hence host should generate Host Sleep Activated event as soon as Host Sleep parameters are configured to FW successfully. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 51e023ec1de4..ea37b887a874 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1102,7 +1102,8 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, &resp->params.opt_hs_cfg; uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); - if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE)) { + if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && + adapter->iface_type == MWIFIEX_SDIO) { mwifiex_hs_activated_event(priv, true); return 0; } else { @@ -1114,6 +1115,9 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, } if (conditions != HOST_SLEEP_CFG_CANCEL) { adapter->is_hs_configured = true; + if (adapter->iface_type == MWIFIEX_USB || + adapter->iface_type == MWIFIEX_PCIE) + mwifiex_hs_activated_event(priv, true); } else { adapter->is_hs_configured = false; if (adapter->hs_activated) -- cgit v1.2.3 From 8dc01811da1119d6a5e595b5200b788c6603d3b2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 21 Jun 2012 11:33:32 +0200 Subject: brcmfmac: make inclusion of vmalloc.h explicit fixing linux-next build This patch fixes problem detected in linux-next build for powerpc allyesconfig. The error message below is no longer observed: CC drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.o drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c: In function 'brcmf_sdio_dump_console': drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c:3085: error: implicit declaration of function 'vzalloc' drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c:3085: warning: assignment makes pointer from integer without a cast drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c:3113: error: implicit declaration of function 'vfree' make[2]: *** [drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.o] Error 1 Reported-by: Stephen Rothwell Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 9971e130476d..076b7720ded9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From c49aa4aa2bc89e88672dc419a293d7b8c1f094d2 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 21 Jun 2012 15:49:13 +0200 Subject: brcmsmac: fix NULL pointer crash in brcms_c_regd_init() In the function brcms_c_regd_init() the channels are validated against the device capabilities. This is done for both 2.4G and 5G band, but there are devices that are 2.4G only, ie. BCM4313. For that device this leads to a NULL dereference. This patch adds a check in brcms_c_regd_init() to fix this. Issue introduced in wireless-next tree by following commit: cf03c5d brcm80211: smac: inform mac80211 of the X2 regulatory domain Cc: Seth Forshee Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/channel.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index d3c726021c35..2d365d3486df 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -768,6 +768,11 @@ void brcms_c_regd_init(struct brcms_c_info *wlc) band = wlc->bandstate[BAND_2G_INDEX]; else band = wlc->bandstate[BAND_5G_INDEX]; + + /* skip if band not initialized */ + if (band->pi == NULL) + continue; + wlc_phy_chanspec_band_validch(band->pi, band->bandtype, &sup_chan); -- cgit v1.2.3 From 81b67fd60a75cac36092aa37cd0728aab3a7a938 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 21 Jun 2012 20:33:59 +0530 Subject: ath9k_hw: rename mrcCCKOff to fix smatch warning Rename mrcCCKOff for better code readability and also fixes the smatch warning. drivers/net/wireless/ath/ath9k/ar9003_phy.c:982 ar9003_hw_ani_control() Error invalid range 1 to 0. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 8 ++------ drivers/net/wireless/ath/ath9k/ani.h | 4 +--- drivers/net/wireless/ath/ath9k/ar5008_phy.c | 4 ++-- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 10 +++++----- 4 files changed, 10 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index a90aa0b4c8f1..7ebc3465f22d 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -238,7 +238,7 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah)) return; - if (aniState->mrcCCKOff == entry_cck->mrc_cck_on) + if (aniState->mrcCCK != entry_cck->mrc_cck_on) ath9k_hw_ani_control(ah, ATH9K_ANI_MRC_CCK, entry_cck->mrc_cck_on); @@ -525,11 +525,7 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; - if (AR_SREV_9300_20_OR_LATER(ah)) - ani->mrcCCKOff = - !ATH9K_ANI_ENABLE_MRC_CCK; - else - ani->mrcCCKOff = true; + ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false; ani->ofdmsTurn = true; diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 79c85fc6c32c..e9d841bbe86f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -62,8 +62,6 @@ #define ATH9K_SIG_SPUR_IMM_SETTING_MIN 0 #define ATH9K_SIG_SPUR_IMM_SETTING_MAX 22 -#define ATH9K_ANI_ENABLE_MRC_CCK true - /* values here are relative to the INI */ enum ath9k_ani_cmd { @@ -111,7 +109,7 @@ struct ar5416AniState { u8 ofdmNoiseImmunityLevel; u8 cckNoiseImmunityLevel; bool ofdmsTurn; - u8 mrcCCKOff; + u8 mrcCCK; u8 spurImmunityLevel; u8 firstepLevel; u8 ofdmWeakSigDetect; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index b6efcd9b3129..874186bfda41 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -1234,7 +1234,7 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah, aniState->spurImmunityLevel, aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, - !aniState->mrcCCKOff ? "on" : "off", + aniState->mrcCCK ? "on" : "off", aniState->listenTime, aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); @@ -1322,7 +1322,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; - aniState->mrcCCKOff = true; /* not available on pre AR9003 */ + aniState->mrcCCK = false; /* not available on pre AR9003 */ } static void ar5008_hw_set_nf_limits(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index ec8a8d5c6db5..f1975b9e5393 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -979,16 +979,16 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, AR_PHY_MRC_CCK_ENABLE, is_on); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_MUX_REG, is_on); - if (!is_on != aniState->mrcCCKOff) { + if (is_on != aniState->mrcCCK) { ath_dbg(common, ANI, "** ch %d: MRC CCK: %s=>%s\n", chan->channel, - !aniState->mrcCCKOff ? "on" : "off", + aniState->mrcCCK ? "on" : "off", is_on ? "on" : "off"); if (is_on) ah->stats.ast_ani_ccklow++; else ah->stats.ast_ani_cckhigh++; - aniState->mrcCCKOff = !is_on; + aniState->mrcCCK = is_on; } break; } @@ -1004,7 +1004,7 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, aniState->spurImmunityLevel, aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, - !aniState->mrcCCKOff ? "on" : "off", + aniState->mrcCCK ? "on" : "off", aniState->listenTime, aniState->ofdmPhyErrCount, aniState->cckPhyErrCount); @@ -1114,7 +1114,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; - aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; + aniState->mrcCCK = true; } static void ar9003_hw_set_radar_params(struct ath_hw *ah, -- cgit v1.2.3 From 4b5237cc86872319a321ca1a694dee8866f7d9a3 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 21 Jun 2012 20:34:00 +0530 Subject: ath9k_hw: fix smatch warning in ar9003_hw_spur_mitigate_mrc_cck drivers/net/wireless/ath/ath9k/ar9003_phy.c:211 ar9003_hw_spur_mitigate_mrc_cck() error: potential NULL dereference 'spur_fbin_ptr'. Signed-off-by: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9003_phy.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index f1975b9e5393..6b91ebb158fe 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -173,7 +173,7 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, int cur_bb_spur, negative = 0, cck_spur_freq; int i; int range, max_spur_cnts, synth_freq; - u8 *spur_fbin_ptr = NULL; + u8 *spur_fbin_ptr = ar9003_get_spur_chan_ptr(ah, IS_CHAN_2GHZ(chan)); /* * Need to verify range +/- 10 MHz in control channel, otherwise spur @@ -181,8 +181,6 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, */ if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah)) { - spur_fbin_ptr = ar9003_get_spur_chan_ptr(ah, - IS_CHAN_2GHZ(chan)); if (spur_fbin_ptr[0] == 0) /* No spur */ return; max_spur_cnts = 5; -- cgit v1.2.3 From 5ea276963eacbef742fe4854883c0f69c903fcfd Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 21 Jun 2012 13:08:04 -0500 Subject: rtlwifi: Change debug level for deletion of an entry in CAM When running in AP mode, the driver reports all deletions from CAM in a cryptic manner that makes users think it is an error. change so that the condition is only reported at higher-levels of debugging. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/cam.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c index 3d8cc4a0c86d..6a2d72beb00d 100644 --- a/drivers/net/wireless/rtlwifi/cam.c +++ b/drivers/net/wireless/rtlwifi/cam.c @@ -128,7 +128,7 @@ u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, u32 us_config; struct rtl_priv *rtlpriv = rtl_priv(hw); - RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "EntryNo:%x, ulKeyId=%x, ulEncAlg=%x, ulUseDK=%x MacAddr %pM\n", ul_entry_idx, ul_key_id, ul_enc_alg, ul_default_key, mac_addr); @@ -342,7 +342,8 @@ void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr) /* Remove from HW Security CAM */ memset(rtlpriv->sec.hwsec_cam_sta_addr[i], 0, ETH_ALEN); rtlpriv->sec.hwsec_cam_bitmap &= ~(BIT(0) << i); - pr_info("&&&&&&&&&del entry %d\n", i); + RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, + "del CAM entry %d\n", i); } } return; -- cgit v1.2.3 From e1262efb9bf9864532c0dfca2b2e222aee7bd0a5 Mon Sep 17 00:00:00 2001 From: Arkady Miasnikov Date: Mon, 18 Jun 2012 16:21:12 +0300 Subject: wlcore: access the firmware memory via debugfs Applications running in the user space needs access to the memory of the chip. Examples of such access - read/write global variables - access to firmware log - dump memory after firmware panic event Arbitrary 4-bytes aligned location can be accessed by read/write file wlcore/mem [Check return value of wlcore_raw_read/write and wlcore_set_partition calls as required by the recent IO changes. -- Luca] Signed-off-by: Arkady Miasnikov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/debugfs.c | 192 +++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 1768f37049bd..80dbc5304fac 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -38,6 +38,8 @@ /* ms */ #define WL1271_DEBUGFS_STATS_LIFETIME 1000 +#define WLCORE_MAX_BLOCK_SIZE ((size_t)(4*PAGE_SIZE)) + /* debugfs macros idea from mac80211 */ int wl1271_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...) @@ -1025,6 +1027,195 @@ static const struct file_operations sleep_auth_ops = { .llseek = default_llseek, }; +static ssize_t dev_mem_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + struct wlcore_partition_set part, old_part; + size_t bytes = count; + int ret; + char *buf; + + /* only requests of dword-aligned size and offset are supported */ + if (bytes % 4) + return -EINVAL; + + if (*ppos % 4) + return -EINVAL; + + /* function should return in reasonable time */ + bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE); + + if (bytes == 0) + return -EINVAL; + + memset(&part, 0, sizeof(part)); + part.mem.start = file->f_pos; + part.mem.size = bytes; + + buf = kmalloc(bytes, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) { + ret = -EFAULT; + goto skip_read; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto skip_read; + + /* store current partition and switch partition */ + memcpy(&old_part, &wl->curr_part, sizeof(old_part)); + ret = wlcore_set_partition(wl, &part); + if (ret < 0) + goto part_err; + + ret = wlcore_raw_read(wl, 0, buf, bytes, false); + if (ret < 0) + goto read_err; + +read_err: + /* recover partition */ + ret = wlcore_set_partition(wl, &old_part); + if (ret < 0) + goto part_err; + +part_err: + wl1271_ps_elp_sleep(wl); + +skip_read: + mutex_unlock(&wl->mutex); + + if (ret == 0) { + ret = copy_to_user(user_buf, buf, bytes); + if (ret < bytes) { + bytes -= ret; + *ppos += bytes; + ret = 0; + } else { + ret = -EFAULT; + } + } + + kfree(buf); + + return ((ret == 0) ? bytes : ret); +} + +static ssize_t dev_mem_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + struct wlcore_partition_set part, old_part; + size_t bytes = count; + int ret; + char *buf; + + /* only requests of dword-aligned size and offset are supported */ + if (bytes % 4) + return -EINVAL; + + if (*ppos % 4) + return -EINVAL; + + /* function should return in reasonable time */ + bytes = min(bytes, WLCORE_MAX_BLOCK_SIZE); + + if (bytes == 0) + return -EINVAL; + + memset(&part, 0, sizeof(part)); + part.mem.start = file->f_pos; + part.mem.size = bytes; + + buf = kmalloc(bytes, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = copy_from_user(buf, user_buf, bytes); + if (ret) { + ret = -EFAULT; + goto err_out; + } + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) { + ret = -EFAULT; + goto skip_write; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto skip_write; + + /* store current partition and switch partition */ + memcpy(&old_part, &wl->curr_part, sizeof(old_part)); + ret = wlcore_set_partition(wl, &part); + if (ret < 0) + goto part_err; + + ret = wlcore_raw_write(wl, 0, buf, bytes, false); + if (ret < 0) + goto write_err; + +write_err: + /* recover partition */ + ret = wlcore_set_partition(wl, &old_part); + if (ret < 0) + goto part_err; + +part_err: + wl1271_ps_elp_sleep(wl); + +skip_write: + mutex_unlock(&wl->mutex); + + if (ret == 0) + *ppos += bytes; + +err_out: + kfree(buf); + + return ((ret == 0) ? bytes : ret); +} + +static loff_t dev_mem_seek(struct file *file, loff_t offset, int orig) +{ + loff_t ret; + + /* only requests of dword-aligned size and offset are supported */ + if (offset % 4) + return -EINVAL; + + switch (orig) { + case SEEK_SET: + file->f_pos = offset; + ret = file->f_pos; + break; + case SEEK_CUR: + file->f_pos += offset; + ret = file->f_pos; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct file_operations dev_mem_ops = { + .open = simple_open, + .read = dev_mem_read, + .write = dev_mem_write, + .llseek = dev_mem_seek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -1059,6 +1250,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming); DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming); + DEBUGFS_ADD_PREFIX(dev, mem, rootdir); return 0; -- cgit v1.2.3 From 96caded8d275f67c6000fa219b0c11e7d6bf8e0b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 21 Jun 2012 18:10:47 +0300 Subject: wlcore: cancel suspend when recovery is pending We wish to postpone suspend if recovery is pending. This will make sure the FW is in a good state and perform wowlan wakeup. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index b0795aac4bac..0df28d5cb331 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1661,6 +1661,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); WARN_ON(!wow); + /* we want to perform the recovery before suspending */ + if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { + wl1271_warning("postponing suspend to perform recovery"); + return -EBUSY; + } + wl1271_tx_flush(wl); mutex_lock(&wl->mutex); -- cgit v1.2.3 From f4b57a3b4352f72e461e362cb25917e28bdba80f Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Fri, 22 Jun 2012 14:55:16 +0800 Subject: PCI/ACPI: provide MMCONFIG address for PCI host bridges This patch provide MMCONFIG address for PCI host bridges, which will be used to support host bridge hotplug. It gets MMCONFIG address by evaluating _CBA method if available. Reviewed-by: Yinghai Lu Signed-off-by: Jiang Liu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_root.c | 2 ++ drivers/pci/pci-acpi.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 7aff6312ce7c..ec54014c321c 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -505,6 +505,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; + root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); + /* * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 61e2fefeedab..87f4c504eafb 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -162,6 +162,20 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) return remove_pm_notifier(dev, pci_acpi_wake_dev); } +phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) +{ + acpi_status status = AE_NOT_EXIST; + unsigned long long mcfg_addr; + + if (handle) + status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, + NULL, &mcfg_addr); + if (ACPI_FAILURE(status)) + return 0; + + return (phys_addr_t)mcfg_addr; +} + /* * _SxD returns the D-state with the highest power * (lowest D-state number) supported in the S-state "x". -- cgit v1.2.3 From 8e27628ecf883b9e5825103e40e6f86bf8225f1a Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Fri, 22 Jun 2012 01:10:15 +0000 Subject: smsc911x.c: encapsulate enable irq calls We encapsulate enbale irq functionality in a function call. As on probe the interrupts will be disabled twice, we delete one. Signed-off-by: Matthias Brugger Signed-off-by: David S. Miller --- drivers/net/ethernet/smsc/smsc911x.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 1466e5d2af44..54ca99dbb406 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -1442,6 +1442,14 @@ smsc911x_set_hw_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6]) smsc911x_mac_write(pdata, ADDRL, mac_low32); } +static void smsc911x_disable_irq_chip(struct net_device *dev) +{ + struct smsc911x_data *pdata = netdev_priv(dev); + + smsc911x_reg_write(pdata, INT_EN, 0); + smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF); +} + static int smsc911x_open(struct net_device *dev) { struct smsc911x_data *pdata = netdev_priv(dev); @@ -1494,8 +1502,7 @@ static int smsc911x_open(struct net_device *dev) spin_unlock_irq(&pdata->mac_lock); /* Initialise irqs, but leave all sources disabled */ - smsc911x_reg_write(pdata, INT_EN, 0); - smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF); + smsc911x_disable_irq_chip(dev); /* Set interrupt deassertion to 100uS */ intcfg = ((10 << 24) | INT_CFG_IRQ_EN_); @@ -2215,9 +2222,6 @@ static int __devinit smsc911x_init(struct net_device *dev) if (smsc911x_soft_reset(pdata)) return -ENODEV; - /* Disable all interrupt sources until we bring the device up */ - smsc911x_reg_write(pdata, INT_EN, 0); - ether_setup(dev); dev->flags |= IFF_MULTICAST; netif_napi_add(dev, &pdata->napi, smsc911x_poll, SMSC_NAPI_WEIGHT); @@ -2434,8 +2438,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev) smsc911x_reg_write(pdata, INT_CFG, intcfg); /* Ensure interrupts are globally disabled before connecting ISR */ - smsc911x_reg_write(pdata, INT_EN, 0); - smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF); + smsc911x_disable_irq_chip(dev); retval = request_irq(dev->irq, smsc911x_irqhandler, irq_flags | IRQF_SHARED, dev->name, dev); -- cgit v1.2.3 From d0b8a6f926be5109d711937a74b8d327b29470ba Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 20 Jun 2012 19:05:18 +0000 Subject: bnx2x: sfp+ Tx fault detection added Adds the ability to identify sfp+ modules' Tx fault, and when such occur shut down the link. Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h | 2 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 112 +++++++++++++++++++---- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h | 1 + 3 files changed, 99 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 6b776309e0a1..e7c390c66b2a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -1150,6 +1150,7 @@ struct drv_port_mb { u32 link_status; /* Driver should update this field on any link change event */ + #define LINK_STATUS_NONE (0<<0) #define LINK_STATUS_LINK_FLAG_MASK 0x00000001 #define LINK_STATUS_LINK_UP 0x00000001 #define LINK_STATUS_SPEED_AND_DUPLEX_MASK 0x0000001E @@ -1207,6 +1208,7 @@ struct drv_port_mb { #define LINK_STATUS_PFC_ENABLED 0x20000000 #define LINK_STATUS_PHYSICAL_LINK_FLAG 0x40000000 + #define LINK_STATUS_SFP_TX_FAULT 0x80000000 u32 port_stx; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 91aa565d4374..9e008e4534d4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -4746,6 +4746,8 @@ void bnx2x_sync_link(struct link_params *params, vars->mac_type = MAC_TYPE_NONE; if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG) vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG; + if (vars->link_status & LINK_STATUS_SFP_TX_FAULT) + vars->phy_flags |= PHY_SFP_TX_FAULT_FLAG; } } @@ -12925,30 +12927,41 @@ static void bnx2x_check_over_curr(struct link_params *params, vars->phy_flags &= ~PHY_OVER_CURRENT_FLAG; } -static void bnx2x_analyze_link_error(struct link_params *params, - struct link_vars *vars, u32 lss_status, - u8 notify) +/* Returns 0 if no change occured since last check; 1 otherwise. */ +static u8 bnx2x_analyze_link_error(struct link_params *params, + struct link_vars *vars, u32 status, + u32 phy_flag, u32 link_flag, u8 notify) { struct bnx2x *bp = params->bp; /* Compare new value with previous value */ u8 led_mode; - u32 half_open_conn = (vars->phy_flags & PHY_HALF_OPEN_CONN_FLAG) > 0; + u32 old_status = (vars->phy_flags & phy_flag) ? 1 : 0; - if ((lss_status ^ half_open_conn) == 0) - return; + if ((status ^ old_status) == 0) + return 0; /* If values differ */ - DP(NETIF_MSG_LINK, "Link changed:%x %x->%x\n", vars->link_up, - half_open_conn, lss_status); + switch (phy_flag) { + case PHY_HALF_OPEN_CONN_FLAG: + DP(NETIF_MSG_LINK, "Analyze Remote Fault\n"); + break; + case PHY_SFP_TX_FAULT_FLAG: + DP(NETIF_MSG_LINK, "Analyze TX Fault\n"); + break; + default: + DP(NETIF_MSG_LINK, "Analyze UNKOWN\n"); + } + DP(NETIF_MSG_LINK, "Link changed:[%x %x]->%x\n", vars->link_up, + old_status, status); /* a. Update shmem->link_status accordingly * b. Update link_vars->link_up */ - if (lss_status) { - DP(NETIF_MSG_LINK, "Remote Fault detected !!!\n"); + if (status) { vars->link_status &= ~LINK_STATUS_LINK_UP; + vars->link_status |= link_flag; vars->link_up = 0; - vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG; + vars->phy_flags |= phy_flag; /* activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 1); @@ -12957,10 +12970,10 @@ static void bnx2x_analyze_link_error(struct link_params *params, */ led_mode = LED_MODE_OFF; } else { - DP(NETIF_MSG_LINK, "Remote Fault cleared\n"); vars->link_status |= LINK_STATUS_LINK_UP; + vars->link_status &= ~link_flag; vars->link_up = 1; - vars->phy_flags &= ~PHY_HALF_OPEN_CONN_FLAG; + vars->phy_flags &= ~phy_flag; led_mode = LED_MODE_OPER; /* Clear nig drain */ @@ -12977,6 +12990,8 @@ static void bnx2x_analyze_link_error(struct link_params *params, vars->periodic_flags |= PERIODIC_FLAGS_LINK_EVENT; if (notify) bnx2x_notify_link_changed(bp); + + return 1; } /****************************************************************************** @@ -13018,7 +13033,9 @@ int bnx2x_check_half_open_conn(struct link_params *params, if (REG_RD(bp, mac_base + XMAC_REG_RX_LSS_STATUS)) lss_status = 1; - bnx2x_analyze_link_error(params, vars, lss_status, notify); + bnx2x_analyze_link_error(params, vars, lss_status, + PHY_HALF_OPEN_CONN_FLAG, + LINK_STATUS_NONE, notify); } else if (REG_RD(bp, MISC_REG_RESET_REG_2) & (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port)) { /* Check E1X / E2 BMAC */ @@ -13035,11 +13052,55 @@ int bnx2x_check_half_open_conn(struct link_params *params, REG_RD_DMAE(bp, mac_base + lss_status_reg, wb_data, 2); lss_status = (wb_data[0] > 0); - bnx2x_analyze_link_error(params, vars, lss_status, notify); + bnx2x_analyze_link_error(params, vars, lss_status, + PHY_HALF_OPEN_CONN_FLAG, + LINK_STATUS_NONE, notify); } return 0; } +static void bnx2x_sfp_tx_fault_detection(struct bnx2x_phy *phy, + struct link_params *params, + struct link_vars *vars) +{ + struct bnx2x *bp = params->bp; + u32 cfg_pin, value = 0; + u8 led_change, port = params->port; + /* Get The SFP+ TX_Fault controlling pin ([eg]pio) */ + cfg_pin = (REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, + dev_info.port_hw_config[port].e3_cmn_pin_cfg)) & + PORT_HW_CFG_E3_TX_FAULT_MASK) >> + PORT_HW_CFG_E3_TX_FAULT_SHIFT; + + if (bnx2x_get_cfg_pin(bp, cfg_pin, &value)) { + DP(NETIF_MSG_LINK, "Failed to read pin 0x%02x\n", cfg_pin); + return; + } + + led_change = bnx2x_analyze_link_error(params, vars, value, + PHY_SFP_TX_FAULT_FLAG, + LINK_STATUS_SFP_TX_FAULT, 1); + + if (led_change) { + /* Change TX_Fault led, set link status for further syncs */ + u8 led_mode; + + if (vars->phy_flags & PHY_SFP_TX_FAULT_FLAG) { + led_mode = MISC_REGISTERS_GPIO_HIGH; + vars->link_status |= LINK_STATUS_SFP_TX_FAULT; + } else { + led_mode = MISC_REGISTERS_GPIO_LOW; + vars->link_status &= ~LINK_STATUS_SFP_TX_FAULT; + } + + /* If module is unapproved, led should be on regardless */ + if (!(phy->flags & FLAGS_SFP_NOT_APPROVED)) { + DP(NETIF_MSG_LINK, "Change TX_Fault LED: ->%x\n", + led_mode); + bnx2x_set_e3_module_fault_led(params, led_mode); + } + } +} void bnx2x_period_func(struct link_params *params, struct link_vars *vars) { u16 phy_idx; @@ -13058,7 +13119,26 @@ void bnx2x_period_func(struct link_params *params, struct link_vars *vars) struct bnx2x_phy *phy = ¶ms->phy[INT_PHY]; bnx2x_set_aer_mmd(params, phy); bnx2x_check_over_curr(params, vars); - bnx2x_warpcore_config_runtime(phy, params, vars); + if (vars->rx_tx_asic_rst) + bnx2x_warpcore_config_runtime(phy, params, vars); + + if ((REG_RD(bp, params->shmem_base + + offsetof(struct shmem_region, dev_info. + port_hw_config[params->port].default_cfg)) + & PORT_HW_CFG_NET_SERDES_IF_MASK) == + PORT_HW_CFG_NET_SERDES_IF_SFI) { + if (bnx2x_is_sfp_module_plugged(phy, params)) { + bnx2x_sfp_tx_fault_detection(phy, params, vars); + } else if (vars->link_status & + LINK_STATUS_SFP_TX_FAULT) { + /* Clean trail, interrupt corrects the leds */ + vars->link_status &= ~LINK_STATUS_SFP_TX_FAULT; + vars->phy_flags &= ~PHY_SFP_TX_FAULT_FLAG; + /* Update link status in the shared memory */ + bnx2x_update_mng(params, vars->link_status); + } + } + } } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index e920800a7bc5..cd1f2719b62d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -307,6 +307,7 @@ struct link_vars { #define PHY_PHYSICAL_LINK_FLAG (1<<2) #define PHY_HALF_OPEN_CONN_FLAG (1<<3) #define PHY_OVER_CURRENT_FLAG (1<<4) +#define PHY_SFP_TX_FAULT_FLAG (1<<5) u8 mac_type; #define MAC_TYPE_NONE 0 -- cgit v1.2.3 From d231023eb17134e43bf1dcea631dd156d9904b70 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 20 Jun 2012 19:05:19 +0000 Subject: bnx2x: link cleanup This patch does several things: 1. Add static to function when possible. 2. Correct comments. 3. Change msleep(small) --> usleep_range(small, small*2). Also correct existing calls to usleep_range. 4. Remove dead code. 5. Change 'if(rc != 0)' --> if(rc) Most of these changes are purely semantic. Signed-off-by: Yuval Mintz Signed-off-by: Yaniv Rosner Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 340 +++++++++++------------ 1 file changed, 167 insertions(+), 173 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 9e008e4534d4..b3c33ed2bc96 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -284,7 +284,6 @@ #define ETS_E3B0_PBF_MIN_W_VAL (10000) #define MAX_PACKET_SIZE (9700) -#define WC_UC_TIMEOUT 100 #define MAX_KR_LINK_RETRY 4 /**********************************************************/ @@ -1627,7 +1626,7 @@ static void bnx2x_umac_enable(struct link_params *params, /* Reset UMAC */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)); - usleep_range(1000, 1000); + usleep_range(1000, 2000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)); @@ -1729,7 +1728,7 @@ static void bnx2x_xmac_init(struct link_params *params, u32 max_speed) /* Hard reset */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, MISC_REGISTERS_RESET_REG_2_XMAC); - usleep_range(1000, 1000); + usleep_range(1000, 2000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, MISC_REGISTERS_RESET_REG_2_XMAC); @@ -1759,7 +1758,7 @@ static void bnx2x_xmac_init(struct link_params *params, u32 max_speed) /* Soft reset */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, MISC_REGISTERS_RESET_REG_2_XMAC_SOFT); - usleep_range(1000, 1000); + usleep_range(1000, 2000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, MISC_REGISTERS_RESET_REG_2_XMAC_SOFT); @@ -1880,11 +1879,6 @@ static int bnx2x_emac_enable(struct link_params *params, bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, EMAC_TX_MODE_RESET); - if (CHIP_REV_IS_SLOW(bp)) { - /* config GMII mode */ - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MODE); - EMAC_WR(bp, EMAC_REG_EMAC_MODE, (val | EMAC_MODE_PORT_GMII)); - } else { /* ASIC */ /* pause enable/disable */ bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_FLOW_EN); @@ -1907,7 +1901,6 @@ static int bnx2x_emac_enable(struct link_params *params, } else bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, EMAC_TX_MODE_FLOW_EN); - } /* KEEP_VLAN_TAG, promiscuous */ val = REG_RD(bp, emac_base + EMAC_REG_EMAC_RX_MODE); @@ -1946,23 +1939,23 @@ static int bnx2x_emac_enable(struct link_params *params, val &= ~0x810; EMAC_WR(bp, EMAC_REG_EMAC_MODE, val); - /* enable emac */ + /* Enable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 1); - /* enable emac for jumbo packets */ + /* Enable emac for jumbo packets */ EMAC_WR(bp, EMAC_REG_EMAC_RX_MTU_SIZE, (EMAC_RX_MTU_SIZE_JUMBO_ENA | (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD))); - /* strip CRC */ + /* Strip CRC */ REG_WR(bp, NIG_REG_NIG_INGRESS_EMAC0_NO_CRC + port*4, 0x1); - /* disable the NIG in/out to the bmac */ + /* Disable the NIG in/out to the bmac */ REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0x0); - /* enable the NIG in/out to the emac */ + /* Enable the NIG in/out to the emac */ REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0x1); val = 0; if ((params->feature_config_flags & @@ -1997,7 +1990,7 @@ static void bnx2x_update_pfc_bmac1(struct link_params *params, wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_CONTROL, wb_data, 2); - /* tx control */ + /* TX control */ val = 0xc0; if (!(params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) && @@ -2057,7 +2050,7 @@ static void bnx2x_update_pfc_bmac2(struct link_params *params, wb_data[0] &= ~(1<<2); } else { DP(NETIF_MSG_LINK, "PFC is disabled\n"); - /* disable PFC RX & TX & STATS and set 8 COS */ + /* Disable PFC RX & TX & STATS and set 8 COS */ wb_data[0] = 0x8; wb_data[1] = 0; } @@ -2151,7 +2144,7 @@ static int bnx2x_pfc_brb_get_config_params( PFC_E2_BRB_MAC_FULL_XOFF_THR_PAUSE; config_val->pauseable_th.full_xon = PFC_E2_BRB_MAC_FULL_XON_THR_PAUSE; - /* non pause able*/ + /* Non pause able*/ config_val->non_pauseable_th.pause_xoff = PFC_E2_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE; config_val->non_pauseable_th.pause_xon = @@ -2179,7 +2172,7 @@ static int bnx2x_pfc_brb_get_config_params( PFC_E3A0_BRB_MAC_FULL_XOFF_THR_PAUSE; config_val->pauseable_th.full_xon = PFC_E3A0_BRB_MAC_FULL_XON_THR_PAUSE; - /* non pause able*/ + /* Non pause able*/ config_val->non_pauseable_th.pause_xoff = PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE; config_val->non_pauseable_th.pause_xon = @@ -2209,7 +2202,7 @@ static int bnx2x_pfc_brb_get_config_params( PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_PAUSE; config_val->pauseable_th.full_xon = PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_PAUSE; - /* non pause able*/ + /* Non pause able*/ config_val->non_pauseable_th.pause_xoff = PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE; config_val->non_pauseable_th.pause_xon = @@ -2227,7 +2220,7 @@ static int bnx2x_pfc_brb_get_config_params( PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_PAUSE; config_val->pauseable_th.full_xon = PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_PAUSE; - /* non pause able*/ + /* Non pause able*/ config_val->non_pauseable_th.pause_xoff = PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE; config_val->non_pauseable_th.pause_xon = @@ -2284,7 +2277,7 @@ static void bnx2x_pfc_brb_get_e3b0_config_params( if (pfc_params->cos0_pauseable != pfc_params->cos1_pauseable) { - /* nonpauseable= Lossy + pauseable = Lossless*/ + /* Nonpauseable= Lossy + pauseable = Lossless*/ e3b0_val->lb_guarantied = PFC_E3B0_2P_MIX_PAUSE_LB_GUART; e3b0_val->mac_0_class_t_guarantied = @@ -2483,9 +2476,9 @@ static int bnx2x_update_pfc_brb(struct link_params *params, * This function is needed because NIG ARB_CREDIT_WEIGHT_X are * not continues and ARB_CREDIT_WEIGHT_0 + offset is suitable. ******************************************************************************/ -int bnx2x_pfc_nig_rx_priority_mask(struct bnx2x *bp, - u8 cos_entry, - u32 priority_mask, u8 port) +static int bnx2x_pfc_nig_rx_priority_mask(struct bnx2x *bp, + u8 cos_entry, + u32 priority_mask, u8 port) { u32 nig_reg_rx_priority_mask_add = 0; @@ -2612,7 +2605,7 @@ static void bnx2x_update_pfc_nig(struct link_params *params, REG_WR(bp, port ? NIG_REG_LLFC_EGRESS_SRC_ENABLE_1 : NIG_REG_LLFC_EGRESS_SRC_ENABLE_0, 0x7); - /* output enable for RX_XCM # IF */ + /* Output enable for RX_XCM # IF */ REG_WR(bp, port ? NIG_REG_XCM1_OUT_EN : NIG_REG_XCM0_OUT_EN, xcm_out_en); @@ -2661,10 +2654,10 @@ int bnx2x_update_pfc(struct link_params *params, bnx2x_update_mng(params, vars->link_status); - /* update NIG params */ + /* Update NIG params */ bnx2x_update_pfc_nig(params, vars, pfc_params); - /* update BRB params */ + /* Update BRB params */ bnx2x_status = bnx2x_update_pfc_brb(params, vars, pfc_params); if (bnx2x_status) return bnx2x_status; @@ -2719,7 +2712,7 @@ static int bnx2x_bmac1_enable(struct link_params *params, REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_XGXS_CONTROL, wb_data, 2); - /* tx MAC SA */ + /* TX MAC SA */ wb_data[0] = ((params->mac_addr[2] << 24) | (params->mac_addr[3] << 16) | (params->mac_addr[4] << 8) | @@ -2728,7 +2721,7 @@ static int bnx2x_bmac1_enable(struct link_params *params, params->mac_addr[1]); REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_SOURCE_ADDR, wb_data, 2); - /* mac control */ + /* MAC control */ val = 0x3; if (is_lb) { val |= 0x4; @@ -2738,24 +2731,24 @@ static int bnx2x_bmac1_enable(struct link_params *params, wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); - /* set rx mtu */ + /* Set rx mtu */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_MAX_SIZE, wb_data, 2); bnx2x_update_pfc_bmac1(params, vars); - /* set tx mtu */ + /* Set tx mtu */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_MAX_SIZE, wb_data, 2); - /* set cnt max size */ + /* Set cnt max size */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_CNT_MAX_SIZE, wb_data, 2); - /* configure safc */ + /* Configure SAFC */ wb_data[0] = 0x1000200; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS, @@ -2789,7 +2782,7 @@ static int bnx2x_bmac2_enable(struct link_params *params, udelay(30); - /* tx MAC SA */ + /* TX MAC SA */ wb_data[0] = ((params->mac_addr[2] << 24) | (params->mac_addr[3] << 16) | (params->mac_addr[4] << 8) | @@ -2808,18 +2801,18 @@ static int bnx2x_bmac2_enable(struct link_params *params, wb_data, 2); udelay(30); - /* set rx mtu */ + /* Set RX MTU */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_RX_MAX_SIZE, wb_data, 2); udelay(30); - /* set tx mtu */ + /* Set TX MTU */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_TX_MAX_SIZE, wb_data, 2); udelay(30); - /* set cnt max size */ + /* Set cnt max size */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD - 2; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_CNT_MAX_SIZE, wb_data, 2); @@ -2837,15 +2830,15 @@ static int bnx2x_bmac_enable(struct link_params *params, u8 port = params->port; struct bnx2x *bp = params->bp; u32 val; - /* reset and unreset the BigMac */ + /* Reset and unreset the BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); - msleep(1); + usleep_range(1000, 2000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); - /* enable access for bmac registers */ + /* Enable access for bmac registers */ REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1); /* Enable BMAC according to BMAC type*/ @@ -2903,7 +2896,7 @@ static void bnx2x_bmac_rx_disable(struct bnx2x *bp, u8 port) BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); } - msleep(1); + usleep_range(1000, 2000); } } @@ -2915,17 +2908,16 @@ static int bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl, u32 init_crd, crd; u32 count = 1000; - /* disable port */ + /* Disable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x1); - /* wait for init credit */ + /* Wait for init credit */ init_crd = REG_RD(bp, PBF_REG_P0_INIT_CRD + port*4); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); DP(NETIF_MSG_LINK, "init_crd 0x%x crd 0x%x\n", init_crd, crd); while ((init_crd != crd) && count) { - msleep(5); - + usleep_range(5000, 10000); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); count--; } @@ -2942,18 +2934,18 @@ static int bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl, line_speed == SPEED_1000 || line_speed == SPEED_2500) { REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 1); - /* update threshold */ + /* Update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, 0); - /* update init credit */ + /* Update init credit */ init_crd = 778; /* (800-18-4) */ } else { u32 thresh = (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD)/16; REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); - /* update threshold */ + /* Update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, thresh); - /* update init credit */ + /* Update init credit */ switch (line_speed) { case SPEED_10000: init_crd = thresh + 553 - 22; @@ -2968,12 +2960,12 @@ static int bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl, DP(NETIF_MSG_LINK, "PBF updated to speed %d credit %d\n", line_speed, init_crd); - /* probe the credit changes */ + /* Probe the credit changes */ REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x1); - msleep(5); + usleep_range(5000, 10000); REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x0); - /* enable port */ + /* Enable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x0); return 0; } @@ -3040,7 +3032,7 @@ static int bnx2x_cl22_write(struct bnx2x *bp, REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, mode & ~EMAC_MDIO_MODE_CLAUSE_45); - /* address */ + /* Address */ tmp = ((phy->addr << 21) | (reg << 16) | val | EMAC_MDIO_COMM_COMMAND_WRITE_22 | EMAC_MDIO_COMM_START_BUSY); @@ -3076,7 +3068,7 @@ static int bnx2x_cl22_read(struct bnx2x *bp, REG_WR(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, mode & ~EMAC_MDIO_MODE_CLAUSE_45); - /* address */ + /* Address */ val = ((phy->addr << 21) | (reg << 16) | EMAC_MDIO_COMM_COMMAND_READ_22 | EMAC_MDIO_COMM_START_BUSY); @@ -3114,7 +3106,7 @@ static int bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy, if (phy->flags & FLAGS_MDC_MDIO_WA_B0) bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS, EMAC_MDIO_STATUS_10MB); - /* address */ + /* Address */ val = ((phy->addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); @@ -3135,7 +3127,7 @@ static int bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy, *ret_val = 0; rc = -EFAULT; } else { - /* data */ + /* Data */ val = ((phy->addr << 21) | (devad << 16) | EMAC_MDIO_COMM_COMMAND_READ_45 | EMAC_MDIO_COMM_START_BUSY); @@ -3183,7 +3175,7 @@ static int bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy, bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS, EMAC_MDIO_STATUS_10MB); - /* address */ + /* Address */ tmp = ((phy->addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); @@ -3203,7 +3195,7 @@ static int bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy, netdev_err(bp->dev, "MDC/MDIO access timeout\n"); rc = -EFAULT; } else { - /* data */ + /* Data */ tmp = ((phy->addr << 21) | (devad << 16) | val | EMAC_MDIO_COMM_COMMAND_WRITE_45 | EMAC_MDIO_COMM_START_BUSY); @@ -3293,23 +3285,23 @@ static int bnx2x_bsc_read(struct link_params *params, xfer_cnt = 16 - lc_addr; - /* enable the engine */ + /* Enable the engine */ val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); val |= MCPR_IMC_COMMAND_ENABLE; REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val); - /* program slave device ID */ + /* Program slave device ID */ val = (sl_devid << 16) | sl_addr; REG_WR(bp, MCP_REG_MCPR_IMC_SLAVE_CONTROL, val); - /* start xfer with 0 byte to update the address pointer ???*/ + /* Start xfer with 0 byte to update the address pointer ???*/ val = (MCPR_IMC_COMMAND_ENABLE) | (MCPR_IMC_COMMAND_WRITE_OP << MCPR_IMC_COMMAND_OPERATION_BITSHIFT) | (lc_addr << MCPR_IMC_COMMAND_TRANSFER_ADDRESS_BITSHIFT) | (0); REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val); - /* poll for completion */ + /* Poll for completion */ i = 0; val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); while (((val >> MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT) & 0x3) != 1) { @@ -3325,7 +3317,7 @@ static int bnx2x_bsc_read(struct link_params *params, if (rc == -EFAULT) return rc; - /* start xfer with read op */ + /* Start xfer with read op */ val = (MCPR_IMC_COMMAND_ENABLE) | (MCPR_IMC_COMMAND_READ_OP << MCPR_IMC_COMMAND_OPERATION_BITSHIFT) | @@ -3333,7 +3325,7 @@ static int bnx2x_bsc_read(struct link_params *params, (xfer_cnt); REG_WR(bp, MCP_REG_MCPR_IMC_COMMAND, val); - /* poll for completion */ + /* Poll for completion */ i = 0; val = REG_RD(bp, MCP_REG_MCPR_IMC_COMMAND); while (((val >> MCPR_IMC_COMMAND_IMC_STATUS_BITSHIFT) & 0x3) != 1) { @@ -3436,7 +3428,7 @@ static u8 bnx2x_get_warpcore_lane(struct bnx2x_phy *phy, port = port ^ 1; lane = (port<<1) + path; - } else { /* two port mode - no port swap */ + } else { /* Two port mode - no port swap */ /* Figure out path swap value */ path_swap_ovr = @@ -3514,7 +3506,7 @@ static void bnx2x_serdes_deassert(struct bnx2x *bp, u8 port) val = SERDES_RESET_BITS << (port*16); - /* reset and unreset the SerDes/XGXS */ + /* Reset and unreset the SerDes/XGXS */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, val); udelay(500); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_SET, val); @@ -3535,7 +3527,7 @@ static void bnx2x_xgxs_deassert(struct link_params *params) val = XGXS_RESET_BITS << (port*16); - /* reset and unreset the SerDes/XGXS */ + /* Reset and unreset the SerDes/XGXS */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, val); udelay(500); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_SET, val); @@ -3627,7 +3619,7 @@ static void bnx2x_ext_phy_set_pause(struct link_params *params, { u16 val; struct bnx2x *bp = params->bp; - /* read modify write pause advertizing */ + /* Read modify write pause advertizing */ bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &val); val &= ~MDIO_AN_REG_ADV_PAUSE_BOTH; @@ -3945,7 +3937,7 @@ static void bnx2x_warpcore_set_10G_KR(struct bnx2x_phy *phy, bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_RX66_CONTROL, 0xF9); - /* set and clear loopback to cause a reset to 64/66 decoder */ + /* Set and clear loopback to cause a reset to 64/66 decoder */ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x4000); bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, @@ -4373,7 +4365,7 @@ static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy, if (!vars->turn_to_run_wc_rt) return; - /* return if there is no link partner */ + /* Return if there is no link partner */ if (!(bnx2x_warpcore_get_sigdet(phy, params))) { DP(NETIF_MSG_LINK, "bnx2x_warpcore_get_sigdet false\n"); return; @@ -4407,7 +4399,7 @@ static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy, bnx2x_warpcore_reset_lane(bp, phy, 1); bnx2x_warpcore_reset_lane(bp, phy, 0); - /* restart Autoneg */ + /* Restart Autoneg */ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1200); @@ -4645,8 +4637,9 @@ static void bnx2x_set_warpcore_loopback(struct bnx2x_phy *phy, } -void bnx2x_sync_link(struct link_params *params, - struct link_vars *vars) + +static void bnx2x_sync_link(struct link_params *params, + struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 link_10g_plus; @@ -4719,7 +4712,7 @@ void bnx2x_sync_link(struct link_params *params, USES_WARPCORE(bp) && (vars->line_speed == SPEED_1000)) vars->phy_flags |= PHY_SGMII_FLAG; - /* anything 10 and over uses the bmac */ + /* Anything 10 and over uses the bmac */ link_10g_plus = (vars->line_speed >= SPEED_10000); if (link_10g_plus) { @@ -4733,7 +4726,7 @@ void bnx2x_sync_link(struct link_params *params, else vars->mac_type = MAC_TYPE_EMAC; } - } else { /* link down */ + } else { /* Link down */ DP(NETIF_MSG_LINK, "phy link down\n"); vars->phy_link_up = 0; @@ -4742,7 +4735,7 @@ void bnx2x_sync_link(struct link_params *params, vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; - /* indicate no mac active */ + /* Indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; if (vars->link_status & LINK_STATUS_PHYSICAL_LINK_FLAG) vars->phy_flags |= PHY_HALF_OPEN_CONN_FLAG; @@ -4813,7 +4806,7 @@ static void bnx2x_set_master_ln(struct link_params *params, PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); - /* set the master_ln for AN */ + /* Set the master_ln for AN */ CL22_RD_OVER_CL45(bp, phy, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TEST_MODE_LANE, @@ -4836,7 +4829,7 @@ static int bnx2x_reset_unicore(struct link_params *params, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); - /* reset the unicore */ + /* Reset the unicore */ CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, @@ -4845,11 +4838,11 @@ static int bnx2x_reset_unicore(struct link_params *params, if (set_serdes) bnx2x_set_serdes_access(bp, params->port); - /* wait for the reset to self clear */ + /* Wait for the reset to self clear */ for (i = 0; i < MDIO_ACCESS_TIMEOUT; i++) { udelay(5); - /* the reset erased the previous bank value */ + /* The reset erased the previous bank value */ CL22_RD_OVER_CL45(bp, phy, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, @@ -5067,7 +5060,7 @@ static void bnx2x_set_autoneg(struct bnx2x_phy *phy, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, reg_val); } -/* program SerDes, forced speed */ +/* Program SerDes, forced speed */ static void bnx2x_program_serdes(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) @@ -5075,7 +5068,7 @@ static void bnx2x_program_serdes(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u16 reg_val; - /* program duplex, disable autoneg and sgmii*/ + /* Program duplex, disable autoneg and sgmii*/ CL22_RD_OVER_CL45(bp, phy, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, ®_val); @@ -5094,7 +5087,7 @@ static void bnx2x_program_serdes(struct bnx2x_phy *phy, CL22_RD_OVER_CL45(bp, phy, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, ®_val); - /* clearing the speed value before setting the right speed */ + /* Clearing the speed value before setting the right speed */ DP(NETIF_MSG_LINK, "MDIO_REG_BANK_SERDES_DIGITAL = 0x%x\n", reg_val); reg_val &= ~(MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_MASK | @@ -5123,7 +5116,7 @@ static void bnx2x_set_brcm_cl37_advertisement(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u16 val = 0; - /* set extended capabilities */ + /* Set extended capabilities */ if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) val |= MDIO_OVER_1G_UP1_2_5G; if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) @@ -5143,7 +5136,7 @@ static void bnx2x_set_ieee_aneg_advertisement(struct bnx2x_phy *phy, { struct bnx2x *bp = params->bp; u16 val; - /* for AN, we are always publishing full duplex */ + /* For AN, we are always publishing full duplex */ CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_COMBO_IEEE0, @@ -5205,14 +5198,14 @@ static void bnx2x_initialize_sgmii_process(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u16 control1; - /* in SGMII mode, the unicore is always slave */ + /* In SGMII mode, the unicore is always slave */ CL22_RD_OVER_CL45(bp, phy, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &control1); control1 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_INVERT_SIGNAL_DETECT; - /* set sgmii mode (and not fiber) */ + /* Set sgmii mode (and not fiber) */ control1 &= ~(MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_FIBER_MODE | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_MSTR_MODE); @@ -5221,9 +5214,9 @@ static void bnx2x_initialize_sgmii_process(struct bnx2x_phy *phy, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, control1); - /* if forced speed */ + /* If forced speed */ if (!(vars->line_speed == SPEED_AUTO_NEG)) { - /* set speed, disable autoneg */ + /* Set speed, disable autoneg */ u16 mii_control; CL22_RD_OVER_CL45(bp, phy, @@ -5244,16 +5237,16 @@ static void bnx2x_initialize_sgmii_process(struct bnx2x_phy *phy, MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_1000; break; case SPEED_10: - /* there is nothing to set for 10M */ + /* There is nothing to set for 10M */ break; default: - /* invalid speed for SGMII */ + /* Invalid speed for SGMII */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", vars->line_speed); break; } - /* setting the full duplex */ + /* Setting the full duplex */ if (phy->req_duplex == DUPLEX_FULL) mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; @@ -5263,7 +5256,7 @@ static void bnx2x_initialize_sgmii_process(struct bnx2x_phy *phy, mii_control); } else { /* AN mode */ - /* enable and restart AN */ + /* Enable and restart AN */ bnx2x_restart_autoneg(phy, params, 0); } } @@ -5359,7 +5352,7 @@ static void bnx2x_flow_ctrl_resolve(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; - /* resolve from gp_status in case of AN complete and not sgmii */ + /* Resolve from gp_status in case of AN complete and not sgmii */ if (phy->req_flow_ctrl != BNX2X_FLOW_CTRL_AUTO) { /* Update the advertised flow-controled of LD/LP in AN */ if (phy->req_line_speed == SPEED_AUTO_NEG) @@ -5583,7 +5576,7 @@ static int bnx2x_link_settings_status(struct bnx2x_phy *phy, bnx2x_xgxs_an_resolve(phy, params, vars, gp_status); } - } else { /* link_down */ + } else { /* Link_down */ if ((phy->req_line_speed == SPEED_AUTO_NEG) && SINGLE_MEDIA_DIRECT(params)) { /* Check signal is detected */ @@ -5732,12 +5725,12 @@ static void bnx2x_set_gmii_tx_driver(struct link_params *params) u16 tx_driver; u16 bank; - /* read precomp */ + /* Read precomp */ CL22_RD_OVER_CL45(bp, phy, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_LP_UP2, &lp_up2); - /* bits [10:7] at lp_up2, positioned at [15:12] */ + /* Bits [10:7] at lp_up2, positioned at [15:12] */ lp_up2 = (((lp_up2 & MDIO_OVER_1G_LP_UP2_PREEMPHASIS_MASK) >> MDIO_OVER_1G_LP_UP2_PREEMPHASIS_SHIFT) << MDIO_TX0_TX_DRIVER_PREEMPHASIS_SHIFT); @@ -5751,7 +5744,7 @@ static void bnx2x_set_gmii_tx_driver(struct link_params *params) bank, MDIO_TX0_TX_DRIVER, &tx_driver); - /* replace tx_driver bits [15:12] */ + /* Replace tx_driver bits [15:12] */ if (lp_up2 != (tx_driver & MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK)) { tx_driver &= ~MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK; @@ -5847,16 +5840,16 @@ static void bnx2x_xgxs_config_init(struct bnx2x_phy *phy, FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) bnx2x_set_preemphasis(phy, params); - /* forced speed requested? */ + /* Forced speed requested? */ if (vars->line_speed != SPEED_AUTO_NEG || (SINGLE_MEDIA_DIRECT(params) && params->loopback_mode == LOOPBACK_EXT)) { DP(NETIF_MSG_LINK, "not SGMII, no AN\n"); - /* disable autoneg */ + /* Disable autoneg */ bnx2x_set_autoneg(phy, params, vars, 0); - /* program speed and duplex */ + /* Program speed and duplex */ bnx2x_program_serdes(phy, params, vars); } else { /* AN_mode */ @@ -5865,14 +5858,14 @@ static void bnx2x_xgxs_config_init(struct bnx2x_phy *phy, /* AN enabled */ bnx2x_set_brcm_cl37_advertisement(phy, params); - /* program duplex & pause advertisement (for aneg) */ + /* Program duplex & pause advertisement (for aneg) */ bnx2x_set_ieee_aneg_advertisement(phy, params, vars->ieee_fc); - /* enable autoneg */ + /* Enable autoneg */ bnx2x_set_autoneg(phy, params, vars, enable_cl73); - /* enable and restart AN */ + /* Enable and restart AN */ bnx2x_restart_autoneg(phy, params, enable_cl73); } @@ -5908,12 +5901,12 @@ static int bnx2x_prepare_xgxs(struct bnx2x_phy *phy, bnx2x_set_master_ln(params, phy); rc = bnx2x_reset_unicore(params, phy, 0); - /* reset the SerDes and wait for reset bit return low */ - if (rc != 0) + /* Reset the SerDes and wait for reset bit return low */ + if (rc) return rc; bnx2x_set_aer_mmd(params, phy); - /* setting the masterLn_def again after the reset */ + /* Setting the masterLn_def again after the reset */ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) { bnx2x_set_master_ln(params, phy); bnx2x_set_swap_lanes(params, phy); @@ -5938,7 +5931,7 @@ static u16 bnx2x_wait_reset_complete(struct bnx2x *bp, MDIO_PMA_REG_CTRL, &ctrl); if (!(ctrl & (1<<15))) break; - msleep(1); + usleep_range(1000, 2000); } if (cnt == 1000) @@ -6169,7 +6162,7 @@ static void bnx2x_set_xgxs_loopback(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "XGXS 10G loopback enable\n"); if (!CHIP_IS_E3(bp)) { - /* change the uni_phy_addr in the nig */ + /* Change the uni_phy_addr in the nig */ md_devad = REG_RD(bp, (NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18)); @@ -6189,11 +6182,11 @@ static void bnx2x_set_xgxs_loopback(struct bnx2x_phy *phy, (MDIO_CL73_IEEEB0_CL73_AN_CONTROL & 0xf)), 0x6041); msleep(200); - /* set aer mmd back */ + /* Set aer mmd back */ bnx2x_set_aer_mmd(params, phy); if (!CHIP_IS_E3(bp)) { - /* and md_devad */ + /* And md_devad */ REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, md_devad); } @@ -6390,7 +6383,7 @@ int bnx2x_test_link(struct link_params *params, struct link_vars *vars, MDIO_REG_BANK_GP_STATUS, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); - /* link is up only if both local phy and external phy are up */ + /* Link is up only if both local phy and external phy are up */ if (!(gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS)) return -ESRCH; } @@ -6512,7 +6505,7 @@ static int bnx2x_link_initialize(struct link_params *params, static void bnx2x_int_link_reset(struct bnx2x_phy *phy, struct link_params *params) { - /* reset the SerDes/XGXS */ + /* Reset the SerDes/XGXS */ REG_WR(params->bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, (0x1ff << (params->port*16))); } @@ -6545,10 +6538,10 @@ static int bnx2x_update_link_down(struct link_params *params, DP(NETIF_MSG_LINK, "Port %x: Link is down\n", port); bnx2x_set_led(params, vars, LED_MODE_OFF, 0); vars->phy_flags &= ~PHY_PHYSICAL_LINK_FLAG; - /* indicate no mac active */ + /* Indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; - /* update shared memory */ + /* Update shared memory */ vars->link_status &= ~(LINK_STATUS_SPEED_AND_DUPLEX_MASK | LINK_STATUS_LINK_UP | LINK_STATUS_PHYSICAL_LINK_FLAG | @@ -6561,15 +6554,15 @@ static int bnx2x_update_link_down(struct link_params *params, vars->line_speed = 0; bnx2x_update_mng(params, vars->link_status); - /* activate nig drain */ + /* Activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); - /* disable emac */ + /* Disable emac */ if (!CHIP_IS_E3(bp)) REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); - msleep(10); - /* reset BigMac/Xmac */ + usleep_range(10000, 20000); + /* Reset BigMac/Xmac */ if (CHIP_IS_E1x(bp) || CHIP_IS_E2(bp)) { bnx2x_bmac_rx_disable(bp, params->port); @@ -6578,6 +6571,7 @@ static int bnx2x_update_link_down(struct link_params *params, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); } if (CHIP_IS_E3(bp)) { + /* Prevent LPI Generation by chip */ REG_WR(bp, MISC_REG_CPMU_LP_FW_ENABLE_P0 + (params->port << 2), 0); REG_WR(bp, MISC_REG_CPMU_LP_DR_ENABLE, 0); @@ -6668,10 +6662,10 @@ static int bnx2x_update_link_up(struct link_params *params, rc |= bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); - /* disable drain */ + /* Disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 0); - /* update shared memory */ + /* Update shared memory */ bnx2x_update_mng(params, vars->link_status); bnx2x_update_mng_eee(params, vars->eee_status); /* Check remote fault */ @@ -6739,7 +6733,7 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); - /* disable emac */ + /* Disable emac */ if (!CHIP_IS_E3(bp)) REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); @@ -6884,11 +6878,11 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) } else if (prev_line_speed != vars->line_speed) { REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); - msleep(1); + usleep_range(1000, 2000); } } - /* anything 10 and over uses the bmac */ + /* Anything 10 and over uses the bmac */ link_10g_plus = (vars->line_speed >= SPEED_10000); bnx2x_link_int_ack(params, vars, link_10g_plus); @@ -6954,7 +6948,7 @@ void bnx2x_ext_phy_hw_reset(struct bnx2x *bp, u8 port) { bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); - msleep(1); + usleep_range(1000, 2000); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); } @@ -7051,7 +7045,7 @@ static int bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp, MDIO_PMA_REG_GEN_CTRL, 0x0001); - /* ucode reboot and rst */ + /* Ucode reboot and rst */ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, @@ -7095,7 +7089,7 @@ static int bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &fw_msgout); - msleep(1); + usleep_range(1000, 2000); } while (fw_ver1 == 0 || fw_ver1 == 0x4321 || ((fw_msgout & 0xff) != 0x03 && (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073))); @@ -7189,11 +7183,11 @@ static int bnx2x_8073_xaui_wa(struct bnx2x *bp, struct bnx2x_phy *phy) "XAUI workaround has completed\n"); return 0; } - msleep(3); + usleep_range(3000, 6000); } break; } - msleep(3); + usleep_range(3000, 6000); } DP(NETIF_MSG_LINK, "Warning: XAUI work-around timeout !!!\n"); return -EINVAL; @@ -7267,7 +7261,7 @@ static int bnx2x_8073_config_init(struct bnx2x_phy *phy, bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, gpio_port); - /* enable LASI */ + /* Enable LASI */ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL, (1<<2)); bnx2x_cl45_write(bp, phy, @@ -7415,7 +7409,7 @@ static u8 bnx2x_8073_read_status(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "8703 LASI status 0x%x\n", val1); - /* clear the interrupt LASI status register */ + /* Clear the interrupt LASI status register */ bnx2x_cl45_read(bp, phy, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); bnx2x_cl45_read(bp, phy, @@ -7794,7 +7788,7 @@ static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy, if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_IDLE) return 0; - msleep(1); + usleep_range(1000, 2000); } return -EINVAL; } @@ -7876,7 +7870,7 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy, /* Wait appropriate time for two-wire command to finish before * polling the status register */ - msleep(1); + usleep_range(1000, 2000); /* Wait up to 500us for command complete status */ for (i = 0; i < 100; i++) { @@ -7912,7 +7906,7 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy, if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_IDLE) return 0; - msleep(1); + usleep_range(1000, 2000); } return -EINVAL; @@ -8091,7 +8085,7 @@ static int bnx2x_verify_sfp_module(struct bnx2x_phy *phy, return 0; } - /* format the warning message */ + /* Format the warning message */ if (bnx2x_read_sfp_module_eeprom(phy, params, SFP_EEPROM_VENDOR_NAME_ADDR, @@ -8137,7 +8131,7 @@ static int bnx2x_wait_for_sfp_module_initialized(struct bnx2x_phy *phy, timeout * 5); return 0; } - msleep(5); + usleep_range(5000, 10000); } return -EINVAL; } @@ -8472,7 +8466,7 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Failed to get valid module type\n"); return -EINVAL; } else if (bnx2x_verify_sfp_module(phy, params) != 0) { - /* check SFP+ module compatibility */ + /* Check SFP+ module compatibility */ DP(NETIF_MSG_LINK, "Module verification failed!!\n"); rc = -EINVAL; /* Turn on fault module-detected led */ @@ -8603,7 +8597,7 @@ static u8 bnx2x_8706_8726_read_status(struct bnx2x_phy *phy, bnx2x_sfp_mask_fault(bp, phy, MDIO_PMA_LASI_TXSTAT, MDIO_PMA_LASI_TXCTRL); - /* clear LASI indication*/ + /* Clear LASI indication*/ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_STAT, &val1); bnx2x_cl45_read(bp, phy, @@ -8671,7 +8665,7 @@ static u8 bnx2x_8706_config_init(struct bnx2x_phy *phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &val); if (val) break; - msleep(10); + usleep_range(10000, 20000); } DP(NETIF_MSG_LINK, "XGXS 8706 is initialized after %d ms\n", cnt); if ((params->feature_config_flags & @@ -8800,7 +8794,7 @@ static void bnx2x_8726_external_rom_boot(struct bnx2x_phy *phy, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); - /* wait for 150ms for microcode load */ + /* Wait for 150ms for microcode load */ msleep(150); /* Disable serial boot control, tristates pins SS_N, SCK, MOSI, MISO */ @@ -9011,7 +9005,7 @@ static int bnx2x_8727_config_init(struct bnx2x_phy *phy, lasi_ctrl_val = 0x0006; DP(NETIF_MSG_LINK, "Initializing BCM8727\n"); - /* enable LASI */ + /* Enable LASI */ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXCTRL, rx_alarm_ctrl_val); @@ -9733,7 +9727,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, MDIO_84833_CMD_HDLR_STATUS, &val); if (val == PHY84833_STATUS_CMD_OPEN_FOR_CMDS) break; - msleep(1); + usleep_range(1000, 2000); } if (idx >= PHY84833_CMDHDLR_WAIT) { DP(NETIF_MSG_LINK, "FW cmd: FW not ready.\n"); @@ -9754,7 +9748,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, if ((val == PHY84833_STATUS_CMD_COMPLETE_PASS) || (val == PHY84833_STATUS_CMD_COMPLETE_ERROR)) break; - msleep(1); + usleep_range(1000, 2000); } if ((idx >= PHY84833_CMDHDLR_WAIT) || (val == PHY84833_STATUS_CMD_COMPLETE_ERROR)) { @@ -9924,7 +9918,7 @@ static int bnx2x_8483x_disable_eee(struct bnx2x_phy *phy, /* Prevent Phy from working in EEE and advertising it */ rc = bnx2x_84833_cmd_hdlr(phy, params, PHY84833_CMD_SET_EEE_MODE, &cmd_args, 1); - if (rc != 0) { + if (rc) { DP(NETIF_MSG_LINK, "EEE disable failed.\n"); return rc; } @@ -9947,7 +9941,7 @@ static int bnx2x_8483x_enable_eee(struct bnx2x_phy *phy, rc = bnx2x_84833_cmd_hdlr(phy, params, PHY84833_CMD_SET_EEE_MODE, &cmd_args, 1); - if (rc != 0) { + if (rc) { DP(NETIF_MSG_LINK, "EEE enable failed.\n"); return rc; } @@ -9975,7 +9969,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, u16 cmd_args[PHY84833_CMDHDLR_MAX_ARGS]; int rc = 0; - msleep(1); + usleep_range(1000, 2000); if (!(CHIP_IS_E1(bp))) port = BP_PATH(bp); @@ -10064,7 +10058,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, rc = bnx2x_84833_cmd_hdlr(phy, params, PHY84833_CMD_SET_EEE_MODE, cmd_args, PHY84833_CMDHDLR_MAX_ARGS); - if (rc != 0) + if (rc) DP(NETIF_MSG_LINK, "Cfg AutogrEEEn failed.\n"); } if (initialize) @@ -10108,7 +10102,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, vars->eee_status &= ~SHMEM_EEE_REQUESTED_BIT; rc = bnx2x_8483x_eee_timers(params, vars); - if (rc != 0) { + if (rc) { DP(NETIF_MSG_LINK, "Failed to configure EEE timers\n"); bnx2x_8483x_disable_eee(phy, params, vars); return rc; @@ -10121,7 +10115,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, rc = bnx2x_8483x_enable_eee(phy, params, vars); else rc = bnx2x_8483x_disable_eee(phy, params, vars); - if (rc != 0) { + if (rc) { DP(NETIF_MSG_LINK, "Failed to set EEE advertisment\n"); return rc; } @@ -10222,7 +10216,7 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy, } } if (link_up) { - DP(NETIF_MSG_LINK, "BCM84823: link speed is %d\n", + DP(NETIF_MSG_LINK, "BCM848x3: link speed is %d\n", vars->line_speed); bnx2x_ext_phy_resolve_fc(phy, params, vars); @@ -10564,7 +10558,7 @@ static int bnx2x_54618se_config_init(struct bnx2x_phy *phy, u32 cfg_pin; DP(NETIF_MSG_LINK, "54618SE cfg init\n"); - usleep_range(1000, 1000); + usleep_range(1000, 2000); /* This works with E3 only, no need to check the chip * before determining the port. @@ -10633,7 +10627,7 @@ static int bnx2x_54618se_config_init(struct bnx2x_phy *phy, MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) fc_val |= MDIO_AN_REG_ADV_PAUSE_PAUSE; - /* read all advertisement */ + /* Read all advertisement */ bnx2x_cl22_read(bp, phy, 0x09, &an_1000_val); @@ -10670,7 +10664,7 @@ static int bnx2x_54618se_config_init(struct bnx2x_phy *phy, 0x09, &an_1000_val); - /* set 100 speed advertisement */ + /* Set 100 speed advertisement */ if (((phy->req_line_speed == SPEED_AUTO_NEG) && (phy->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL | @@ -10684,7 +10678,7 @@ static int bnx2x_54618se_config_init(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Advertising 100M\n"); } - /* set 10 speed advertisement */ + /* Set 10 speed advertisement */ if (((phy->req_line_speed == SPEED_AUTO_NEG) && (phy->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL | @@ -11050,7 +11044,7 @@ static u8 bnx2x_7101_read_status(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "10G-base-T PMA status 0x%x->0x%x\n", val2, val1); link_up = ((val1 & 4) == 4); - /* if link is up print the AN outcome of the SFX7101 PHY */ + /* If link is up print the AN outcome of the SFX7101 PHY */ if (link_up) { bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_MASTER_STATUS, @@ -11062,7 +11056,7 @@ static u8 bnx2x_7101_read_status(struct bnx2x_phy *phy, bnx2x_ext_phy_10G_an_resolve(bp, phy, vars); bnx2x_ext_phy_resolve_fc(phy, params, vars); - /* read LP advertised speeds */ + /* Read LP advertised speeds */ if (val2 & (1<<11)) vars->link_status |= LINK_STATUS_LINK_PARTNER_10GXFD_CAPABLE; @@ -12260,7 +12254,7 @@ int bnx2x_phy_init(struct link_params *params, struct link_vars *vars) vars->mac_type = MAC_TYPE_NONE; vars->phy_flags = 0; - /* disable attentions */ + /* Disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | @@ -12320,7 +12314,7 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, struct bnx2x *bp = params->bp; u8 phy_index, port = params->port, clear_latch_ind = 0; DP(NETIF_MSG_LINK, "Resetting the link of port %d\n", port); - /* disable attentions */ + /* Disable attentions */ vars->link_status = 0; bnx2x_update_mng(params, vars->link_status); vars->eee_status &= ~(SHMEM_EEE_LP_ADV_STATUS_MASK | @@ -12332,10 +12326,10 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); - /* activate nig drain */ + /* Activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); - /* disable nig egress interface */ + /* Disable nig egress interface */ if (!CHIP_IS_E3(bp)) { REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0); @@ -12348,15 +12342,15 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, bnx2x_xmac_disable(params); bnx2x_umac_disable(params); } - /* disable emac */ + /* Disable emac */ if (!CHIP_IS_E3(bp)) REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); - msleep(10); + usleep_range(10000, 20000); /* The PHY reset is controlled by GPIO 1 * Hold it as vars low */ - /* clear link led */ + /* Clear link led */ bnx2x_set_mdio_clk(bp, params->chip_id, port); bnx2x_set_led(params, vars, LED_MODE_OFF, 0); @@ -12386,9 +12380,9 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, params->phy[INT_PHY].link_reset( ¶ms->phy[INT_PHY], params); - /* disable nig ingress interface */ + /* Disable nig ingress interface */ if (!CHIP_IS_E3(bp)) { - /* reset BigMac */ + /* Reset BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0); @@ -12445,7 +12439,7 @@ static int bnx2x_8073_common_init_phy(struct bnx2x *bp, DP(NETIF_MSG_LINK, "populate_phy failed\n"); return -EINVAL; } - /* disable attentions */ + /* Disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port_of_path*4, (NIG_MASK_XGXS0_LINK_STATUS | @@ -12519,7 +12513,7 @@ static int bnx2x_8073_common_init_phy(struct bnx2x *bp, bnx2x_cl45_write(bp, phy_blk[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, (val & (~(1<<10)))); - msleep(15); + usleep_range(15000, 30000); /* Read modify write the SPI-ROM version select register */ bnx2x_cl45_read(bp, phy_blk[port], @@ -12551,7 +12545,7 @@ static int bnx2x_8726_common_init_phy(struct bnx2x *bp, REG_WR(bp, MISC_REG_GPIO_EVENT_EN, val); bnx2x_ext_phy_hw_reset(bp, 0); - msleep(5); + usleep_range(5000, 10000); for (port = 0; port < PORT_MAX; port++) { u32 shmem_base, shmem2_base; @@ -12658,11 +12652,11 @@ static int bnx2x_8727_common_init_phy(struct bnx2x *bp, /* Initiate PHY reset*/ bnx2x_set_gpio(bp, reset_gpio, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); - msleep(1); + usleep_range(1000, 2000); bnx2x_set_gpio(bp, reset_gpio, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); - msleep(5); + usleep_range(5000, 10000); /* PART1 - Reset both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { @@ -12756,7 +12750,7 @@ static int bnx2x_84833_pre_init_phy(struct bnx2x *bp, MDIO_PMA_REG_CTRL, &val); if (!(val & (1<<15))) break; - msleep(1); + usleep_range(1000, 2000); } if (cnt >= 1500) { DP(NETIF_MSG_LINK, "84833 reset timeout\n"); @@ -12846,7 +12840,7 @@ static int bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[], break; } - if (rc != 0) + if (rc) netdev_err(bp->dev, "Warning: PHY was not initialized," " Port %d\n", 0); -- cgit v1.2.3 From 1440090111ad626c8ab3d3c10076254ab7d98777 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 20 Jun 2012 19:05:20 +0000 Subject: bnx2x: treat 0 speed as link down (copper) Signed-off-by: Yuval Mintz Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index b3c33ed2bc96..d38d269824fc 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -10178,17 +10178,19 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Legacy speed status = 0x%x\n", legacy_status); link_up = ((legacy_status & (1<<11)) == (1<<11)); - if (link_up) { - legacy_speed = (legacy_status & (3<<9)); - if (legacy_speed == (0<<9)) - vars->line_speed = SPEED_10; - else if (legacy_speed == (1<<9)) - vars->line_speed = SPEED_100; - else if (legacy_speed == (2<<9)) - vars->line_speed = SPEED_1000; - else /* Should not happen */ - vars->line_speed = 0; + legacy_speed = (legacy_status & (3<<9)); + if (legacy_speed == (0<<9)) + vars->line_speed = SPEED_10; + else if (legacy_speed == (1<<9)) + vars->line_speed = SPEED_100; + else if (legacy_speed == (2<<9)) + vars->line_speed = SPEED_1000; + else { /* Should not happen: Treat as link down */ + vars->line_speed = 0; + link_up = 0; + } + if (link_up) { if (legacy_status & (1<<8)) vars->duplex = DUPLEX_FULL; else -- cgit v1.2.3 From a351d497f3367461fc96bf4cb9749bdb163c897e Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 20 Jun 2012 19:05:21 +0000 Subject: bnx2x: revised link register access This is a semantic change, cleaning some sections in which the bnx2x handles the phy's registers. Signed-off-by: Yuval Mintz Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 238 +++++++++-------------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h | 5 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h | 1 + 3 files changed, 100 insertions(+), 144 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index d38d269824fc..23ca0b642a8e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -3754,44 +3754,35 @@ static u8 bnx2x_ext_phy_resolve_fc(struct bnx2x_phy *phy, static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) { - u16 val16 = 0, lane, bam37 = 0; - struct bnx2x *bp = params->bp; + u16 val16 = 0, lane, i; + struct bnx2x *bp = params->bp; + static struct bnx2x_reg_set reg_set[] = { + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7}, + {MDIO_AN_DEVAD, MDIO_WC_REG_PAR_DET_10G_CTRL, 0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, 0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_XGXSBLK1_LANECTRL0, 0xff}, + {MDIO_WC_DEVAD, MDIO_WC_REG_XGXSBLK1_LANECTRL1, 0x5555}, + {MDIO_PMA_DEVAD, MDIO_WC_REG_IEEE0BLK_AUTONEGNP, 0x0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_RX66_CONTROL, 0x7415}, + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_MISC2, 0x6190}, + /* Disable Autoneg: re-enable it after adv is done. */ + {MDIO_AN_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0} + }; DP(NETIF_MSG_LINK, "Enable Auto Negotiation for KR\n"); /* Set to default registers that may be overriden by 10G force */ - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7); - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_PAR_DET_10G_CTRL, 0); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, 0); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL0, 0xff); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL1, 0x5555); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, - MDIO_WC_REG_IEEE0BLK_AUTONEGNP, 0x0); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_RX66_CONTROL, 0x7415); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_MISC2, 0x6190); - /* Disable Autoneg: re-enable it after adv is done. */ - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, 0); + for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); i++) + bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg, + reg_set[i].val); /* Check adding advertisement for 1G KX */ if (((vars->line_speed == SPEED_AUTO_NEG) && (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) || (vars->line_speed == SPEED_1000)) { - u16 sd_digital; + u32 addr = MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2; val16 |= (1<<5); /* Enable CL37 1G Parallel Detect */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, &sd_digital); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, - (sd_digital | 0x1)); - + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, addr, 0x1); DP(NETIF_MSG_LINK, "Advertize 1G\n"); } if (((vars->line_speed == SPEED_AUTO_NEG) && @@ -3801,7 +3792,7 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, val16 |= (1<<7); /* Enable 10G Parallel Detect */ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_PAR_DET_10G_CTRL, 1); + MDIO_WC_REG_PAR_DET_10G_CTRL, 1); DP(NETIF_MSG_LINK, "Advertize 10G\n"); } @@ -3835,10 +3826,9 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, offsetof(struct shmem_region, dev_info. port_hw_config[params->port].default_cfg)) & PORT_HW_CFG_ENABLE_BAM_ON_KR_ENABLED) { - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL, &bam37); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL, bam37 | 1); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL, + 1); DP(NETIF_MSG_LINK, "Enable CL37 BAM on KR\n"); } @@ -3852,11 +3842,8 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Enable AN KR work-around\n"); vars->rx_tx_asic_rst = MAX_KR_LINK_RETRY; } - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL5_MISC7, &val16); - - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL5_MISC7, val16 | 0x100); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL5_MISC7, 0x100); /* Over 1G - AN local device user page 1 */ bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, @@ -3873,50 +3860,35 @@ static void bnx2x_warpcore_set_10G_KR(struct bnx2x_phy *phy, struct link_vars *vars) { struct bnx2x *bp = params->bp; - u16 val; - - /* Disable Autoneg */ - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7); - - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_PAR_DET_10G_CTRL, 0); - - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, 0x3f00); - - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT1, 0); - - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x0); - - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL3_UP1, 0x1); - - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL5_MISC7, 0xa); - - /* Disable CL36 PCS Tx */ - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL0, 0x0); - - /* Double Wide Single Data Rate @ pll rate */ - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL1, 0xFFFF); - - /* Leave cl72 training enable, needed for KR */ - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, + u16 i; + static struct bnx2x_reg_set reg_set[] = { + /* Disable Autoneg */ + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x7}, + {MDIO_AN_DEVAD, MDIO_WC_REG_PAR_DET_10G_CTRL, 0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, + 0x3f00}, + {MDIO_AN_DEVAD, MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT1, 0}, + {MDIO_AN_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL3_UP1, 0x1}, + {MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL5_MISC7, 0xa}, + /* Disable CL36 PCS Tx */ + {MDIO_WC_DEVAD, MDIO_WC_REG_XGXSBLK1_LANECTRL0, 0x0}, + /* Double Wide Single Data Rate @ pll rate */ + {MDIO_WC_DEVAD, MDIO_WC_REG_XGXSBLK1_LANECTRL1, 0xFFFF}, + /* Leave cl72 training enable, needed for KR */ + {MDIO_PMA_DEVAD, MDIO_WC_REG_PMD_IEEE9BLK_TENGBASE_KR_PMD_CONTROL_REGISTER_150, - 0x2); + 0x2} + }; + + for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); i++) + bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg, + reg_set[i].val); /* Leave CL72 enabled */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, - &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, - val | 0x3800); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, + 0x3800); /* Set speed via PMA/PMD register */ bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, @@ -3952,16 +3924,12 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u16 misc1_val, tap_val, tx_driver_val, lane, val; /* Hold rxSeqStart */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, (val | 0x8000)); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, 0x8000); /* Hold tx_fifo_reset */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, (val | 0x1)); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, 0x1); /* Disable CL73 AN */ bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0); @@ -3973,10 +3941,8 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, MDIO_WC_REG_FX100_CTRL1, (val & 0xFFFA)); /* Disable 100FX Idle detect */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_FX100_CTRL3, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_FX100_CTRL3, (val | 0x0080)); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_FX100_CTRL3, 0x0080); /* Set Block address to Remote PHY & Clear forced_speed[5] */ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, @@ -4037,16 +4003,12 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, tx_driver_val); /* Enable fiber mode, enable and invert sig_det */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, val | 0xd); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, 0xd); /* Set Block address to Remote PHY & Set forced_speed[5], 40bit mode */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL4_MISC3, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL4_MISC3, val | 0x8080); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL4_MISC3, 0x8080); /* Enable LPI pass through */ DP(NETIF_MSG_LINK, "Configure WC for LPI pass through\n"); @@ -4244,40 +4206,35 @@ static void bnx2x_warpcore_clear_regs(struct bnx2x_phy *phy, u16 lane) { struct bnx2x *bp = params->bp; - u16 val16; - + u16 i; + static struct bnx2x_reg_set wc_regs[] = { + {MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_FX100_CTRL1, 0x014a}, + {MDIO_WC_DEVAD, MDIO_WC_REG_FX100_CTRL3, 0x0800}, + {MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL4_MISC3, 0x8008}, + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, + 0x0195}, + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, + 0x0007}, + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, + 0x0002}, + {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_MISC1, 0x6000}, + {MDIO_WC_DEVAD, MDIO_WC_REG_TX_FIR_TAP, 0x0000}, + {MDIO_WC_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x2040}, + {MDIO_WC_DEVAD, MDIO_WC_REG_COMBO_IEEE0_MIICTRL, 0x0140} + }; /* Set XFI clock comp as default. */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_RX66_CONTROL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_RX66_CONTROL, val16 | (3<<13)); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_RX66_CONTROL, (3<<13)); + + for (i = 0; i < sizeof(wc_regs)/sizeof(struct bnx2x_reg_set); i++) + bnx2x_cl45_write(bp, phy, wc_regs[i].devad, wc_regs[i].reg, + wc_regs[i].val); - bnx2x_warpcore_reset_lane(bp, phy, 1); - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_FX100_CTRL1, 0x014a); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_FX100_CTRL3, 0x0800); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL4_MISC3, 0x8008); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, 0x0195); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X2, 0x0007); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, 0x0002); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_MISC1, 0x6000); lane = bnx2x_get_warpcore_lane(phy, params); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_TX_FIR_TAP, 0x0000); bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_TX0_TX_DRIVER + 0x10*lane, 0x0990); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x2040); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, 0x0140); - bnx2x_warpcore_reset_lane(bp, phy, 0); + } static int bnx2x_get_mod_abs_int_cfg(struct bnx2x *bp, @@ -4605,12 +4562,9 @@ static void bnx2x_set_warpcore_loopback(struct bnx2x_phy *phy, CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, MDIO_AER_BLOCK_AER_REG, 0); /* Enable 1G MDIO (1-copy) */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, - &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, - val16 | 0x10); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, + 0x10); /* Set 1G loopback based on lane (1-copy) */ lane = bnx2x_get_warpcore_lane(phy, params); bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, @@ -4623,16 +4577,12 @@ static void bnx2x_set_warpcore_loopback(struct bnx2x_phy *phy, bnx2x_set_aer_mmd(params, phy); } else { /* 10G & 20G */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, val16 | - 0x4000); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_COMBO_IEEE0_MIICTRL, + 0x4000); - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, val16 | 0x1); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1); } } @@ -10819,7 +10769,7 @@ static u8 bnx2x_54618se_read_status(struct bnx2x_phy *phy, /* Get speed operation status */ bnx2x_cl22_read(bp, phy, - 0x19, + MDIO_REG_GPHY_AUX_STATUS, &legacy_status); DP(NETIF_MSG_LINK, "54618SE read_status: 0x%x\n", legacy_status); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index cd1f2719b62d..7b6051bc4551 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -125,6 +125,11 @@ typedef void (*set_link_led_t)(struct bnx2x_phy *phy, struct link_params *params, u8 mode); typedef void (*phy_specific_func_t)(struct bnx2x_phy *phy, struct link_params *params, u32 action); +struct bnx2x_reg_set { + u8 devad; + u16 reg; + u16 val; +}; struct bnx2x_phy { u32 type; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index a78e35683b03..f371e3c06094 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -7160,6 +7160,7 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_REG_GPHY_EEE_1G (0x1 << 2) #define MDIO_REG_GPHY_EEE_100 (0x1 << 1) #define MDIO_REG_GPHY_EEE_RESOLVED 0x803e +#define MDIO_REG_GPHY_AUX_STATUS 0x19 #define MDIO_REG_INTR_STATUS 0x1a #define MDIO_REG_INTR_MASK 0x1b #define MDIO_REG_INTR_MASK_LINK_STATUS (0x1 << 1) -- cgit v1.2.3 From dbef807ee890b45f9c9125b665c0dddc993c3d15 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 20 Jun 2012 19:05:22 +0000 Subject: bnx2x: 1G sfp+ module handling Automatically lower requested link speed to 1G in case 1G SFP+ module is detected. Signed-off-by: Yuval Mintz Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 16 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 202 +++++++++++++-------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h | 17 +- 3 files changed, 151 insertions(+), 84 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 70c0881ce5a0..819170ee152a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -187,7 +187,8 @@ static int bnx2x_get_port_type(struct bnx2x *bp) int port_type; u32 phy_idx = bnx2x_get_cur_phy_idx(bp); switch (bp->link_params.phy[phy_idx].media_type) { - case ETH_PHY_SFP_FIBER: + case ETH_PHY_SFPP_10G_FIBER: + case ETH_PHY_SFP_1G_FIBER: case ETH_PHY_XFP_FIBER: case ETH_PHY_KR: case ETH_PHY_CX4: @@ -220,6 +221,11 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) (bp->port.supported[cfg_idx ^ 1] & (SUPPORTED_TP | SUPPORTED_FIBRE)); cmd->advertising = bp->port.advertising[cfg_idx]; + if (bp->link_params.phy[bnx2x_get_cur_phy_idx(bp)].media_type == + ETH_PHY_SFP_1G_FIBER) { + cmd->supported &= ~(SUPPORTED_10000baseT_Full); + cmd->advertising &= ~(ADVERTISED_10000baseT_Full); + } if ((bp->state == BNX2X_STATE_OPEN) && (bp->link_vars.link_up)) { if (!(bp->flags & MF_FUNC_DIS)) { @@ -295,7 +301,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct bnx2x *bp = netdev_priv(dev); u32 advertising, cfg_idx, old_multi_phy_config, new_multi_phy_config; - u32 speed; + u32 speed, phy_idx; if (IS_MF_SD(bp)) return 0; @@ -550,9 +556,11 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) "10G half not supported\n"); return -EINVAL; } - + phy_idx = bnx2x_get_cur_phy_idx(bp); if (!(bp->port.supported[cfg_idx] - & SUPPORTED_10000baseT_Full)) { + & SUPPORTED_10000baseT_Full) || + (bp->link_params.phy[phy_idx].media_type == + ETH_PHY_SFP_1G_FIBER)) { DP(BNX2X_MSG_ETHTOOL, "10G full not supported\n"); return -EINVAL; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 23ca0b642a8e..b21a6be81032 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -4373,6 +4373,23 @@ static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy, } /*params->rx_tx_asic_rst*/ } +static void bnx2x_warpcore_config_sfi(struct bnx2x_phy *phy, + struct link_params *params) +{ + u16 lane = bnx2x_get_warpcore_lane(phy, params); + struct bnx2x *bp = params->bp; + bnx2x_warpcore_clear_regs(phy, params, lane); + if ((params->req_line_speed[LINK_CONFIG_IDX(INT_PHY)] == + SPEED_10000) && + (phy->media_type != ETH_PHY_SFP_1G_FIBER)) { + DP(NETIF_MSG_LINK, "Setting 10G SFI\n"); + bnx2x_warpcore_set_10G_XFI(phy, params, 0); + } else { + DP(NETIF_MSG_LINK, "Setting 1G Fiber\n"); + bnx2x_warpcore_set_sgmii_speed(phy, params, 1, 0); + } +} + static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) @@ -4433,19 +4450,11 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, break; case PORT_HW_CFG_NET_SERDES_IF_SFI: - - bnx2x_warpcore_clear_regs(phy, params, lane); - if (vars->line_speed == SPEED_10000) { - DP(NETIF_MSG_LINK, "Setting 10G SFI\n"); - bnx2x_warpcore_set_10G_XFI(phy, params, 0); - } else if (vars->line_speed == SPEED_1000) { - DP(NETIF_MSG_LINK, "Setting 1G Fiber\n"); - bnx2x_warpcore_set_sgmii_speed( - phy, params, 1, 0); - } /* Issue Module detection */ if (bnx2x_is_sfp_module_plugged(phy, params)) bnx2x_sfp_module_detection(phy, params); + + bnx2x_warpcore_config_sfi(phy, params); break; case PORT_HW_CFG_NET_SERDES_IF_DXGXS: @@ -6354,7 +6363,9 @@ int bnx2x_test_link(struct link_params *params, struct link_vars *vars, for (phy_index = EXT_PHY1; phy_index < params->num_phys; phy_index++) { serdes_phy_type = ((params->phy[phy_index].media_type == - ETH_PHY_SFP_FIBER) || + ETH_PHY_SFPP_10G_FIBER) || + (params->phy[phy_index].media_type == + ETH_PHY_SFP_1G_FIBER) || (params->phy[phy_index].media_type == ETH_PHY_XFP_FIBER) || (params->phy[phy_index].media_type == @@ -7891,7 +7902,7 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy, { struct bnx2x *bp = params->bp; u32 sync_offset = 0, phy_idx, media_types; - u8 val, check_limiting_mode = 0; + u8 val[2], check_limiting_mode = 0; *edc_mode = EDC_MODE_LIMITING; phy->media_type = ETH_PHY_UNSPECIFIED; @@ -7899,13 +7910,13 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy, if (bnx2x_read_sfp_module_eeprom(phy, params, SFP_EEPROM_CON_TYPE_ADDR, - 1, - &val) != 0) { + 2, + (u8 *)val) != 0) { DP(NETIF_MSG_LINK, "Failed to read from SFP+ module EEPROM\n"); return -EINVAL; } - switch (val) { + switch (val[0]) { case SFP_EEPROM_CON_TYPE_VAL_COPPER: { u8 copper_module_type; @@ -7943,13 +7954,29 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy, break; } case SFP_EEPROM_CON_TYPE_VAL_LC: - phy->media_type = ETH_PHY_SFP_FIBER; - DP(NETIF_MSG_LINK, "Optic module detected\n"); check_limiting_mode = 1; + if ((val[1] & (SFP_EEPROM_COMP_CODE_SR_MASK | + SFP_EEPROM_COMP_CODE_LR_MASK | + SFP_EEPROM_COMP_CODE_LRM_MASK)) == 0) { + DP(NETIF_MSG_LINK, "1G Optic module detected\n"); + phy->media_type = ETH_PHY_SFP_1G_FIBER; + phy->req_line_speed = SPEED_1000; + } else { + int idx, cfg_idx = 0; + DP(NETIF_MSG_LINK, "10G Optic module detected\n"); + for (idx = INT_PHY; idx < MAX_PHYS; idx++) { + if (params->phy[idx].type == phy->type) { + cfg_idx = LINK_CONFIG_IDX(idx); + break; + } + } + phy->media_type = ETH_PHY_SFPP_10G_FIBER; + phy->req_line_speed = params->req_line_speed[cfg_idx]; + } break; default: DP(NETIF_MSG_LINK, "Unable to determine module type 0x%x !!!\n", - val); + val[0]); return -EINVAL; } sync_offset = params->shmem_base + @@ -8479,14 +8506,34 @@ void bnx2x_handle_module_detect_int(struct link_params *params) /* Call the handling function in case module is detected */ if (gpio_val == 0) { + bnx2x_set_mdio_clk(bp, params->chip_id, params->port); + bnx2x_set_aer_mmd(params, phy); + bnx2x_power_sfp_module(params, phy, 1); bnx2x_set_gpio_int(bp, gpio_num, MISC_REGISTERS_GPIO_INT_OUTPUT_CLR, gpio_port); - if (bnx2x_wait_for_sfp_module_initialized(phy, params) == 0) + if (bnx2x_wait_for_sfp_module_initialized(phy, params) == 0) { bnx2x_sfp_module_detection(phy, params); - else + if (CHIP_IS_E3(bp)) { + u16 rx_tx_in_reset; + /* In case WC is out of reset, reconfigure the + * link speed while taking into account 1G + * module limitation. + */ + bnx2x_cl45_read(bp, phy, + MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL5_MISC6, + &rx_tx_in_reset); + if (!rx_tx_in_reset) { + bnx2x_warpcore_reset_lane(bp, phy, 1); + bnx2x_warpcore_config_sfi(phy, params); + bnx2x_warpcore_reset_lane(bp, phy, 0); + } + } + } else { DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); + } } else { u32 val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. @@ -8938,6 +8985,63 @@ static void bnx2x_8727_hw_reset(struct bnx2x_phy *phy, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); } +static void bnx2x_8727_config_speed(struct bnx2x_phy *phy, + struct link_params *params) +{ + struct bnx2x *bp = params->bp; + u16 tmp1, val; + /* Set option 1G speed */ + if ((phy->req_line_speed == SPEED_1000) || + (phy->media_type == ETH_PHY_SFP_1G_FIBER)) { + DP(NETIF_MSG_LINK, "Setting 1G force\n"); + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x40); + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0xD); + bnx2x_cl45_read(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, &tmp1); + DP(NETIF_MSG_LINK, "1.7 = 0x%x\n", tmp1); + /* Power down the XAUI until link is up in case of dual-media + * and 1G + */ + if (DUAL_MEDIA(params)) { + bnx2x_cl45_read(bp, phy, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_8727_PCS_GP, &val); + val |= (3<<10); + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, + MDIO_PMA_REG_8727_PCS_GP, val); + } + } else if ((phy->req_line_speed == SPEED_AUTO_NEG) && + ((phy->speed_cap_mask & + PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) && + ((phy->speed_cap_mask & + PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) != + PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) { + + DP(NETIF_MSG_LINK, "Setting 1G clause37\n"); + bnx2x_cl45_write(bp, phy, + MDIO_AN_DEVAD, MDIO_AN_REG_8727_MISC_CTRL, 0); + bnx2x_cl45_write(bp, phy, + MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1300); + } else { + /* Since the 8727 has only single reset pin, need to set the 10G + * registers although it is default + */ + bnx2x_cl45_write(bp, phy, + MDIO_AN_DEVAD, MDIO_AN_REG_8727_MISC_CTRL, + 0x0020); + bnx2x_cl45_write(bp, phy, + MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x0100); + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x2040); + bnx2x_cl45_write(bp, phy, + MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, + 0x0008); + } +} + static int bnx2x_8727_config_init(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) @@ -9007,56 +9111,7 @@ static int bnx2x_8727_config_init(struct bnx2x_phy *phy, bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT, &tmp1); - /* Set option 1G speed */ - if (phy->req_line_speed == SPEED_1000) { - DP(NETIF_MSG_LINK, "Setting 1G force\n"); - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x40); - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0xD); - bnx2x_cl45_read(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, &tmp1); - DP(NETIF_MSG_LINK, "1.7 = 0x%x\n", tmp1); - /* Power down the XAUI until link is up in case of dual-media - * and 1G - */ - if (DUAL_MEDIA(params)) { - bnx2x_cl45_read(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8727_PCS_GP, &val); - val |= (3<<10); - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8727_PCS_GP, val); - } - } else if ((phy->req_line_speed == SPEED_AUTO_NEG) && - ((phy->speed_cap_mask & - PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) && - ((phy->speed_cap_mask & - PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) != - PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) { - - DP(NETIF_MSG_LINK, "Setting 1G clause37\n"); - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, MDIO_AN_REG_8727_MISC_CTRL, 0); - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1300); - } else { - /* Since the 8727 has only single reset pin, need to set the 10G - * registers although it is default - */ - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, MDIO_AN_REG_8727_MISC_CTRL, - 0x0020); - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x0100); - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x2040); - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, - 0x0008); - } - + bnx2x_8727_config_speed(phy, params); /* Set 2-wire transfer rate of SFP+ module EEPROM * to 100Khz since some DACs(direct attached cables) do * not work at 400Khz. @@ -9183,6 +9238,9 @@ static void bnx2x_8727_handle_mod_abs(struct bnx2x_phy *phy, bnx2x_sfp_module_detection(phy, params); else DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); + + /* Reconfigure link speed based on module type limitations */ + bnx2x_8727_config_speed(phy, params); } DP(NETIF_MSG_LINK, "8727 RX_ALARM_STATUS 0x%x\n", @@ -11327,7 +11385,7 @@ static struct bnx2x_phy phy_8706 = { SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .media_type = ETH_PHY_SFP_FIBER, + .media_type = ETH_PHY_SFPP_10G_FIBER, .ver_addr = 0, .req_flow_ctrl = 0, .req_line_speed = 0, @@ -11666,7 +11724,7 @@ static int bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port, SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); - phy->media_type = ETH_PHY_SFP_FIBER; + phy->media_type = ETH_PHY_SFPP_10G_FIBER; break; case PORT_HW_CFG_NET_SERDES_IF_KR: phy->media_type = ETH_PHY_KR; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index 7b6051bc4551..017236bee323 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -168,14 +168,15 @@ struct bnx2x_phy { u32 supported; u32 media_type; -#define ETH_PHY_UNSPECIFIED 0x0 -#define ETH_PHY_SFP_FIBER 0x1 -#define ETH_PHY_XFP_FIBER 0x2 -#define ETH_PHY_DA_TWINAX 0x3 -#define ETH_PHY_BASE_T 0x4 -#define ETH_PHY_KR 0xf0 -#define ETH_PHY_CX4 0xf1 -#define ETH_PHY_NOT_PRESENT 0xff +#define ETH_PHY_UNSPECIFIED 0x0 +#define ETH_PHY_SFPP_10G_FIBER 0x1 +#define ETH_PHY_XFP_FIBER 0x2 +#define ETH_PHY_DA_TWINAX 0x3 +#define ETH_PHY_BASE_T 0x4 +#define ETH_PHY_SFP_1G_FIBER 0x5 +#define ETH_PHY_KR 0xf0 +#define ETH_PHY_CX4 0xf1 +#define ETH_PHY_NOT_PRESENT 0xff /* The address in which version is located*/ u32 ver_addr; -- cgit v1.2.3 From 24ea818e305b92ad1fadcca015ae3b0c1222c497 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Wed, 20 Jun 2012 19:05:23 +0000 Subject: bnx2x: link module eeprom Add the ethtool functionality of accessing optic modules' information and eeprom to the bnx2x driver. Signed-off-by: Yuval Mintz Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- .../net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 61 ++++++++++++++++++++++ drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c | 9 ++-- drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h | 1 + 3 files changed, 67 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 819170ee152a..1f8c1561cdec 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1160,6 +1160,65 @@ static int bnx2x_get_eeprom(struct net_device *dev, return rc; } +static int bnx2x_get_module_eeprom(struct net_device *dev, + struct ethtool_eeprom *ee, + u8 *data) +{ + struct bnx2x *bp = netdev_priv(dev); + int rc = 0, phy_idx; + u8 *user_data = data; + int remaining_len = ee->len, xfer_size; + unsigned int page_off = ee->offset; + + if (!netif_running(dev)) { + DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, + "cannot access eeprom when the interface is down\n"); + return -EAGAIN; + } + + phy_idx = bnx2x_get_cur_phy_idx(bp); + bnx2x_acquire_phy_lock(bp); + while (!rc && remaining_len > 0) { + xfer_size = (remaining_len > SFP_EEPROM_PAGE_SIZE) ? + SFP_EEPROM_PAGE_SIZE : remaining_len; + rc = bnx2x_read_sfp_module_eeprom(&bp->link_params.phy[phy_idx], + &bp->link_params, + page_off, + xfer_size, + user_data); + remaining_len -= xfer_size; + user_data += xfer_size; + page_off += xfer_size; + } + + bnx2x_release_phy_lock(bp); + return rc; +} + +static int bnx2x_get_module_info(struct net_device *dev, + struct ethtool_modinfo *modinfo) +{ + struct bnx2x *bp = netdev_priv(dev); + int phy_idx; + if (!netif_running(dev)) { + DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, + "cannot access eeprom when the interface is down\n"); + return -EAGAIN; + } + + phy_idx = bnx2x_get_cur_phy_idx(bp); + switch (bp->link_params.phy[phy_idx].media_type) { + case ETH_PHY_SFPP_10G_FIBER: + case ETH_PHY_SFP_1G_FIBER: + case ETH_PHY_DA_TWINAX: + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + return 0; + default: + return -EOPNOTSUPP; + } +} + static int bnx2x_nvram_write_dword(struct bnx2x *bp, u32 offset, u32 val, u32 cmd_flags) { @@ -2915,6 +2974,8 @@ static const struct ethtool_ops bnx2x_ethtool_ops = { .set_rxfh_indir = bnx2x_set_rxfh_indir, .get_channels = bnx2x_get_channels, .set_channels = bnx2x_set_channels, + .get_module_info = bnx2x_get_module_info, + .get_module_eeprom = bnx2x_get_module_eeprom, .get_eee = bnx2x_get_eee, .set_eee = bnx2x_set_eee, }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index b21a6be81032..3e662bf7e5d8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -7695,7 +7695,7 @@ static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u16 val = 0; u16 i; - if (byte_cnt > 16) { + if (byte_cnt > SFP_EEPROM_PAGE_SIZE) { DP(NETIF_MSG_LINK, "Reading from eeprom is limited to 0xf\n"); return -EINVAL; @@ -7764,7 +7764,8 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy, u32 data_array[4]; u16 addr32; struct bnx2x *bp = params->bp; - if (byte_cnt > 16) { + + if (byte_cnt > SFP_EEPROM_PAGE_SIZE) { DP(NETIF_MSG_LINK, "Reading from eeprom is limited to 16 bytes\n"); return -EINVAL; @@ -7794,7 +7795,7 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u16 val, i; - if (byte_cnt > 16) { + if (byte_cnt > SFP_EEPROM_PAGE_SIZE) { DP(NETIF_MSG_LINK, "Reading from eeprom is limited to 0xf\n"); return -EINVAL; @@ -7877,7 +7878,7 @@ int bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy, struct link_params *params, u16 addr, u8 byte_cnt, u8 *o_buf) { - int rc = -EINVAL; + int rc = -EOPNOTSUPP; switch (phy->type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: rc = bnx2x_8726_read_sfp_module_eeprom(phy, params, addr, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index 017236bee323..c05f9d94938f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -41,6 +41,7 @@ #define SPEED_AUTO_NEG 0 #define SPEED_20000 20000 +#define SFP_EEPROM_PAGE_SIZE 16 #define SFP_EEPROM_VENDOR_NAME_ADDR 0x14 #define SFP_EEPROM_VENDOR_NAME_SIZE 16 #define SFP_EEPROM_VENDOR_OUI_ADDR 0x25 -- cgit v1.2.3 From 1d23396d9df0a9543b2ba5c288f4914ad1f19e46 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 21 Jun 2012 18:10:48 +0300 Subject: wlcore: don't allow SDIO read/writes after failure Set a flag and after the first read/write failure is encountered. This flag will disallow further SDIO read/writes until op_stop() is executed, which will clear all flags. This prevents further errors from occurring, since one error usually indicates that IO operations won't work anymore until the chip is rebooted. By blocking more calls, we avoid extra timeouts and having to wait for them to occur. [Added second paragraph explaining why the change is needed. -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/io.h | 22 ++++++++++++++++++++-- drivers/net/wireless/ti/wlcore/wlcore_i.h | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index 1cd545b0ed1e..fef80adc8bf5 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -57,14 +57,32 @@ static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { - return wl->if_ops->write(wl->dev, addr, buf, len, fixed); + int ret; + + if (test_bit(WL1271_FLAG_SDIO_FAILED, &wl->flags)) + return -EIO; + + ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed); + if (ret) + set_bit(WL1271_FLAG_SDIO_FAILED, &wl->flags); + + return ret; } static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { - return wl->if_ops->read(wl->dev, addr, buf, len, fixed); + int ret; + + if (test_bit(WL1271_FLAG_SDIO_FAILED, &wl->flags)) + return -EIO; + + ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed); + if (ret) + set_bit(WL1271_FLAG_SDIO_FAILED, &wl->flags); + + return ret; } static inline int __must_check wlcore_raw_read_data(struct wl1271 *wl, int reg, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index e5a34dd34baf..4273a21cdde1 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -247,6 +247,7 @@ enum wl12xx_flags { WL1271_FLAG_RECOVERY_IN_PROGRESS, WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, WL1271_FLAG_INTENDED_FW_RECOVERY, + WL1271_FLAG_SDIO_FAILED, }; enum wl12xx_vif_flags { -- cgit v1.2.3 From ea0a3cf95ed8839ce6f11bf9a050e6333bfc27d6 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 21 Jun 2012 18:10:49 +0300 Subject: wlcore: force recovery on resume if suspended without recovering If an error is detected after mac80211 is already suspended, the recovery work will not be queued. This will leave the driver in a bad state on resume. Detect this in the resume op and re-queue a recovery. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0df28d5cb331..0b51522d8eae 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1707,7 +1707,7 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif; unsigned long flags; - bool run_irq_work = false; + bool run_irq_work = false, pending_recovery; wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", wl->wow_enabled); @@ -1723,17 +1723,33 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) run_irq_work = true; spin_unlock_irqrestore(&wl->wl_lock, flags); + /* test the recovery flag before calling any SDIO functions */ + pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, + &wl->flags); + if (run_irq_work) { wl1271_debug(DEBUG_MAC80211, "run postponed irq_work directly"); - wl1271_irq(0, wl); + + /* don't talk to the HW if recovery is pending */ + if (!pending_recovery) + wl1271_irq(0, wl); + wlcore_enable_interrupts(wl); } mutex_lock(&wl->mutex); + if (pending_recovery) { + wl1271_warning("queuing forgotten recovery on resume"); + ieee80211_queue_work(wl->hw, &wl->recovery_work); + goto out; + } + wl12xx_for_each_wlvif(wl, wlvif) { wl1271_configure_resume(wl, wlvif); } + +out: wl->wow_enabled = false; mutex_unlock(&wl->mutex); -- cgit v1.2.3 From c439a1ca3bdc58febf51a388a9930eeba361b410 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 21 Jun 2012 18:10:50 +0300 Subject: wlcore: check Rx-filter functions in the suspend path Propagate some missing return values for Rx-filter related functions. This and makes sure we always fail the suspend in case of SDIO errors. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 27 +++++++++++++++++++++------ drivers/net/wireless/ti/wlcore/rx.c | 11 ++++++++--- drivers/net/wireless/ti/wlcore/rx.h | 2 +- 3 files changed, 30 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0b51522d8eae..e6e665440f81 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1513,8 +1513,15 @@ static int wl1271_configure_wowlan(struct wl1271 *wl, int i, ret; if (!wow || wow->any || !wow->n_patterns) { - wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL); - wl1271_rx_filter_clear_all(wl); + ret = wl1271_acx_default_rx_filter_enable(wl, 0, + FILTER_SIGNAL); + if (ret) + goto out; + + ret = wl1271_rx_filter_clear_all(wl); + if (ret) + goto out; + return 0; } @@ -1530,8 +1537,13 @@ static int wl1271_configure_wowlan(struct wl1271 *wl, } } - wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL); - wl1271_rx_filter_clear_all(wl); + ret = wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL); + if (ret) + goto out; + + ret = wl1271_rx_filter_clear_all(wl); + if (ret) + goto out; /* Translate WoWLAN patterns into filters */ for (i = 0; i < wow->n_patterns; i++) { @@ -1573,7 +1585,10 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, if (ret < 0) goto out; - wl1271_configure_wowlan(wl, wow); + ret = wl1271_configure_wowlan(wl, wow); + if (ret < 0) + goto out_sleep; + ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.suspend_wake_up_event, wl->conf.conn.suspend_listen_interval); @@ -1581,8 +1596,8 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, if (ret < 0) wl1271_error("suspend: set wake up conditions failed: %d", ret); +out_sleep: wl1271_ps_elp_sleep(wl); - out: return ret; diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index f42b969c1de9..f55e2f9e7ac5 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -318,14 +318,19 @@ int wl1271_rx_filter_enable(struct wl1271 *wl, return 0; } -void wl1271_rx_filter_clear_all(struct wl1271 *wl) +int wl1271_rx_filter_clear_all(struct wl1271 *wl) { - int i; + int i, ret = 0; for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) { if (!wl->rx_filter_enabled[i]) continue; - wl1271_rx_filter_enable(wl, i, 0, NULL); + ret = wl1271_rx_filter_enable(wl, i, 0, NULL); + if (ret) + goto out; } + +out: + return ret; } #endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 79f7839a06e2..71eba1899915 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -148,6 +148,6 @@ u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); int wl1271_rx_filter_enable(struct wl1271 *wl, int index, bool enable, struct wl12xx_rx_filter *filter); -void wl1271_rx_filter_clear_all(struct wl1271 *wl); +int wl1271_rx_filter_clear_all(struct wl1271 *wl); #endif -- cgit v1.2.3 From b5b45b3cbd56162d9612dd76529d7ad9f6be9a56 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 21 Jun 2012 18:10:51 +0300 Subject: wlcore: refactor threaded IRQ routine Separate the threaded IRQ handling routine into two functions. The outer function takes the mutex and calls recovery on errors. It also performs a Tx-path optimization to avoid redundant works. The inner function is simplified - all calls to recovery are removed and it assumes the lock is taken. The locked variant will be reused elsewhere. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 62 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index e6e665440f81..0c1e0751ecaa 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -494,20 +494,15 @@ static void wl1271_netstack_work(struct work_struct *work) #define WL1271_IRQ_MAX_LOOPS 256 -static irqreturn_t wl1271_irq(int irq, void *cookie) +static int wlcore_irq_locked(struct wl1271 *wl) { - int ret; + int ret = 0; u32 intr; int loopcount = WL1271_IRQ_MAX_LOOPS; - struct wl1271 *wl = (struct wl1271 *)cookie; bool done = false; unsigned int defer_count; unsigned long flags; - /* TX might be handled here, avoid redundant work */ - set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); - cancel_work_sync(&wl->tx_work); - /* * In case edge triggered interrupt must be used, we cannot iterate * more than once without introducing race conditions with the hardirq. @@ -515,8 +510,6 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) loopcount = 1; - mutex_lock(&wl->mutex); - wl1271_debug(DEBUG_IRQ, "IRQ work"); if (unlikely(wl->state == WL1271_STATE_OFF)) @@ -536,10 +529,8 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) smp_mb__after_clear_bit(); ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); - if (ret < 0) { - wl12xx_queue_recovery_work(wl); + if (ret < 0) goto out; - } wlcore_hw_tx_immediate_compl(wl); @@ -553,7 +544,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (unlikely(intr & WL1271_ACX_INTR_WATCHDOG)) { wl1271_error("HW watchdog interrupt received! starting recovery."); wl->watchdog_recovery = true; - wl12xx_queue_recovery_work(wl); + ret = -EIO; /* restarting the chip. ignore any other interrupt. */ goto out; @@ -563,7 +554,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) wl1271_error("SW watchdog interrupt received! " "starting recovery."); wl->watchdog_recovery = true; - wl12xx_queue_recovery_work(wl); + ret = -EIO; /* restarting the chip. ignore any other interrupt. */ goto out; @@ -573,10 +564,8 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); ret = wlcore_rx(wl, wl->fw_status_1); - if (ret < 0) { - wl12xx_queue_recovery_work(wl); + if (ret < 0) goto out; - } /* Check if any tx blocks were freed */ spin_lock_irqsave(&wl->wl_lock, flags); @@ -588,20 +577,16 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) * call the work function directly. */ ret = wlcore_tx_work_locked(wl); - if (ret < 0) { - wl12xx_queue_recovery_work(wl); + if (ret < 0) goto out; - } } else { spin_unlock_irqrestore(&wl->wl_lock, flags); } /* check for tx results */ ret = wlcore_hw_tx_delayed_compl(wl); - if (ret < 0) { - wl12xx_queue_recovery_work(wl); + if (ret < 0) goto out; - } /* Make sure the deferred queues don't get too long */ defer_count = skb_queue_len(&wl->deferred_tx_queue) + @@ -613,19 +598,15 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) if (intr & WL1271_ACX_INTR_EVENT_A) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_A"); ret = wl1271_event_handle(wl, 0); - if (ret < 0) { - wl12xx_queue_recovery_work(wl); + if (ret < 0) goto out; - } } if (intr & WL1271_ACX_INTR_EVENT_B) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT_B"); ret = wl1271_event_handle(wl, 1); - if (ret < 0) { - wl12xx_queue_recovery_work(wl); + if (ret < 0) goto out; - } } if (intr & WL1271_ACX_INTR_INIT_COMPLETE) @@ -639,6 +620,25 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) wl1271_ps_elp_sleep(wl); out: + return ret; +} + +static irqreturn_t wlcore_irq(int irq, void *cookie) +{ + int ret; + unsigned long flags; + struct wl1271 *wl = cookie; + + /* TX might be handled here, avoid redundant work */ + set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); + cancel_work_sync(&wl->tx_work); + + mutex_lock(&wl->mutex); + + ret = wlcore_irq_locked(wl); + if (ret) + wl12xx_queue_recovery_work(wl); + spin_lock_irqsave(&wl->wl_lock, flags); /* In case TX was not handled here, queue TX work */ clear_bit(WL1271_FLAG_TX_PENDING, &wl->flags); @@ -1748,7 +1748,7 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) /* don't talk to the HW if recovery is pending */ if (!pending_recovery) - wl1271_irq(0, wl); + wlcore_irq(0, wl); wlcore_enable_interrupts(wl); } @@ -5489,7 +5489,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) else irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; - ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wl1271_irq, + ret = request_threaded_irq(wl->irq, wl12xx_hardirq, wlcore_irq, irqflags, pdev->name, wl); if (ret < 0) { -- cgit v1.2.3 From 725b82775e7901dc92afaddfa45683934e75c33e Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 21 Jun 2012 18:10:52 +0300 Subject: wlcore: prevent recovery in the middle of resume Take the mutex early in the resume handler and use the locked version of the IRQ routine. This ensures any recoveries queued will only take place after resume has fully completed. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 0c1e0751ecaa..372ccf277b1e 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1723,6 +1723,7 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) struct wl12xx_vif *wlvif; unsigned long flags; bool run_irq_work = false, pending_recovery; + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", wl->wow_enabled); @@ -1738,6 +1739,8 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) run_irq_work = true; spin_unlock_irqrestore(&wl->wl_lock, flags); + mutex_lock(&wl->mutex); + /* test the recovery flag before calling any SDIO functions */ pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); @@ -1747,13 +1750,15 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) "run postponed irq_work directly"); /* don't talk to the HW if recovery is pending */ - if (!pending_recovery) - wlcore_irq(0, wl); + if (!pending_recovery) { + ret = wlcore_irq_locked(wl); + if (ret) + wl12xx_queue_recovery_work(wl); + } wlcore_enable_interrupts(wl); } - mutex_lock(&wl->mutex); if (pending_recovery) { wl1271_warning("queuing forgotten recovery on resume"); ieee80211_queue_work(wl->hw, &wl->recovery_work); -- cgit v1.2.3 From 49d57b5e588ce511bd826f5759aa2aebf322626b Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 22 Jun 2012 10:29:53 +0530 Subject: dmaengine: mmp_tdma: fix the arch dependency Reported-by: Stephen Rothwell Signed-off-by: Vinod Koul --- drivers/dma/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 90fec0e0e8f5..d45cf1bcbde5 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -276,7 +276,7 @@ config DMA_SA11X0 config MMP_TDMA bool "MMP Two-Channel DMA support" - default ARCH_MMP + depends on ARCH_MMP select DMA_ENGINE help Support the MMP Two-Channel DMA engine. -- cgit v1.2.3 From b19dbf711e8dae026f8d014eae90d766d02f4acb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 23 Jun 2012 11:34:20 +0100 Subject: regulator: core: Add export of regulator_set_voltage_time_sel() Signed-off-by: Mark Brown --- drivers/regulator/core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ce0a3462e0de..26b71048709b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2330,6 +2330,7 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, return 0; } +EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel); /** * regulator_sync_voltage - re-apply last regulator output voltage -- cgit v1.2.3 From b5152415225ba0d489939778f3b85217b25036db Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 14 Jun 2012 09:38:26 +0800 Subject: regulator: tps62360: Remove chip_id and voltage_base from struct tps62360_chip The chip_id is not used. The voltage_base is not necessary, set base voltage to tps->desc.min_uV instead. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps62360-regulator.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index d044a58640e7..bcea4e1eea1e 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -65,10 +65,8 @@ struct tps62360_chip { struct regulator_desc desc; struct regulator_dev *rdev; struct regmap *regmap; - int chip_id; int vsel0_gpio; int vsel1_gpio; - int voltage_base; u8 voltage_reg_mask; bool en_internal_pulldn; bool en_discharge; @@ -401,13 +399,13 @@ static int __devinit tps62360_probe(struct i2c_client *client, switch (chip_id) { case TPS62360: case TPS62362: - tps->voltage_base = TPS62360_BASE_VOLTAGE; + tps->desc.min_uV = TPS62360_BASE_VOLTAGE; tps->voltage_reg_mask = 0x3F; tps->desc.n_voltages = TPS62360_N_VOLTAGES; break; case TPS62361: case TPS62363: - tps->voltage_base = TPS62361_BASE_VOLTAGE; + tps->desc.min_uV = TPS62361_BASE_VOLTAGE; tps->voltage_reg_mask = 0x7F; tps->desc.n_voltages = TPS62361_N_VOLTAGES; break; @@ -420,7 +418,6 @@ static int __devinit tps62360_probe(struct i2c_client *client, tps->desc.ops = &tps62360_dcdc_ops; tps->desc.type = REGULATOR_VOLTAGE; tps->desc.owner = THIS_MODULE; - tps->desc.min_uV = tps->voltage_base; tps->desc.uV_step = 10000; tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config); -- cgit v1.2.3 From 9a0fbb627cfa86303b8eb57377f536c64812cc85 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 17 Jun 2012 09:33:07 +0800 Subject: regulator: max8998: Convert to regulator_list_voltage_linear() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max8998.c | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 18bb58b9b96e..105dafca3369 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -111,27 +111,6 @@ static const struct voltage_map_desc *ldo_voltage_map[] = { &buck4_voltage_map_desc, /* BUCK4 */ }; -static int max8998_list_voltage(struct regulator_dev *rdev, - unsigned int selector) -{ - const struct voltage_map_desc *desc; - int ldo = rdev_get_id(rdev); - int val; - - if (ldo >= ARRAY_SIZE(ldo_voltage_map)) - return -EINVAL; - - desc = ldo_voltage_map[ldo]; - if (desc == NULL) - return -EINVAL; - - val = desc->min + desc->step * selector; - if (val > desc->max) - return -EINVAL; - - return val * 1000; -} - static int max8998_get_enable_register(struct regulator_dev *rdev, int *reg, int *shift) { @@ -392,8 +371,8 @@ static int max8998_set_voltage_buck(struct regulator_dev *rdev, /* if previous_voltage equal new voltage, return */ if (previous_sel == i) { dev_dbg(max8998->dev, "No voltage change, old:%d, new:%d\n", - max8998_list_voltage(rdev, previous_sel), - max8998_list_voltage(rdev, i)); + regulator_list_voltage_linear(rdev, previous_sel), + regulator_list_voltage_linear(rdev, i)); return ret; } @@ -519,7 +498,7 @@ static int max8998_set_voltage_buck_time_sel(struct regulator_dev *rdev, } static struct regulator_ops max8998_ldo_ops = { - .list_voltage = max8998_list_voltage, + .list_voltage = regulator_list_voltage_linear, .is_enabled = max8998_ldo_is_enabled, .enable = max8998_ldo_enable, .disable = max8998_ldo_disable, @@ -530,7 +509,7 @@ static struct regulator_ops max8998_ldo_ops = { }; static struct regulator_ops max8998_buck_ops = { - .list_voltage = max8998_list_voltage, + .list_voltage = regulator_list_voltage_linear, .is_enabled = max8998_ldo_is_enabled, .enable = max8998_ldo_enable, .disable = max8998_ldo_disable, @@ -860,7 +839,10 @@ static __devinit int max8998_pmic_probe(struct platform_device *pdev) desc = ldo_voltage_map[id]; if (desc && regulators[index].ops != &max8998_others_ops) { int count = (desc->max - desc->min) / desc->step + 1; + regulators[index].n_voltages = count; + regulators[index].min_uV = desc->min * 1000; + regulators[index].uV_step = desc->step * 1000; } config.dev = max8998->dev; -- cgit v1.2.3 From baae019efe84b6d2c6d5e7e7e1c9cfa130ad6b2a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 17 Jun 2012 09:34:29 +0800 Subject: regulator: max8998: Convert to set_voltage_sel and regulator_map_voltage_linear Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/max8998.c | 97 +++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 105dafca3369..a1fa26613494 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -276,41 +276,18 @@ static int max8998_get_voltage_sel(struct regulator_dev *rdev) return val; } -static int max8998_set_voltage_ldo(struct regulator_dev *rdev, - int min_uV, int max_uV, unsigned *selector) +static int max8998_set_voltage_ldo_sel(struct regulator_dev *rdev, + unsigned selector) { struct max8998_data *max8998 = rdev_get_drvdata(rdev); struct i2c_client *i2c = max8998->iodev->i2c; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; - const struct voltage_map_desc *desc; - int ldo = rdev_get_id(rdev); - int reg, shift = 0, mask, ret, i; - - if (ldo >= ARRAY_SIZE(ldo_voltage_map)) - return -EINVAL; - - desc = ldo_voltage_map[ldo]; - if (desc == NULL) - return -EINVAL; - - if (max_vol < desc->min || min_vol > desc->max) - return -EINVAL; - - if (min_vol < desc->min) - min_vol = desc->min; - - i = DIV_ROUND_UP(min_vol - desc->min, desc->step); - - if (desc->min + desc->step*i > max_vol) - return -EINVAL; - - *selector = i; + int reg, shift = 0, mask, ret; ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); if (ret) return ret; - ret = max8998_update_reg(i2c, reg, i<iodev->dev); struct i2c_client *i2c = max8998->iodev->i2c; - int min_vol = min_uV / 1000, max_vol = max_uV / 1000; - const struct voltage_map_desc *desc; int buck = rdev_get_id(rdev); int reg, shift = 0, mask, ret; - int i, j, previous_sel; + int j, previous_sel; static u8 buck1_last_val; - if (buck >= ARRAY_SIZE(ldo_voltage_map)) - return -EINVAL; - - desc = ldo_voltage_map[buck]; - - if (desc == NULL) - return -EINVAL; - - if (max_vol < desc->min || min_vol > desc->max) - return -EINVAL; - - if (min_vol < desc->min) - min_vol = desc->min; - - i = DIV_ROUND_UP(min_vol - desc->min, desc->step); - - if (desc->min + desc->step*i > max_vol) - return -EINVAL; - - *selector = i; - ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); if (ret) return ret; @@ -369,19 +323,19 @@ static int max8998_set_voltage_buck(struct regulator_dev *rdev, /* Check if voltage needs to be changed */ /* if previous_voltage equal new voltage, return */ - if (previous_sel == i) { + if (previous_sel == selector) { dev_dbg(max8998->dev, "No voltage change, old:%d, new:%d\n", regulator_list_voltage_linear(rdev, previous_sel), - regulator_list_voltage_linear(rdev, i)); + regulator_list_voltage_linear(rdev, selector)); return ret; } switch (buck) { case MAX8998_BUCK1: dev_dbg(max8998->dev, - "BUCK1, i:%d, buck1_vol1:%d, buck1_vol2:%d\n" + "BUCK1, selector:%d, buck1_vol1:%d, buck1_vol2:%d\n" "buck1_vol3:%d, buck1_vol4:%d\n", - i, max8998->buck1_vol[0], max8998->buck1_vol[1], + selector, max8998->buck1_vol[0], max8998->buck1_vol[1], max8998->buck1_vol[2], max8998->buck1_vol[3]); if (gpio_is_valid(pdata->buck1_set1) && @@ -390,7 +344,7 @@ static int max8998_set_voltage_buck(struct regulator_dev *rdev, /* check if requested voltage */ /* value is already defined */ for (j = 0; j < ARRAY_SIZE(max8998->buck1_vol); j++) { - if (max8998->buck1_vol[j] == i) { + if (max8998->buck1_vol[j] == selector) { max8998->buck1_idx = j; buck1_gpio_set(pdata->buck1_set1, pdata->buck1_set2, j); @@ -405,11 +359,11 @@ static int max8998_set_voltage_buck(struct regulator_dev *rdev, max8998->buck1_idx = (buck1_last_val % 2) + 2; dev_dbg(max8998->dev, "max8998->buck1_idx:%d\n", max8998->buck1_idx); - max8998->buck1_vol[max8998->buck1_idx] = i; + max8998->buck1_vol[max8998->buck1_idx] = selector; ret = max8998_get_voltage_register(rdev, ®, &shift, &mask); - ret = max8998_write_reg(i2c, reg, i); + ret = max8998_write_reg(i2c, reg, selector); buck1_gpio_set(pdata->buck1_set1, pdata->buck1_set2, max8998->buck1_idx); buck1_last_val++; @@ -419,20 +373,20 @@ buck1_exit: gpio_get_value(pdata->buck1_set2)); break; } else { - ret = max8998_write_reg(i2c, reg, i); + ret = max8998_write_reg(i2c, reg, selector); } break; case MAX8998_BUCK2: dev_dbg(max8998->dev, - "BUCK2, i:%d buck2_vol1:%d, buck2_vol2:%d\n" - , i, max8998->buck2_vol[0], max8998->buck2_vol[1]); + "BUCK2, selector:%d buck2_vol1:%d, buck2_vol2:%d\n", + selector, max8998->buck2_vol[0], max8998->buck2_vol[1]); if (gpio_is_valid(pdata->buck2_set3)) { /* check if requested voltage */ /* value is already defined */ for (j = 0; j < ARRAY_SIZE(max8998->buck2_vol); j++) { - if (max8998->buck2_vol[j] == i) { + if (max8998->buck2_vol[j] == selector) { max8998->buck2_idx = j; buck2_gpio_set(pdata->buck2_set3, j); goto buck2_exit; @@ -444,20 +398,21 @@ buck1_exit: max8998_get_voltage_register(rdev, ®, &shift, &mask); - ret = max8998_write_reg(i2c, reg, i); - max8998->buck2_vol[max8998->buck2_idx] = i; + ret = max8998_write_reg(i2c, reg, selector); + max8998->buck2_vol[max8998->buck2_idx] = selector; buck2_gpio_set(pdata->buck2_set3, max8998->buck2_idx); buck2_exit: dev_dbg(max8998->dev, "%s: SET3:%d\n", i2c->name, gpio_get_value(pdata->buck2_set3)); } else { - ret = max8998_write_reg(i2c, reg, i); + ret = max8998_write_reg(i2c, reg, selector); } break; case MAX8998_BUCK3: case MAX8998_BUCK4: - ret = max8998_update_reg(i2c, reg, i< Date: Tue, 19 Jun 2012 09:38:45 +0800 Subject: regulator: Use list_voltage() to get voltage in regulator_set_voltage_time_sel With this change, regulator_set_voltage_time_sel() can be more generic and not limited to linear and table based mapping now. One side-effect of this change is that list_voltage() must be implemented. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/core.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 26b71048709b..e3597ab09be3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2296,8 +2296,7 @@ EXPORT_SYMBOL_GPL(regulator_set_voltage_time); * Provided with the starting and target voltage selectors, this function * returns time in microseconds required to rise or fall to this new voltage * - * Drivers providing uV_step or volt_table in their regulator_desc and - * ramp_delay in regulation_constraints can use this as their + * Drivers providing ramp_delay in regulation_constraints can use this as their * set_voltage_time_sel() operation. */ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, @@ -2305,6 +2304,7 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int new_selector) { unsigned int ramp_delay = 0; + int old_volt, new_volt; if (rdev->constraints->ramp_delay) ramp_delay = rdev->constraints->ramp_delay; @@ -2316,19 +2316,14 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, return 0; } - if (rdev->desc->uV_step) { - return DIV_ROUND_UP(rdev->desc->uV_step * - abs(new_selector - old_selector), - ramp_delay); - } else if (rdev->desc->volt_table) { - return DIV_ROUND_UP(abs(rdev->desc->volt_table[new_selector] - - rdev->desc->volt_table[old_selector]), - ramp_delay); - } else { - rdev_warn(rdev, "Unsupported voltage mapping settings\n"); - } + /* sanity check */ + if (!rdev->desc->ops->list_voltage) + return -EINVAL; - return 0; + old_volt = rdev->desc->ops->list_voltage(rdev, old_selector); + new_volt = rdev->desc->ops->list_voltage(rdev, new_selector); + + return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay); } EXPORT_SYMBOL_GPL(regulator_set_voltage_time_sel); -- cgit v1.2.3 From ee85f543710dd56ce526cb44e39191f32972e5ad Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:48 +0800 Subject: ACPI/PM: specify lowest allowed state for device sleep state Lower device sleep state can save more power, but has more exit latency too. Sometimes, to satisfy some power QoS and other requirement, we need to constrain the lowest device sleep state. In this patch, a parameter to specify lowest allowed state for acpi_pm_device_sleep_state is added. So that the caller can enforce the constraint via the parameter. This is needed by PCIe D3cold support, where the lowest power state allowed may be D3_HOT instead of default D3_COLD. CC: Len Brown CC: linux-acpi@vger.kernel.org Reviewed-by: Rafael J. Wysocki Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/acpi/sleep.c | 24 +++++++++++++++++++----- drivers/pci/pci-acpi.c | 3 ++- drivers/pnp/pnpacpi/core.c | 4 ++-- 3 files changed, 23 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 88561029cca8..1cc02ca2af2a 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -716,8 +716,9 @@ int acpi_suspend(u32 acpi_state) * @dev: device to examine; its driver model wakeup flags control * whether it should be able to wake up the system * @d_min_p: used to store the upper limit of allowed states range - * Return value: preferred power state of the device on success, -ENODEV on - * failure (ie. if there's no 'struct acpi_device' for @dev) + * @d_max_in: specify the lowest allowed states + * Return value: preferred power state of the device on success, -ENODEV + * (ie. if there's no 'struct acpi_device' for @dev) or -EINVAL on failure * * Find the lowest power (highest number) ACPI device power state that * device @dev can be in while the system is in the sleep state represented @@ -732,13 +733,15 @@ int acpi_suspend(u32 acpi_state) * via @wake. */ -int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) +int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); struct acpi_device *adev; char acpi_method[] = "_SxD"; unsigned long long d_min, d_max; + if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3) + return -EINVAL; if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { printk(KERN_DEBUG "ACPI handle has no context!\n"); return -ENODEV; @@ -746,8 +749,10 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) acpi_method[2] = '0' + acpi_target_sleep_state; /* - * If the sleep state is S0, we will return D3, but if the device has - * _S0W, we will use the value from _S0W + * If the sleep state is S0, the lowest limit from ACPI is D3, + * but if the device has _S0W, we will use the value from _S0W + * as the lowest limit from ACPI. Finally, we will constrain + * the lowest limit with the specified one. */ d_min = ACPI_STATE_D0; d_max = ACPI_STATE_D3; @@ -791,8 +796,17 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) } } + if (d_max_in < d_min) + return -EINVAL; if (d_min_p) *d_min_p = d_min; + /* constrain d_max with specified lowest limit (max number) */ + if (d_max > d_max_in) { + for (d_max = d_max_in; d_max > d_min; d_max--) { + if (adev->power.states[d_max].flags.valid) + break; + } + } return d_max; } #endif /* CONFIG_PM */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 61e2fefeedab..a9efebc586b4 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -189,7 +189,8 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { int acpi_state; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL); + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, + ACPI_STATE_D3); if (acpi_state < 0) return PCI_POWER_ERROR; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index d21e8f59c84e..507a8e2b9a4c 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -170,8 +170,8 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state) } if (acpi_bus_power_manageable(handle)) { - int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL); - + int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL, + ACPI_STATE_D3); if (power_state < 0) power_state = (state.event == PM_EVENT_ON) ? ACPI_STATE_D0 : ACPI_STATE_D3; -- cgit v1.2.3 From 71a83bd727cc31c5fe960c3758cb396267ff710e Mon Sep 17 00:00:00 2001 From: Zheng Yan Date: Sat, 23 Jun 2012 10:23:49 +0800 Subject: PCI/PM: add runtime PM support to PCIe port This patch adds runtime PM support to PCIe port. This is needed by PCIe D3cold support, where PCIe device without ACPI node may be powered on/off by PCIe port. Because runtime suspend is broken for some chipsets, a black list is used to disable runtime PM support for these chipsets. Reviewed-by: Rafael J. Wysocki Signed-off-by: Zheng Yan Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/pcie/portdrv_pci.c | 24 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..9eae64b17954 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work) if (!list_empty(&pci_pme_list)) { list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { if (pme_dev->dev->pme_poll) { + struct pci_dev *bridge; + + bridge = pme_dev->dev->bus->self; + /* + * If bridge is in low power state, the + * configuration space of subordinate devices + * may be not accessible + */ + if (bridge && bridge->current_state != PCI_D0) + continue; pci_pme_wakeup(pme_dev->dev, NULL); } else { list_del(&pme_dev->list); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index e0610bda1dea..7c576b9aa01d 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int pcie_port_runtime_pm(struct device *dev) +{ + return 0; +} +#else +#define pcie_port_runtime_pm NULL +#endif + static const struct dev_pm_ops pcie_portdrv_pm_ops = { .suspend = pcie_port_device_suspend, .resume = pcie_port_device_resume, @@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, .resume_noirq = pcie_port_resume_noirq, + .runtime_suspend = pcie_port_runtime_pm, + .runtime_resume = pcie_port_runtime_pm, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) @@ -116,6 +128,14 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { #define PCIE_PORTDRV_PM_OPS NULL #endif /* !PM */ +/* + * PCIe port runtime suspend is broken for some chipsets, so use a + * black list to disable runtime PM for these chipsets. + */ +static const struct pci_device_id port_runtime_pm_black_list[] = { + { /* end: all zeroes */ } +}; + /* * pcie_portdrv_probe - Probe PCI-Express port devices * @dev: PCI-Express port device being probed @@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev, return status; pci_save_state(dev); + if (!pci_match_id(port_runtime_pm_black_list, dev)) + pm_runtime_put_noidle(&dev->dev); return 0; } static void pcie_portdrv_remove(struct pci_dev *dev) { + if (!pci_match_id(port_runtime_pm_black_list, dev)) + pm_runtime_get_noresume(&dev->dev); pcie_port_device_remove(dev); pci_disable_device(dev); } -- cgit v1.2.3 From 8497f696686ae1ab3f01e5956046d59844b9f500 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:50 +0800 Subject: PCI: do not call pci_set_power_state with PCI_D3cold PCI subsystem has not been ready for D3cold support yet. So PCI_D3cold should not be used as parameter for pci_set_power_state. This patch is needed for upcoming PCI_D3cold support. This patch has no functionality change, because pci_set_power_state will bound the parameter to PCI_D3hot too. CC: Michal Miroslaw CC: Jesse Barnes Reviewed-by: Rafael J. Wysocki Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/misc/cb710/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/cb710/core.c b/drivers/misc/cb710/core.c index 85cc7710193c..9d5eed754666 100644 --- a/drivers/misc/cb710/core.c +++ b/drivers/misc/cb710/core.c @@ -180,7 +180,7 @@ static int cb710_suspend(struct pci_dev *pdev, pm_message_t state) pci_save_state(pdev); pci_disable_device(pdev); if (state.event & PM_EVENT_SLEEP) - pci_set_power_state(pdev, PCI_D3cold); + pci_set_power_state(pdev, PCI_D3hot); return 0; } -- cgit v1.2.3 From 448bd857d48e69b33ef323739dc6d8ca20d4cda7 Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Sat, 23 Jun 2012 10:23:51 +0800 Subject: PCI/PM: add PCIe runtime D3cold support This patch adds runtime D3cold support and corresponding ACPI platform support. This patch only enables runtime D3cold support; it does not enable D3cold support during system suspend/hibernate. D3cold is the deepest power saving state for a PCIe device, where its main power is removed. While it is in D3cold, you can't access the device at all, not even its configuration space (which is still accessible in D3hot). Therefore the PCI PM registers can not be used to transition into/out of the D3cold state; that must be done by platform logic such as ACPI _PR3. To support wakeup from D3cold, a system may provide auxiliary power, which allows a device to request wakeup using a Beacon or the sideband WAKE# signal. WAKE# is usually connected to platform logic such as ACPI GPE. This is quite different from other power saving states, where devices request wakeup via a PME message on the PCIe link. Some devices, such as those in plug-in slots, have no direct platform logic. For example, there is usually no ACPI _PR3 for them. D3cold support for these devices can be done via the PCIe Downstream Port leading to the device. When the PCIe port is powered on/off, the device is powered on/off too. Wakeup events from the device will be notified to the corresponding PCIe port. For more information about PCIe D3cold and corresponding ACPI support, please refer to: - PCI Express Base Specification Revision 2.0 - Advanced Configuration and Power Interface Specification Revision 5.0 [bhelgaas: changelog] Reviewed-by: Rafael J. Wysocki Originally-by: Zheng Yan Signed-off-by: Huang Ying Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 23 +++++++-- drivers/pci/pci-driver.c | 10 +++- drivers/pci/pci-sysfs.c | 29 +++++++++++ drivers/pci/pci.c | 114 +++++++++++++++++++++++++++++++++++++---- drivers/pci/pci.h | 1 + drivers/pci/pcie/portdrv_pci.c | 44 ++++++++++++++-- 6 files changed, 203 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a9efebc586b4..e1658afef873 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -48,6 +48,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) return; + if (pci_dev->current_state == PCI_D3cold) { + pci_wakeup_event(pci_dev); + pm_runtime_resume(&pci_dev->dev); + return; + } + if (!pci_dev->pm_cap || !pci_dev->pme_support || pci_check_pme_status(pci_dev)) { if (pci_dev->pme_poll) @@ -187,10 +193,13 @@ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev) { - int acpi_state; + int acpi_state, d_max; - acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, - ACPI_STATE_D3); + if (pdev->no_d3cold) + d_max = ACPI_STATE_D3_HOT; + else + d_max = ACPI_STATE_D3_COLD; + acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL, d_max); if (acpi_state < 0) return PCI_POWER_ERROR; @@ -297,7 +306,13 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) { - if (dev->pme_interrupt) + /* + * Per PCI Express Base Specification Revision 2.0 section + * 5.3.3.2 Link Wakeup, platform support is needed for D3cold + * waking up to power on the main link even if there is PME + * support for D3cold + */ + if (dev->pme_interrupt && !dev->runtime_d3cold) return 0; if (!acpi_pm_device_run_wake(&dev->dev, enable)) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bf0cee629b60..ca2e4c79a588 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1019,10 +1019,13 @@ static int pci_pm_runtime_suspend(struct device *dev) if (!pm || !pm->runtime_suspend) return -ENOSYS; + pci_dev->no_d3cold = false; error = pm->runtime_suspend(dev); suspend_report_result(pm->runtime_suspend, error); if (error) return error; + if (!pci_dev->d3cold_allowed) + pci_dev->no_d3cold = true; pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -1044,6 +1047,7 @@ static int pci_pm_runtime_suspend(struct device *dev) static int pci_pm_runtime_resume(struct device *dev) { + int rc; struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; @@ -1054,7 +1058,11 @@ static int pci_pm_runtime_resume(struct device *dev) __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); - return pm->runtime_resume(dev); + rc = pm->runtime_resume(dev); + + pci_dev->runtime_d3cold = false; + + return rc; } static int pci_pm_runtime_idle(struct device *dev) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 86c63fe45d11..1426db0c0607 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -378,6 +379,31 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr, #endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) +static ssize_t d3cold_allowed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + pdev->d3cold_allowed = !!val; + pm_runtime_resume(dev); + + return count; +} + +static ssize_t d3cold_allowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + return sprintf (buf, "%u\n", pdev->d3cold_allowed); +} +#endif + struct device_attribute pci_dev_attrs[] = { __ATTR_RO(resource), __ATTR_RO(vendor), @@ -401,6 +427,9 @@ struct device_attribute pci_dev_attrs[] = { #ifdef CONFIG_HOTPLUG __ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store), __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store), +#endif +#if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) + __ATTR(d3cold_allowed, 0644, d3cold_allowed_show, d3cold_allowed_store), #endif __ATTR_NULL, }; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9eae64b17954..8effb9b23eec 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -622,7 +622,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) dev_info(&dev->dev, "Refused to change power state, " "currently in D%d\n", dev->current_state); - /* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT + /* + * According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning * from D3hot to D0 _may_ perform an internal reset, thereby * going to "D0 Uninitialized" rather than "D0 Initialized". @@ -654,6 +655,16 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) if (dev->pm_cap) { u16 pmcsr; + /* + * Configuration space is not accessible for device in + * D3cold, so just keep or set D3cold for safety + */ + if (dev->current_state == PCI_D3cold) + return; + if (state == PCI_D3cold) { + dev->current_state = PCI_D3cold; + return; + } pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } else { @@ -694,8 +705,50 @@ static int pci_platform_power_transition(struct pci_dev *dev, pci_power_t state) */ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) { - if (state == PCI_D0) + if (state == PCI_D0) { pci_platform_power_transition(dev, PCI_D0); + /* + * Mandatory power management transition delays, see + * PCI Express Base Specification Revision 2.0 Section + * 6.6.1: Conventional Reset. Do not delay for + * devices powered on/off by corresponding bridge, + * because have already delayed for the bridge. + */ + if (dev->runtime_d3cold) { + msleep(dev->d3cold_delay); + /* + * When powering on a bridge from D3cold, the + * whole hierarchy may be powered on into + * D0uninitialized state, resume them to give + * them a chance to suspend again + */ + pci_wakeup_bus(dev->subordinate); + } + } +} + +/** + * __pci_dev_set_current_state - Set current state of a PCI device + * @dev: Device to handle + * @data: pointer to state to be set + */ +static int __pci_dev_set_current_state(struct pci_dev *dev, void *data) +{ + pci_power_t state = *(pci_power_t *)data; + + dev->current_state = state; + return 0; +} + +/** + * __pci_bus_set_current_state - Walk given bus and set current state of devices + * @bus: Top bus of the subtree to walk. + * @state: state to be set + */ +static void __pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state) +{ + if (bus) + pci_walk_bus(bus, __pci_dev_set_current_state, &state); } /** @@ -707,8 +760,15 @@ static void __pci_start_power_transition(struct pci_dev *dev, pci_power_t state) */ int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state) { - return state >= PCI_D0 ? - pci_platform_power_transition(dev, state) : -EINVAL; + int ret; + + if (state < PCI_D0) + return -EINVAL; + ret = pci_platform_power_transition(dev, state); + /* Power off the bridge may power off the whole hierarchy */ + if (!ret && state == PCI_D3cold) + __pci_bus_set_current_state(dev->subordinate, PCI_D3cold); + return ret; } EXPORT_SYMBOL_GPL(__pci_complete_power_transition); @@ -732,8 +792,8 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) int error; /* bound the state we're entering */ - if (state > PCI_D3hot) - state = PCI_D3hot; + if (state > PCI_D3cold) + state = PCI_D3cold; else if (state < PCI_D0) state = PCI_D0; else if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev)) @@ -748,10 +808,15 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* This device is quirked not to be put into D3, so don't put it in D3 */ - if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) + if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) return 0; - error = pci_raw_set_power_state(dev, state); + /* + * To put device in D3cold, we put device into D3hot in native + * way, then put device into D3cold with platform ops + */ + error = pci_raw_set_power_state(dev, state > PCI_D3hot ? + PCI_D3hot : state); if (!__pci_complete_power_transition(dev, state)) error = 0; @@ -1497,6 +1562,28 @@ void pci_pme_wakeup_bus(struct pci_bus *bus) pci_walk_bus(bus, pci_pme_wakeup, (void *)true); } +/** + * pci_wakeup - Wake up a PCI device + * @dev: Device to handle. + * @ign: ignored parameter + */ +static int pci_wakeup(struct pci_dev *pci_dev, void *ign) +{ + pci_wakeup_event(pci_dev); + pm_request_resume(&pci_dev->dev); + return 0; +} + +/** + * pci_wakeup_bus - Walk given bus and wake up devices on it + * @bus: Top bus of the subtree to walk. + */ +void pci_wakeup_bus(struct pci_bus *bus) +{ + if (bus) + pci_walk_bus(bus, pci_wakeup, NULL); +} + /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. @@ -1754,6 +1841,10 @@ int pci_prepare_to_sleep(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + /* D3cold during system suspend/hibernate is not supported */ + if (target_state > PCI_D3hot) + target_state = PCI_D3hot; + pci_enable_wake(dev, target_state, device_may_wakeup(&dev->dev)); error = pci_set_power_state(dev, target_state); @@ -1791,12 +1882,16 @@ int pci_finish_runtime_suspend(struct pci_dev *dev) if (target_state == PCI_POWER_ERROR) return -EIO; + dev->runtime_d3cold = target_state == PCI_D3cold; + __pci_enable_wake(dev, target_state, true, pci_dev_run_wake(dev)); error = pci_set_power_state(dev, target_state); - if (error) + if (error) { __pci_enable_wake(dev, target_state, true, false); + dev->runtime_d3cold = false; + } return error; } @@ -1866,6 +1961,7 @@ void pci_pm_init(struct pci_dev *dev) dev->pm_cap = pm; dev->d3_delay = PCI_PM_D3_WAIT; + dev->d3cold_delay = PCI_PM_D3COLD_WAIT; dev->d1_support = false; dev->d2_support = false; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index e4943479b234..5cd3dce7a245 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -70,6 +70,7 @@ extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +extern void pci_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 7c576b9aa01d..3a7eefcb270a 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -101,12 +101,48 @@ static int pcie_port_resume_noirq(struct device *dev) } #ifdef CONFIG_PM_RUNTIME -static int pcie_port_runtime_pm(struct device *dev) +struct d3cold_info { + bool no_d3cold; + unsigned int d3cold_delay; +}; + +static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data) +{ + struct d3cold_info *info = data; + + info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay, + info->d3cold_delay); + if (pdev->no_d3cold) + info->no_d3cold = true; + return 0; +} + +static int pcie_port_runtime_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct d3cold_info d3cold_info = { + .no_d3cold = false, + .d3cold_delay = PCI_PM_D3_WAIT, + }; + + /* + * If any subordinate device disable D3cold, we should not put + * the port into D3cold. The D3cold delay of port should be + * the max of that of all subordinate devices. + */ + pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info); + pdev->no_d3cold = d3cold_info.no_d3cold; + pdev->d3cold_delay = d3cold_info.d3cold_delay; + return 0; +} + +static int pcie_port_runtime_resume(struct device *dev) { return 0; } #else -#define pcie_port_runtime_pm NULL +#define pcie_port_runtime_suspend NULL +#define pcie_port_runtime_resume NULL #endif static const struct dev_pm_ops pcie_portdrv_pm_ops = { @@ -117,8 +153,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { .poweroff = pcie_port_device_suspend, .restore = pcie_port_device_resume, .resume_noirq = pcie_port_resume_noirq, - .runtime_suspend = pcie_port_runtime_pm, - .runtime_resume = pcie_port_runtime_pm, + .runtime_suspend = pcie_port_runtime_suspend, + .runtime_resume = pcie_port_runtime_resume, }; #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) -- cgit v1.2.3 From 2f6ae6ef631cd16b0725958ee805a24b9e38d7ad Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 19 Jun 2012 09:26:52 +0800 Subject: regulator: rc5t583: Use regulator_set_voltage_time_sel() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/rc5t583-regulator.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/rc5t583-regulator.c b/drivers/regulator/rc5t583-regulator.c index 332eae897dab..8bf4e8c9de9a 100644 --- a/drivers/regulator/rc5t583-regulator.c +++ b/drivers/regulator/rc5t583-regulator.c @@ -42,7 +42,6 @@ struct rc5t583_regulator_info { /* Regulator specific turn-on delay and voltage settling time*/ int enable_uv_per_us; - int change_uv_per_us; /* Used by regulator core */ struct regulator_desc desc; @@ -66,17 +65,6 @@ static int rc5t583_regulator_enable_time(struct regulator_dev *rdev) return DIV_ROUND_UP(curr_uV, reg->reg_info->enable_uv_per_us); } -static int rc5t583_set_voltage_time_sel(struct regulator_dev *rdev, - unsigned int old_selector, unsigned int new_selector) -{ - struct rc5t583_regulator *reg = rdev_get_drvdata(rdev); - - return DIV_ROUND_UP(abs(new_selector - old_selector) * - rdev->desc->uV_step, - reg->reg_info->change_uv_per_us); -} - - static struct regulator_ops rc5t583_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, @@ -86,7 +74,7 @@ static struct regulator_ops rc5t583_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, - .set_voltage_time_sel = rc5t583_set_voltage_time_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, }; #define RC5T583_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, \ @@ -96,7 +84,6 @@ static struct regulator_ops rc5t583_ops = { .disc_bit = _disc_bit, \ .deepsleep_reg = RC5T583_REG_##_id##DAC_DS, \ .enable_uv_per_us = _enable_mv * 1000, \ - .change_uv_per_us = 40 * 1000, \ .deepsleep_id = RC5T583_DS_##_id, \ .desc = { \ .name = "rc5t583-regulator-"#_id, \ @@ -111,6 +98,7 @@ static struct regulator_ops rc5t583_ops = { .enable_mask = BIT(_en_bit), \ .min_uV = _min_mv * 1000, \ .uV_step = _step_uV, \ + .ramp_delay = 40 * 1000, \ }, \ } -- cgit v1.2.3 From 1f758b23177d588a71b96ad02990e715949bb82f Mon Sep 17 00:00:00 2001 From: Mandeep Singh Baines Date: Sun, 24 Jun 2012 23:31:09 +0200 Subject: PM / Sleep: Prevent waiting forever on asynchronous suspend after abort __device_suspend() must always send a completion. Otherwise, parent devices will wait forever. Commit 1e2ef05b, "PM: Limit race conditions between runtime PM and system sleep (v2)", introduced a regression by short-circuiting the complete_all() for certain error cases. This patch fixes the bug by always signalling a completion. Addresses http://crosbug.com/31972 Tested by injecting an abort. Signed-off-by: Mandeep Singh Baines Cc: stable@vger.kernel.org Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e0fb5b0435a3..9cb845e49334 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1031,7 +1031,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) dpm_wait_for_children(dev, async); if (async_error) - return 0; + goto Complete; pm_runtime_get_noresume(dev); if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) @@ -1040,7 +1040,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (pm_wakeup_pending()) { pm_runtime_put_sync(dev); async_error = -EBUSY; - return 0; + goto Complete; } device_lock(dev); @@ -1097,6 +1097,8 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) } device_unlock(dev); + + Complete: complete_all(&dev->power.completion); if (error) { -- cgit v1.2.3 From b04db9ac4f7641332e0133b2fd8f82e6e4553947 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 21 Jun 2012 11:53:44 +0300 Subject: iwlwifi: configure the queues from the op_mode Since the op_mode defines the queue mapping, let it do it completely through the API functions. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/commands.h | 40 +++++++++++++++++++- drivers/net/wireless/iwlwifi/dvm/dev.h | 16 -------- drivers/net/wireless/iwlwifi/dvm/main.c | 56 +--------------------------- drivers/net/wireless/iwlwifi/dvm/ucode.c | 37 ++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-trans.h | 27 +++++++++----- drivers/net/wireless/iwlwifi/pcie/internal.h | 3 +- drivers/net/wireless/iwlwifi/pcie/trans.c | 22 ++--------- 7 files changed, 98 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 64811cd91635..97bea16f3592 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -190,6 +190,44 @@ enum { REPLY_MAX = 0xff }; +/* + * Minimum number of queues. MAX_NUM is defined in hw specific files. + * Set the minimum to accommodate + * - 4 standard TX queues + * - the command queue + * - 4 PAN TX queues + * - the PAN multicast queue, and + * - the AUX (TX during scan dwell) queue. + */ +#define IWL_MIN_NUM_QUEUES 11 + +/* + * Command queue depends on iPAN support. + */ +#define IWL_DEFAULT_CMD_QUEUE_NUM 4 +#define IWL_IPAN_CMD_QUEUE_NUM 9 + +#define IWL_TX_FIFO_BK 0 /* shared */ +#define IWL_TX_FIFO_BE 1 +#define IWL_TX_FIFO_VI 2 /* shared */ +#define IWL_TX_FIFO_VO 3 +#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK +#define IWL_TX_FIFO_BE_IPAN 4 +#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI +#define IWL_TX_FIFO_VO_IPAN 5 +/* re-uses the VO FIFO, uCode will properly flush/schedule */ +#define IWL_TX_FIFO_AUX 5 +#define IWL_TX_FIFO_UNUSED 255 + +#define IWLAGN_CMD_FIFO_NUM 7 + +/* + * This queue number is required for proper operation + * because the ucode will stop/start the scheduler as + * required. + */ +#define IWL_IPAN_MCAST_QUEUE 8 + /****************************************************************************** * (0) * Commonly used structures and definitions: @@ -755,8 +793,6 @@ struct iwl_qosparam_cmd { #define IWLAGN_BROADCAST_ID 15 #define IWLAGN_STATION_COUNT 16 -#define IWL_INVALID_STATION 255 -#define IWL_MAX_TID_COUNT 8 #define IWL_TID_NON_QOS IWL_MAX_TID_COUNT #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 54cf085ddc89..054f728f6266 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -90,22 +90,6 @@ #define IWL_NUM_SCAN_RATES (2) -/* - * Minimum number of queues. MAX_NUM is defined in hw specific files. - * Set the minimum to accommodate - * - 4 standard TX queues - * - the command queue - * - 4 PAN TX queues - * - the PAN multicast queue, and - * - the AUX (TX during scan dwell) queue. - */ -#define IWL_MIN_NUM_QUEUES 11 - -/* - * Command queue depends on iPAN support. - */ -#define IWL_DEFAULT_CMD_QUEUE_NUM 4 -#define IWL_IPAN_CMD_QUEUE_NUM 9 #define IEEE80211_DATA_LEN 2304 #define IEEE80211_4ADDR_LEN 30 diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index abfd7916bde6..612f05d757db 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -518,49 +518,6 @@ static void iwl_bg_tx_flush(struct work_struct *work) * queue/FIFO/AC mapping definitions */ -#define IWL_TX_FIFO_BK 0 /* shared */ -#define IWL_TX_FIFO_BE 1 -#define IWL_TX_FIFO_VI 2 /* shared */ -#define IWL_TX_FIFO_VO 3 -#define IWL_TX_FIFO_BK_IPAN IWL_TX_FIFO_BK -#define IWL_TX_FIFO_BE_IPAN 4 -#define IWL_TX_FIFO_VI_IPAN IWL_TX_FIFO_VI -#define IWL_TX_FIFO_VO_IPAN 5 -/* re-uses the VO FIFO, uCode will properly flush/schedule */ -#define IWL_TX_FIFO_AUX 5 -#define IWL_TX_FIFO_UNUSED -1 - -#define IWLAGN_CMD_FIFO_NUM 7 - -/* - * This queue number is required for proper operation - * because the ucode will stop/start the scheduler as - * required. - */ -#define IWL_IPAN_MCAST_QUEUE 8 - -static const u8 iwlagn_default_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, - IWLAGN_CMD_FIFO_NUM, -}; - -static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, - IWL_TX_FIFO_BK_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_VI_IPAN, - IWL_TX_FIFO_VO_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWLAGN_CMD_FIFO_NUM, - IWL_TX_FIFO_AUX, -}; - static const u8 iwlagn_bss_ac_to_fifo[] = { IWL_TX_FIFO_VO, IWL_TX_FIFO_VI, @@ -1350,6 +1307,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, else trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_dvm_cmd_strings; + trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < priv->cfg->base_params->num_of_queues); @@ -1363,15 +1321,9 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; - trans_cfg.queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo; - trans_cfg.n_queue_to_fifo = - ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); } else { priv->sta_key_max_num = STA_KEY_MAX_NUM; trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo; - trans_cfg.n_queue_to_fifo = - ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); } /* Configure transport layer */ @@ -1460,9 +1412,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P; priv->sta_key_max_num = STA_KEY_MAX_NUM; trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - trans_cfg.queue_to_fifo = iwlagn_default_queue_to_tx_fifo; - trans_cfg.n_queue_to_fifo = - ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); /* Configure transport layer again*/ iwl_trans_configure(priv->trans, &trans_cfg); @@ -1480,9 +1429,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, atomic_set(&priv->queue_stop_count[i], 0); } - WARN_ON(trans_cfg.queue_to_fifo[trans_cfg.cmd_queue] != - IWLAGN_CMD_FIFO_NUM); - if (iwl_init_drv(priv)) goto out_free_eeprom; diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index b3a314ba48c7..6d8d6dd7943f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -226,13 +226,50 @@ int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) return ret; } +static const u8 iwlagn_default_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, +}; + +static const u8 iwlagn_ipan_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, + IWL_TX_FIFO_BK_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_VI_IPAN, + IWL_TX_FIFO_VO_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_UNUSED, + IWL_TX_FIFO_AUX, +}; static int iwl_alive_notify(struct iwl_priv *priv) { + const u8 *queue_to_txf; + u8 n_queues; int ret; + int i; iwl_trans_fw_alive(priv->trans); + if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && + priv->eeprom_data->sku & EEPROM_SKU_CAP_IPAN_ENABLE) { + n_queues = ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo); + queue_to_txf = iwlagn_ipan_queue_to_tx_fifo; + } else { + n_queues = ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo); + queue_to_txf = iwlagn_default_queue_to_tx_fifo; + } + + for (i = 0; i < n_queues; i++) + if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED) + iwl_trans_ac_txq_enable(priv->trans, i, + queue_to_txf[i]); + priv->passive_no_rx = false; priv->transport_queue_stop = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 00efde8e5536..867d8e194da4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -290,16 +290,17 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) * currently supports */ #define IWL_MAX_HW_QUEUES 32 +#define IWL_INVALID_STATION 255 +#define IWL_MAX_TID_COUNT 8 +#define IWL_FRAME_LIMIT 64 /** * struct iwl_trans_config - transport configuration * * @op_mode: pointer to the upper layer. - * @queue_to_fifo: queue to FIFO mapping to set up by - * default - * @n_queue_to_fifo: number of queues to set up * @cmd_queue: the index of the command queue. * Must be set before start_fw. + * @cmd_fifo: the fifo for host commands * @no_reclaim_cmds: Some devices erroneously don't set the * SEQ_RX_FRAME bit on some notifications, this is the * list of such notifications to filter. Max length is @@ -314,10 +315,9 @@ static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) */ struct iwl_trans_config { struct iwl_op_mode *op_mode; - const u8 *queue_to_fifo; - u8 n_queue_to_fifo; u8 cmd_queue; + u8 cmd_fifo; const u8 *no_reclaim_cmds; int n_no_reclaim_cmds; @@ -355,9 +355,9 @@ struct iwl_trans; * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. * Must be atomic - * @txq_enable: setup a tx queue for AMPDU - will be called once the HW is - * ready and a successful ADDBA response has been received. - * May sleep + * @txq_enable: setup a queue. To setup an AC queue, use the + * iwl_trans_ac_txq_enable wrapper. fw_alive must have been called before + * this one. The op_mode must not configure the HCMD queue. May sleep. * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic * @wait_tx_queue_empty: wait until all tx queues are empty @@ -497,9 +497,9 @@ static inline void iwl_trans_fw_alive(struct iwl_trans *trans) { might_sleep(); - trans->ops->fw_alive(trans); - trans->state = IWL_TRANS_FW_ALIVE; + + trans->ops->fw_alive(trans); } static inline int iwl_trans_start_fw(struct iwl_trans *trans, @@ -593,6 +593,13 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, frame_limit, ssn); } +static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, + int fifo) +{ + iwl_trans_txq_enable(trans, queue, fifo, IWL_INVALID_STATION, + IWL_MAX_TID_COUNT, IWL_FRAME_LIMIT, 0); +} + static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans) { WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 5024fb662bf6..d9694c58208c 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -269,10 +269,9 @@ struct iwl_trans_pcie { wait_queue_head_t ucode_write_waitq; unsigned long status; u8 cmd_queue; + u8 cmd_fifo; u8 n_no_reclaim_cmds; u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; - u8 setup_q_to_fifo[IWL_MAX_HW_QUEUES]; - u8 n_q_to_fifo; bool rx_buf_size_8k; u32 rx_page_order; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 42f369d15f48..bac0eb0d046d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1059,7 +1059,7 @@ static void iwl_tx_start(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 a; - int i, chan; + int chan; u32 reg_val; /* make sure all queue are not stopped/used */ @@ -1091,12 +1091,8 @@ static void iwl_tx_start(struct iwl_trans *trans) */ iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); - for (i = 0; i < trans_pcie->n_q_to_fifo; i++) { - int fifo = trans_pcie->setup_q_to_fifo[i]; - - iwl_trans_pcie_txq_enable(trans, i, fifo, IWL_INVALID_STATION, - IWL_TID_NON_QOS, SCD_FRAME_LIMIT, 0); - } + iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, + trans_pcie->cmd_fifo); /* Activate all Tx DMA/FIFO channels */ iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); @@ -1528,6 +1524,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie->cmd_queue = trans_cfg->cmd_queue; + trans_pcie->cmd_fifo = trans_cfg->cmd_fifo; if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) trans_pcie->n_no_reclaim_cmds = 0; else @@ -1536,17 +1533,6 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, trans_pcie->n_no_reclaim_cmds * sizeof(u8)); - trans_pcie->n_q_to_fifo = trans_cfg->n_queue_to_fifo; - - if (WARN_ON(trans_pcie->n_q_to_fifo > IWL_MAX_HW_QUEUES)) - trans_pcie->n_q_to_fifo = IWL_MAX_HW_QUEUES; - - /* at least the command queue must be mapped */ - WARN_ON(!trans_pcie->n_q_to_fifo); - - memcpy(trans_pcie->setup_q_to_fifo, trans_cfg->queue_to_fifo, - trans_pcie->n_q_to_fifo * sizeof(u8)); - trans_pcie->rx_buf_size_8k = trans_cfg->rx_buf_size_8k; if (trans_pcie->rx_buf_size_8k) trans_pcie->rx_page_order = get_order(8 * 1024); -- cgit v1.2.3 From eecf21a126dd23030c66949796561a9b0dde8b6e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 Jun 2012 15:21:02 +0200 Subject: iwlwifi: bump trace message limit There's one message that goes just over the limit of 100 characters, so bump the limit to 110 to get rid of the warning from that. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-devtrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 65364793021f..ad2dba8fc450 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -176,7 +176,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_msg -#define MAX_MSG_LEN 100 +#define MAX_MSG_LEN 110 DECLARE_EVENT_CLASS(iwlwifi_msg_event, TP_PROTO(struct va_format *vaf), -- cgit v1.2.3 From 94bfa4a255b3436e9a258585be6687d1f5c02a71 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 Jun 2012 19:39:23 +0200 Subject: iwlwifi: use __get_str in tracing __get_str() is identical to (char *)__get_dynamic_array() that is in the code now, substitute __get_str to make the code more readable. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-devtrace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index ad2dba8fc450..06ca505bb2cc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -189,7 +189,7 @@ DECLARE_EVENT_CLASS(iwlwifi_msg_event, MAX_MSG_LEN, vaf->fmt, *vaf->va) >= MAX_MSG_LEN); ), - TP_printk("%s", (char *)__get_dynamic_array(msg)) + TP_printk("%s", __get_str(msg)) ); DEFINE_EVENT(iwlwifi_msg_event, iwlwifi_err, -- cgit v1.2.3 From 1af142c6f9843952d8eaff1c3de40d0b5d16f3ed Mon Sep 17 00:00:00 2001 From: Sangbeom Kim Date: Wed, 13 Jun 2012 17:27:54 +0900 Subject: regulator: Modify ramp_delay value for s5m8767a As s5m8767a is revisioned, ramp_delay register is changed. 5mV/uS, 10mV/uS, 25mV/uS, 50mV/uS, 100mV/uS ramp delay can be selected. Signed-off-by: Sangbeom Kim Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 22642c3ccf76..533bde68e4b7 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -720,9 +720,13 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) if (s5m8767->buck2_ramp || s5m8767->buck3_ramp || s5m8767->buck4_ramp) { switch (s5m8767->ramp_delay) { - case 15: + case 5: s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, - 0xc0, 0xf0); + 0x40, 0xf0); + break; + case 10: + s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, + 0x90, 0xf0); break; case 25: s5m_reg_update(s5m8767->iodev, S5M8767_REG_DVSRAMP, -- cgit v1.2.3 From 1910efa1d0fdf8109b285d4486f6a0de810b5574 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 23 Jun 2012 13:07:57 +0100 Subject: regulator: Add driver for Arizona LDO1 Arizona class devices feature an integrated LDO which is intended to supply the digital core for the device. Provide a driver offering minimal control of this regulator. Signed-off-by: Mark Brown --- drivers/regulator/Makefile | 2 +- drivers/regulator/arizona-ldo1.c | 134 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 drivers/regulator/arizona-ldo1.c (limited to 'drivers') diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ec4a0cfb3ca7..c7460888edcb 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -15,7 +15,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o -obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o +obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c new file mode 100644 index 000000000000..51f02cac443f --- /dev/null +++ b/drivers/regulator/arizona-ldo1.c @@ -0,0 +1,134 @@ +/* + * arizona-ldo1.c -- LDO1 supply for Arizona devices + * + * Copyright 2012 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct arizona_ldo1 { + struct regulator_dev *regulator; + struct arizona *arizona; + + struct regulator_consumer_supply supply; + struct regulator_init_data init_data; +}; + +static struct regulator_ops arizona_ldo1_ops = { + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_desc arizona_ldo1 = { + .name = "LDO1", + .supply_name = "LDOVDD", + .type = REGULATOR_VOLTAGE, + .ops = &arizona_ldo1_ops, + + .vsel_reg = ARIZONA_LDO1_CONTROL_1, + .vsel_mask = ARIZONA_LDO1_VSEL_MASK, + .min_uV = 900000, + .uV_step = 50000, + .n_voltages = 7, + + .owner = THIS_MODULE, +}; + +static const struct regulator_init_data arizona_ldo1_default = { + .num_consumer_supplies = 1, +}; + +static __devinit int arizona_ldo1_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct arizona_ldo1 *ldo1; + int ret; + + ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); + if (ldo1 == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo1->arizona = arizona; + + /* + * Since the chip usually supplies itself we provide some + * default init_data for it. This will be overridden with + * platform data if provided. + */ + ldo1->init_data = arizona_ldo1_default; + ldo1->init_data.consumer_supplies = &ldo1->supply; + ldo1->supply.supply = "DCVDD"; + ldo1->supply.dev_name = dev_name(arizona->dev); + + config.dev = arizona->dev; + config.driver_data = ldo1; + config.regmap = arizona->regmap; + + if (arizona->pdata.ldo1) + config.init_data = arizona->pdata.ldo1; + else + config.init_data = &ldo1->init_data; + + ldo1->regulator = regulator_register(&arizona_ldo1, &config); + if (IS_ERR(ldo1->regulator)) { + ret = PTR_ERR(ldo1->regulator); + dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, ldo1); + + return 0; +} + +static __devexit int arizona_ldo1_remove(struct platform_device *pdev) +{ + struct arizona_ldo1 *ldo1 = platform_get_drvdata(pdev); + + regulator_unregister(ldo1->regulator); + + return 0; +} + +static struct platform_driver arizona_ldo1_driver = { + .probe = arizona_ldo1_probe, + .remove = __devexit_p(arizona_ldo1_remove), + .driver = { + .name = "arizona-ldo1", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(arizona_ldo1_driver); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Arizona LDO1 driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:arizona-ldo1"); -- cgit v1.2.3 From ac1534a55d1e87d59a21c09c570605933b551480 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Thu, 21 Jun 2012 14:52:40 +0200 Subject: iommu/amd: Initialize dma_ops for hotplug and sriov devices When a device is added to the system at runtime the AMD IOMMU driver initializes the necessary data structures to handle translation for it. But it forgets to change the per-device dma_ops to point to the AMD IOMMU driver. So mapping actually never happens and all DMA accesses end in an IO_PAGE_FAULT. Fix this. Reported-by: Stefan Assmann Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a2e418cba0ff..dfe7d37c82c5 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -83,6 +83,8 @@ static struct iommu_ops amd_iommu_ops; static ATOMIC_NOTIFIER_HEAD(ppr_notifier); int amd_iommu_max_glx_val = -1; +static struct dma_map_ops amd_iommu_dma_ops; + /* * general struct to manage commands send to an IOMMU */ @@ -2267,6 +2269,13 @@ static int device_change_notifier(struct notifier_block *nb, list_add_tail(&dma_domain->list, &iommu_pd_list); spin_unlock_irqrestore(&iommu_pd_list_lock, flags); + dev_data = get_dev_data(dev); + + if (!dev_data->passthrough) + dev->archdata.dma_ops = &amd_iommu_dma_ops; + else + dev->archdata.dma_ops = &nommu_dma_ops; + break; case BUS_NOTIFY_DEL_DEVICE: -- cgit v1.2.3 From d72e31c9374627068df29da8085ca18c92ae35d3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:18:53 -0600 Subject: iommu: IOMMU Groups IOMMU device groups are currently a rather vague associative notion with assembly required by the user or user level driver provider to do anything useful. This patch intends to grow the IOMMU group concept into something a bit more consumable. To do this, we first create an object representing the group, struct iommu_group. This structure is allocated (iommu_group_alloc) and filled (iommu_group_add_device) by the iommu driver. The iommu driver is free to add devices to the group using it's own set of policies. This allows inclusion of devices based on physical hardware or topology limitations of the platform, as well as soft requirements, such as multi-function trust levels or peer-to-peer protection of the interconnects. Each device may only belong to a single iommu group, which is linked from struct device.iommu_group. IOMMU groups are maintained using kobject reference counting, allowing for automatic removal of empty, unreferenced groups. It is the responsibility of the iommu driver to remove devices from the group (iommu_group_remove_device). IOMMU groups also include a userspace representation in sysfs under /sys/kernel/iommu_groups. When allocated, each group is given a dynamically assign ID (int). The ID is managed by the core IOMMU group code to support multiple heterogeneous iommu drivers, which could potentially collide in group naming/numbering. This also keeps group IDs to small, easily managed values. A directory is created under /sys/kernel/iommu_groups for each group. A further subdirectory named "devices" contains links to each device within the group. The iommu_group file in the device's sysfs directory, which formerly contained a group number when read, is now a link to the iommu group. Example: $ ls -l /sys/kernel/iommu_groups/26/devices/ total 0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:00:1e.0 -> ../../../../devices/pci0000:00/0000:00:1e.0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:06:0d.0 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.0 lrwxrwxrwx. 1 root root 0 Apr 17 12:57 0000:06:0d.1 -> ../../../../devices/pci0000:00/0000:00:1e.0/0000:06:0d.1 $ ls -l /sys/kernel/iommu_groups/26/devices/*/iommu_group [truncating perms/owner/timestamp] /sys/kernel/iommu_groups/26/devices/0000:00:1e.0/iommu_group -> ../../../kernel/iommu_groups/26 /sys/kernel/iommu_groups/26/devices/0000:06:0d.0/iommu_group -> ../../../../kernel/iommu_groups/26 /sys/kernel/iommu_groups/26/devices/0000:06:0d.1/iommu_group -> ../../../../kernel/iommu_groups/26 Groups also include several exported functions for use by user level driver providers, for example VFIO. These include: iommu_group_get(): Acquires a reference to a group from a device iommu_group_put(): Releases reference iommu_group_for_each_dev(): Iterates over group devices using callback iommu_group_[un]register_notifier(): Allows notification of device add and remove operations relevant to the group iommu_group_id(): Return the group number This patch also extends the IOMMU API to allow attaching groups to domains. This is currently a simple wrapper for iterating through devices within a group, but it's expected that the IOMMU API may eventually make groups a more integral part of domains. Groups intentionally do not try to manage group ownership. A user level driver provider must independently acquire ownership for each device within a group before making use of the group as a whole. This may change in the future if group usage becomes more pervasive across both DMA and IOMMU ops. Groups intentionally do not provide a mechanism for driver locking or otherwise manipulating driver matching/probing of devices within the group. Such interfaces are generic to devices and beyond the scope of IOMMU groups. If implemented, user level providers have ready access via iommu_group_for_each_dev and group notifiers. iommu_device_group() is removed here as it has no users. The replacement is: group = iommu_group_get(dev); id = iommu_group_id(group); iommu_group_put(group); AMD-Vi & Intel VT-d support re-added in following patches. Signed-off-by: Alex Williamson Acked-by: Benjamin Herrenschmidt Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 21 -- drivers/iommu/intel-iommu.c | 49 ---- drivers/iommu/iommu.c | 578 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 548 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index a2e418cba0ff..55283d6291c8 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -3227,26 +3227,6 @@ static int amd_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -static int amd_iommu_device_group(struct device *dev, unsigned int *groupid) -{ - struct iommu_dev_data *dev_data = dev->archdata.iommu; - struct pci_dev *pdev = to_pci_dev(dev); - u16 devid; - - if (!dev_data) - return -ENODEV; - - if (pdev->is_virtfn || !iommu_group_mf) - devid = dev_data->devid; - else - devid = calc_devid(pdev->bus->number, - PCI_DEVFN(PCI_SLOT(pdev->devfn), 0)); - - *groupid = amd_iommu_alias_table[devid]; - - return 0; -} - static struct iommu_ops amd_iommu_ops = { .domain_init = amd_iommu_domain_init, .domain_destroy = amd_iommu_domain_destroy, @@ -3256,7 +3236,6 @@ static struct iommu_ops amd_iommu_ops = { .unmap = amd_iommu_unmap, .iova_to_phys = amd_iommu_iova_to_phys, .domain_has_cap = amd_iommu_domain_has_cap, - .device_group = amd_iommu_device_group, .pgsize_bitmap = AMD_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index b12af2ff8c54..c62f2df25221 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4090,54 +4090,6 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } -/* - * Group numbers are arbitrary. Device with the same group number - * indicate the iommu cannot differentiate between them. To avoid - * tracking used groups we just use the seg|bus|devfn of the lowest - * level we're able to differentiate devices - */ -static int intel_iommu_device_group(struct device *dev, unsigned int *groupid) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct pci_dev *bridge; - union { - struct { - u8 devfn; - u8 bus; - u16 segment; - } pci; - u32 group; - } id; - - if (iommu_no_mapping(dev)) - return -ENODEV; - - id.pci.segment = pci_domain_nr(pdev->bus); - id.pci.bus = pdev->bus->number; - id.pci.devfn = pdev->devfn; - - if (!device_to_iommu(id.pci.segment, id.pci.bus, id.pci.devfn)) - return -ENODEV; - - bridge = pci_find_upstream_pcie_bridge(pdev); - if (bridge) { - if (pci_is_pcie(bridge)) { - id.pci.bus = bridge->subordinate->number; - id.pci.devfn = 0; - } else { - id.pci.bus = bridge->bus->number; - id.pci.devfn = bridge->devfn; - } - } - - if (!pdev->is_virtfn && iommu_group_mf) - id.pci.devfn = PCI_DEVFN(PCI_SLOT(id.pci.devfn), 0); - - *groupid = id.group; - - return 0; -} - static struct iommu_ops intel_iommu_ops = { .domain_init = intel_iommu_domain_init, .domain_destroy = intel_iommu_domain_destroy, @@ -4147,7 +4099,6 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap, .iova_to_phys = intel_iommu_iova_to_phys, .domain_has_cap = intel_iommu_domain_has_cap, - .device_group = intel_iommu_device_group, .pgsize_bitmap = INTEL_IOMMU_PGSIZES, }; diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8b9ded88e6f5..0e928acd7dcf 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -26,60 +26,535 @@ #include #include #include +#include +#include +#include + +static struct kset *iommu_group_kset; +static struct ida iommu_group_ida; +static struct mutex iommu_group_mutex; + +struct iommu_group { + struct kobject kobj; + struct kobject *devices_kobj; + struct list_head devices; + struct mutex mutex; + struct blocking_notifier_head notifier; + void *iommu_data; + void (*iommu_data_release)(void *iommu_data); + char *name; + int id; +}; + +struct iommu_device { + struct list_head list; + struct device *dev; + char *name; +}; + +struct iommu_group_attribute { + struct attribute attr; + ssize_t (*show)(struct iommu_group *group, char *buf); + ssize_t (*store)(struct iommu_group *group, + const char *buf, size_t count); +}; + +#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \ +struct iommu_group_attribute iommu_group_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) -static ssize_t show_iommu_group(struct device *dev, - struct device_attribute *attr, char *buf) +#define to_iommu_group_attr(_attr) \ + container_of(_attr, struct iommu_group_attribute, attr) +#define to_iommu_group(_kobj) \ + container_of(_kobj, struct iommu_group, kobj) + +static ssize_t iommu_group_attr_show(struct kobject *kobj, + struct attribute *__attr, char *buf) { - unsigned int groupid; + struct iommu_group_attribute *attr = to_iommu_group_attr(__attr); + struct iommu_group *group = to_iommu_group(kobj); + ssize_t ret = -EIO; - if (iommu_device_group(dev, &groupid)) - return 0; + if (attr->show) + ret = attr->show(group, buf); + return ret; +} + +static ssize_t iommu_group_attr_store(struct kobject *kobj, + struct attribute *__attr, + const char *buf, size_t count) +{ + struct iommu_group_attribute *attr = to_iommu_group_attr(__attr); + struct iommu_group *group = to_iommu_group(kobj); + ssize_t ret = -EIO; - return sprintf(buf, "%u", groupid); + if (attr->store) + ret = attr->store(group, buf, count); + return ret; } -static DEVICE_ATTR(iommu_group, S_IRUGO, show_iommu_group, NULL); -static int add_iommu_group(struct device *dev, void *data) +static const struct sysfs_ops iommu_group_sysfs_ops = { + .show = iommu_group_attr_show, + .store = iommu_group_attr_store, +}; + +static int iommu_group_create_file(struct iommu_group *group, + struct iommu_group_attribute *attr) +{ + return sysfs_create_file(&group->kobj, &attr->attr); +} + +static void iommu_group_remove_file(struct iommu_group *group, + struct iommu_group_attribute *attr) +{ + sysfs_remove_file(&group->kobj, &attr->attr); +} + +static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf) +{ + return sprintf(buf, "%s\n", group->name); +} + +static IOMMU_GROUP_ATTR(name, S_IRUGO, iommu_group_show_name, NULL); + +static void iommu_group_release(struct kobject *kobj) +{ + struct iommu_group *group = to_iommu_group(kobj); + + if (group->iommu_data_release) + group->iommu_data_release(group->iommu_data); + + mutex_lock(&iommu_group_mutex); + ida_remove(&iommu_group_ida, group->id); + mutex_unlock(&iommu_group_mutex); + + kfree(group->name); + kfree(group); +} + +static struct kobj_type iommu_group_ktype = { + .sysfs_ops = &iommu_group_sysfs_ops, + .release = iommu_group_release, +}; + +/** + * iommu_group_alloc - Allocate a new group + * @name: Optional name to associate with group, visible in sysfs + * + * This function is called by an iommu driver to allocate a new iommu + * group. The iommu group represents the minimum granularity of the iommu. + * Upon successful return, the caller holds a reference to the supplied + * group in order to hold the group until devices are added. Use + * iommu_group_put() to release this extra reference count, allowing the + * group to be automatically reclaimed once it has no devices or external + * references. + */ +struct iommu_group *iommu_group_alloc(void) { - unsigned int groupid; + struct iommu_group *group; + int ret; + + group = kzalloc(sizeof(*group), GFP_KERNEL); + if (!group) + return ERR_PTR(-ENOMEM); + + group->kobj.kset = iommu_group_kset; + mutex_init(&group->mutex); + INIT_LIST_HEAD(&group->devices); + BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier); + + mutex_lock(&iommu_group_mutex); + +again: + if (unlikely(0 == ida_pre_get(&iommu_group_ida, GFP_KERNEL))) { + kfree(group); + mutex_unlock(&iommu_group_mutex); + return ERR_PTR(-ENOMEM); + } + + if (-EAGAIN == ida_get_new(&iommu_group_ida, &group->id)) + goto again; + + mutex_unlock(&iommu_group_mutex); - if (iommu_device_group(dev, &groupid) == 0) - return device_create_file(dev, &dev_attr_iommu_group); + ret = kobject_init_and_add(&group->kobj, &iommu_group_ktype, + NULL, "%d", group->id); + if (ret) { + mutex_lock(&iommu_group_mutex); + ida_remove(&iommu_group_ida, group->id); + mutex_unlock(&iommu_group_mutex); + kfree(group); + return ERR_PTR(ret); + } + + group->devices_kobj = kobject_create_and_add("devices", &group->kobj); + if (!group->devices_kobj) { + kobject_put(&group->kobj); /* triggers .release & free */ + return ERR_PTR(-ENOMEM); + } + + /* + * The devices_kobj holds a reference on the group kobject, so + * as long as that exists so will the group. We can therefore + * use the devices_kobj for reference counting. + */ + kobject_put(&group->kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_alloc); + +/** + * iommu_group_get_iommudata - retrieve iommu_data registered for a group + * @group: the group + * + * iommu drivers can store data in the group for use when doing iommu + * operations. This function provides a way to retrieve it. Caller + * should hold a group reference. + */ +void *iommu_group_get_iommudata(struct iommu_group *group) +{ + return group->iommu_data; +} +EXPORT_SYMBOL_GPL(iommu_group_get_iommudata); + +/** + * iommu_group_set_iommudata - set iommu_data for a group + * @group: the group + * @iommu_data: new data + * @release: release function for iommu_data + * + * iommu drivers can store data in the group for use when doing iommu + * operations. This function provides a way to set the data after + * the group has been allocated. Caller should hold a group reference. + */ +void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data, + void (*release)(void *iommu_data)) +{ + group->iommu_data = iommu_data; + group->iommu_data_release = release; +} +EXPORT_SYMBOL_GPL(iommu_group_set_iommudata); + +/** + * iommu_group_set_name - set name for a group + * @group: the group + * @name: name + * + * Allow iommu driver to set a name for a group. When set it will + * appear in a name attribute file under the group in sysfs. + */ +int iommu_group_set_name(struct iommu_group *group, const char *name) +{ + int ret; + + if (group->name) { + iommu_group_remove_file(group, &iommu_group_attr_name); + kfree(group->name); + group->name = NULL; + if (!name) + return 0; + } + + group->name = kstrdup(name, GFP_KERNEL); + if (!group->name) + return -ENOMEM; + + ret = iommu_group_create_file(group, &iommu_group_attr_name); + if (ret) { + kfree(group->name); + group->name = NULL; + return ret; + } return 0; } +EXPORT_SYMBOL_GPL(iommu_group_set_name); -static int remove_iommu_group(struct device *dev) +/** + * iommu_group_add_device - add a device to an iommu group + * @group: the group into which to add the device (reference should be held) + * @dev: the device + * + * This function is called by an iommu driver to add a device into a + * group. Adding a device increments the group reference count. + */ +int iommu_group_add_device(struct iommu_group *group, struct device *dev) { - unsigned int groupid; + int ret, i = 0; + struct iommu_device *device; + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + device->dev = dev; - if (iommu_device_group(dev, &groupid) == 0) - device_remove_file(dev, &dev_attr_iommu_group); + ret = sysfs_create_link(&dev->kobj, &group->kobj, "iommu_group"); + if (ret) { + kfree(device); + return ret; + } + + device->name = kasprintf(GFP_KERNEL, "%s", kobject_name(&dev->kobj)); +rename: + if (!device->name) { + sysfs_remove_link(&dev->kobj, "iommu_group"); + kfree(device); + return -ENOMEM; + } + ret = sysfs_create_link_nowarn(group->devices_kobj, + &dev->kobj, device->name); + if (ret) { + kfree(device->name); + if (ret == -EEXIST && i >= 0) { + /* + * Account for the slim chance of collision + * and append an instance to the name. + */ + device->name = kasprintf(GFP_KERNEL, "%s.%d", + kobject_name(&dev->kobj), i++); + goto rename; + } + + sysfs_remove_link(&dev->kobj, "iommu_group"); + kfree(device); + return ret; + } + + kobject_get(group->devices_kobj); + + dev->iommu_group = group; + + mutex_lock(&group->mutex); + list_add_tail(&device->list, &group->devices); + mutex_unlock(&group->mutex); + + /* Notify any listeners about change to group. */ + blocking_notifier_call_chain(&group->notifier, + IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev); return 0; } +EXPORT_SYMBOL_GPL(iommu_group_add_device); -static int iommu_device_notifier(struct notifier_block *nb, - unsigned long action, void *data) +/** + * iommu_group_remove_device - remove a device from it's current group + * @dev: device to be removed + * + * This function is called by an iommu driver to remove the device from + * it's current group. This decrements the iommu group reference count. + */ +void iommu_group_remove_device(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + struct iommu_device *tmp_device, *device = NULL; + + /* Pre-notify listeners that a device is being removed. */ + blocking_notifier_call_chain(&group->notifier, + IOMMU_GROUP_NOTIFY_DEL_DEVICE, dev); + + mutex_lock(&group->mutex); + list_for_each_entry(tmp_device, &group->devices, list) { + if (tmp_device->dev == dev) { + device = tmp_device; + list_del(&device->list); + break; + } + } + mutex_unlock(&group->mutex); + + if (!device) + return; + + sysfs_remove_link(group->devices_kobj, device->name); + sysfs_remove_link(&dev->kobj, "iommu_group"); + + kfree(device->name); + kfree(device); + dev->iommu_group = NULL; + kobject_put(group->devices_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_remove_device); + +/** + * iommu_group_for_each_dev - iterate over each device in the group + * @group: the group + * @data: caller opaque data to be passed to callback function + * @fn: caller supplied callback function + * + * This function is called by group users to iterate over group devices. + * Callers should hold a reference count to the group during callback. + * The group->mutex is held across callbacks, which will block calls to + * iommu_group_add/remove_device. + */ +int iommu_group_for_each_dev(struct iommu_group *group, void *data, + int (*fn)(struct device *, void *)) +{ + struct iommu_device *device; + int ret = 0; + + mutex_lock(&group->mutex); + list_for_each_entry(device, &group->devices, list) { + ret = fn(device->dev, data); + if (ret) + break; + } + mutex_unlock(&group->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(iommu_group_for_each_dev); + +/** + * iommu_group_get - Return the group for a device and increment reference + * @dev: get the group that this device belongs to + * + * This function is called by iommu drivers and users to get the group + * for the specified device. If found, the group is returned and the group + * reference in incremented, else NULL. + */ +struct iommu_group *iommu_group_get(struct device *dev) +{ + struct iommu_group *group = dev->iommu_group; + + if (group) + kobject_get(group->devices_kobj); + + return group; +} +EXPORT_SYMBOL_GPL(iommu_group_get); + +/** + * iommu_group_put - Decrement group reference + * @group: the group to use + * + * This function is called by iommu drivers and users to release the + * iommu group. Once the reference count is zero, the group is released. + */ +void iommu_group_put(struct iommu_group *group) +{ + if (group) + kobject_put(group->devices_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_put); + +/** + * iommu_group_register_notifier - Register a notifier for group changes + * @group: the group to watch + * @nb: notifier block to signal + * + * This function allows iommu group users to track changes in a group. + * See include/linux/iommu.h for actions sent via this notifier. Caller + * should hold a reference to the group throughout notifier registration. + */ +int iommu_group_register_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&group->notifier, nb); +} +EXPORT_SYMBOL_GPL(iommu_group_register_notifier); + +/** + * iommu_group_unregister_notifier - Unregister a notifier + * @group: the group to watch + * @nb: notifier block to signal + * + * Unregister a previously registered group notifier block. + */ +int iommu_group_unregister_notifier(struct iommu_group *group, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&group->notifier, nb); +} +EXPORT_SYMBOL_GPL(iommu_group_unregister_notifier); + +/** + * iommu_group_id - Return ID for a group + * @group: the group to ID + * + * Return the unique ID for the group matching the sysfs group number. + */ +int iommu_group_id(struct iommu_group *group) +{ + return group->id; +} +EXPORT_SYMBOL_GPL(iommu_group_id); + +static int add_iommu_group(struct device *dev, void *data) +{ + struct iommu_ops *ops = data; + + if (!ops->add_device) + return -ENODEV; + + WARN_ON(dev->iommu_group); + + ops->add_device(dev); + + return 0; +} + +static int iommu_bus_notifier(struct notifier_block *nb, + unsigned long action, void *data) { struct device *dev = data; + struct iommu_ops *ops = dev->bus->iommu_ops; + struct iommu_group *group; + unsigned long group_action = 0; + + /* + * ADD/DEL call into iommu driver ops if provided, which may + * result in ADD/DEL notifiers to group->notifier + */ + if (action == BUS_NOTIFY_ADD_DEVICE) { + if (ops->add_device) + return ops->add_device(dev); + } else if (action == BUS_NOTIFY_DEL_DEVICE) { + if (ops->remove_device && dev->iommu_group) { + ops->remove_device(dev); + return 0; + } + } - if (action == BUS_NOTIFY_ADD_DEVICE) - return add_iommu_group(dev, NULL); - else if (action == BUS_NOTIFY_DEL_DEVICE) - return remove_iommu_group(dev); + /* + * Remaining BUS_NOTIFYs get filtered and republished to the + * group, if anyone is listening + */ + group = iommu_group_get(dev); + if (!group) + return 0; + switch (action) { + case BUS_NOTIFY_BIND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_BIND_DRIVER; + break; + case BUS_NOTIFY_BOUND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_BOUND_DRIVER; + break; + case BUS_NOTIFY_UNBIND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_UNBIND_DRIVER; + break; + case BUS_NOTIFY_UNBOUND_DRIVER: + group_action = IOMMU_GROUP_NOTIFY_UNBOUND_DRIVER; + break; + } + + if (group_action) + blocking_notifier_call_chain(&group->notifier, + group_action, dev); + + iommu_group_put(group); return 0; } -static struct notifier_block iommu_device_nb = { - .notifier_call = iommu_device_notifier, +static struct notifier_block iommu_bus_nb = { + .notifier_call = iommu_bus_notifier, }; static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) { - bus_register_notifier(bus, &iommu_device_nb); - bus_for_each_dev(bus, NULL, NULL, add_iommu_group); + bus_register_notifier(bus, &iommu_bus_nb); + bus_for_each_dev(bus, NULL, ops, add_iommu_group); } /** @@ -192,6 +667,45 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev) } EXPORT_SYMBOL_GPL(iommu_detach_device); +/* + * IOMMU groups are really the natrual working unit of the IOMMU, but + * the IOMMU API works on domains and devices. Bridge that gap by + * iterating over the devices in a group. Ideally we'd have a single + * device which represents the requestor ID of the group, but we also + * allow IOMMU drivers to create policy defined minimum sets, where + * the physical hardware may be able to distiguish members, but we + * wish to group them at a higher level (ex. untrusted multi-function + * PCI devices). Thus we attach each device. + */ +static int iommu_group_do_attach_device(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + + return iommu_attach_device(domain, dev); +} + +int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + return iommu_group_for_each_dev(group, domain, + iommu_group_do_attach_device); +} +EXPORT_SYMBOL_GPL(iommu_attach_group); + +static int iommu_group_do_detach_device(struct device *dev, void *data) +{ + struct iommu_domain *domain = data; + + iommu_detach_device(domain, dev); + + return 0; +} + +void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group) +{ + iommu_group_for_each_dev(group, domain, iommu_group_do_detach_device); +} +EXPORT_SYMBOL_GPL(iommu_detach_group); + phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, unsigned long iova) { @@ -336,11 +850,15 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size) } EXPORT_SYMBOL_GPL(iommu_unmap); -int iommu_device_group(struct device *dev, unsigned int *groupid) +static int __init iommu_init(void) { - if (iommu_present(dev->bus) && dev->bus->iommu_ops->device_group) - return dev->bus->iommu_ops->device_group(dev, groupid); + iommu_group_kset = kset_create_and_add("iommu_groups", + NULL, kernel_kobj); + ida_init(&iommu_group_ida); + mutex_init(&iommu_group_mutex); - return -ENODEV; + BUG_ON(!iommu_group_kset); + + return 0; } -EXPORT_SYMBOL_GPL(iommu_device_group); +subsys_initcall(iommu_init); -- cgit v1.2.3 From 9dcd61303af862c279df86aa97fde7ce371be774 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:19:07 -0600 Subject: amd_iommu: Support IOMMU groups Add IOMMU group support to AMD-Vi device init and uninit code. Existing notifiers make sure this gets called for each device. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 55283d6291c8..60ea92065d31 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -256,9 +256,11 @@ static bool check_device(struct device *dev) static int iommu_init_device(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); + struct pci_dev *dma_pdev, *pdev = to_pci_dev(dev); struct iommu_dev_data *dev_data; + struct iommu_group *group; u16 alias; + int ret; if (dev->archdata.iommu) return 0; @@ -279,8 +281,26 @@ static int iommu_init_device(struct device *dev) return -ENOTSUPP; } dev_data->alias_data = alias_data; + + dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); + } else + dma_pdev = pci_dev_get(pdev); + + group = iommu_group_get(&dma_pdev->dev); + pci_dev_put(dma_pdev); + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) + return PTR_ERR(group); } + ret = iommu_group_add_device(group, dev); + + iommu_group_put(group); + + if (ret) + return ret; + if (pci_iommuv2_capable(pdev)) { struct amd_iommu *iommu; @@ -309,6 +329,8 @@ static void iommu_ignore_device(struct device *dev) static void iommu_uninit_device(struct device *dev) { + iommu_group_remove_device(dev); + /* * Nothing to do here - we keep dev_data around for unplugged devices * and reuse it when the device is re-plugged - not doing so would -- cgit v1.2.3 From abdfdde2534c48d7a761fc437ad3d840a5947dbc Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:19:19 -0600 Subject: intel-iommu: Support IOMMU groups Add IOMMU group support to Intel VT-d code. This driver sets up devices ondemand, so make use of the add_device/remove_device callbacks in IOMMU API to manage setting up the groups. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c62f2df25221..4a434528cd1c 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4090,6 +4090,47 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } +static int intel_iommu_add_device(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_dev *bridge, *dma_pdev; + struct iommu_group *group; + int ret; + + if (!device_to_iommu(pci_domain_nr(pdev->bus), + pdev->bus->number, pdev->devfn)) + return -ENODEV; + + bridge = pci_find_upstream_pcie_bridge(pdev); + if (bridge) { + if (pci_is_pcie(bridge)) + dma_pdev = pci_get_domain_bus_and_slot( + pci_domain_nr(pdev->bus), + bridge->subordinate->number, 0); + else + dma_pdev = pci_dev_get(bridge); + } else + dma_pdev = pci_dev_get(pdev); + + group = iommu_group_get(&dma_pdev->dev); + pci_dev_put(dma_pdev); + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) + return PTR_ERR(group); + } + + ret = iommu_group_add_device(group, dev); + + iommu_group_put(group); + return ret; +} + +static void intel_iommu_remove_device(struct device *dev) +{ + iommu_group_remove_device(dev); +} + static struct iommu_ops intel_iommu_ops = { .domain_init = intel_iommu_domain_init, .domain_destroy = intel_iommu_domain_destroy, @@ -4099,6 +4140,8 @@ static struct iommu_ops intel_iommu_ops = { .unmap = intel_iommu_unmap, .iova_to_phys = intel_iommu_iova_to_phys, .domain_has_cap = intel_iommu_domain_has_cap, + .add_device = intel_iommu_add_device, + .remove_device = intel_iommu_remove_device, .pgsize_bitmap = INTEL_IOMMU_PGSIZES, }; -- cgit v1.2.3 From 664b600331128146d8ddd3e004defe23ba5c47dc Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:19:31 -0600 Subject: amd_iommu: Make use of DMA quirks and ACS checks in IOMMU groups Work around broken devices and adhere to ACS support when determining IOMMU grouping. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 60ea92065d31..49172393d6ec 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -254,6 +254,14 @@ static bool check_device(struct device *dev) return true; } +static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) +{ + pci_dev_put(*from); + *from = to; +} + +#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) + static int iommu_init_device(struct device *dev) { struct pci_dev *dma_pdev, *pdev = to_pci_dev(dev); @@ -286,6 +294,23 @@ static int iommu_init_device(struct device *dev) } else dma_pdev = pci_dev_get(pdev); + swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); + + if (dma_pdev->multifunction && + !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) + swap_pci_ref(&dma_pdev, + pci_get_slot(dma_pdev->bus, + PCI_DEVFN(PCI_SLOT(dma_pdev->devfn), + 0))); + + while (!pci_is_root_bus(dma_pdev->bus)) { + if (pci_acs_path_enabled(dma_pdev->bus->self, + NULL, REQ_ACS_FLAGS)) + break; + + swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self)); + } + group = iommu_group_get(&dma_pdev->dev); pci_dev_put(dma_pdev); if (!group) { -- cgit v1.2.3 From 783f157bc5a7fa30ee17b4099b27146bd1b68af4 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 30 May 2012 14:19:43 -0600 Subject: intel-iommu: Make use of DMA quirks and ACS checks in IOMMU groups Work around broken devices and adhere to ACS support when determining IOMMU grouping. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 4a434528cd1c..ebf2b310cdde 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4090,6 +4090,14 @@ static int intel_iommu_domain_has_cap(struct iommu_domain *domain, return 0; } +static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) +{ + pci_dev_put(*from); + *from = to; +} + +#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) + static int intel_iommu_add_device(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -4112,6 +4120,23 @@ static int intel_iommu_add_device(struct device *dev) } else dma_pdev = pci_dev_get(pdev); + swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); + + if (dma_pdev->multifunction && + !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)) + swap_pci_ref(&dma_pdev, + pci_get_slot(dma_pdev->bus, + PCI_DEVFN(PCI_SLOT(dma_pdev->devfn), + 0))); + + while (!pci_is_root_bus(dma_pdev->bus)) { + if (pci_acs_path_enabled(dma_pdev->bus->self, + NULL, REQ_ACS_FLAGS)) + break; + + swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self)); + } + group = iommu_group_get(&dma_pdev->dev); pci_dev_put(dma_pdev); if (!group) { -- cgit v1.2.3 From 4e0ee78f2af96676c9dca898c13250f62c513058 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:54 +0300 Subject: iommu: Add DMA window parser, of_get_dma_window() This code was based on: "arch/microblaze/kernel/prom_parse.c" "arch/powerpc/kernel/prom_parse.c" Can replace "of_parse_dma_window()" in the above. This supports different formats flexibly. "prefix" can be configured if any. "busno" and "index" are optionally specified. Set NULL and 0 if not used. Signed-off-by: Hiroshi DOYU Acked-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 4 +++ drivers/iommu/Makefile | 1 + drivers/iommu/of_iommu.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 drivers/iommu/of_iommu.c (limited to 'drivers') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 340893727538..4826af62a9de 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -13,6 +13,10 @@ menuconfig IOMMU_SUPPORT if IOMMU_SUPPORT +config OF_IOMMU + def_bool y + depends on OF + # MSM IOMMU support config MSM_IOMMU bool "MSM IOMMU Support" diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile index 76e54ef796de..14a4d5fc94fa 100644 --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_IOMMU_API) += iommu.o +obj-$(CONFIG_OF_IOMMU) += of_iommu.o obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c new file mode 100644 index 000000000000..ee249bc959f8 --- /dev/null +++ b/drivers/iommu/of_iommu.c @@ -0,0 +1,90 @@ +/* + * OF helpers for IOMMU + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +/** + * of_get_dma_window - Parse *dma-window property and returns 0 if found. + * + * @dn: device node + * @prefix: prefix for property name if any + * @index: index to start to parse + * @busno: Returns busno if supported. Otherwise pass NULL + * @addr: Returns address that DMA starts + * @size: Returns the range that DMA can handle + * + * This supports different formats flexibly. "prefix" can be + * configured if any. "busno" and "index" are optionally + * specified. Set 0(or NULL) if not used. + */ +int of_get_dma_window(struct device_node *dn, const char *prefix, int index, + unsigned long *busno, dma_addr_t *addr, size_t *size) +{ + const __be32 *dma_window, *end; + int bytes, cur_index = 0; + char propname[NAME_MAX], addrname[NAME_MAX], sizename[NAME_MAX]; + + if (!dn || !addr || !size) + return -EINVAL; + + if (!prefix) + prefix = ""; + + snprintf(propname, sizeof(propname), "%sdma-window", prefix); + snprintf(addrname, sizeof(addrname), "%s#dma-address-cells", prefix); + snprintf(sizename, sizeof(sizename), "%s#dma-size-cells", prefix); + + dma_window = of_get_property(dn, propname, &bytes); + if (!dma_window) + return -ENODEV; + end = dma_window + bytes / sizeof(*dma_window); + + while (dma_window < end) { + u32 cells; + const void *prop; + + /* busno is one cell if supported */ + if (busno) + *busno = be32_to_cpup(dma_window++); + + prop = of_get_property(dn, addrname, NULL); + if (!prop) + prop = of_get_property(dn, "#address-cells", NULL); + + cells = prop ? be32_to_cpup(prop) : of_n_addr_cells(dn); + if (!cells) + return -EINVAL; + *addr = of_read_number(dma_window, cells); + dma_window += cells; + + prop = of_get_property(dn, sizename, NULL); + cells = prop ? be32_to_cpup(prop) : of_n_size_cells(dn); + if (!cells) + return -EINVAL; + *size = of_read_number(dma_window, cells); + dma_window += cells; + + if (cur_index++ == index) + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(of_get_dma_window); -- cgit v1.2.3 From 0760e8faa960f8ee991fa4acb802db4e20661281 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:55 +0300 Subject: iommu/tegra: smmu: Add device tree support for SMMU The necessary info is expected to pass from DT. For more precise resource reservation, there shouldn't be any overlapping of register range between SMMU and MC. SMMU register offset needs to be calculated correctly, based on its register bank. Signed-off-by: Hiroshi DOYU Acked-by: Stephen Warren Acked-by: Grant Likely Signed-off-by: Joerg Roedel --- drivers/iommu/Kconfig | 2 +- drivers/iommu/tegra-smmu.c | 149 +++++++++++++++++++++++++++++---------------- 2 files changed, 96 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index 4826af62a9de..9f69b561f5db 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -158,7 +158,7 @@ config TEGRA_IOMMU_GART config TEGRA_IOMMU_SMMU bool "Tegra SMMU IOMMU Support" - depends on ARCH_TEGRA_3x_SOC + depends on ARCH_TEGRA_3x_SOC && TEGRA_AHB select IOMMU_API help Enables support for remapping discontiguous physical memory diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index ecd679043d77..2c92b8c3514e 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -30,12 +30,15 @@ #include #include #include +#include +#include #include #include #include #include +#include /* bitmap of the page sizes currently supported */ #define SMMU_IOMMU_PGSIZES (SZ_4K) @@ -111,12 +114,6 @@ #define SMMU_PDE_NEXT_SHIFT 28 -/* AHB Arbiter Registers */ -#define AHB_XBAR_CTRL 0xe0 -#define AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE 1 -#define AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT 17 - -#define SMMU_NUM_ASIDS 4 #define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000 #define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */ #define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000 @@ -136,6 +133,7 @@ #define SMMU_PAGE_SHIFT 12 #define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT) +#define SMMU_PAGE_MASK ((1 << SMMU_PAGE_SHIFT) - 1) #define SMMU_PDIR_COUNT 1024 #define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT) @@ -177,6 +175,8 @@ #define SMMU_ASID_DISABLE 0 #define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0)) +#define NUM_SMMU_REG_BANKS 3 + #define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1) #define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0) #define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1) @@ -235,7 +235,7 @@ struct smmu_as { * Per SMMU device - IOMMU device */ struct smmu_device { - void __iomem *regs, *regs_ahbarb; + void __iomem *regs[NUM_SMMU_REG_BANKS]; unsigned long iovmm_base; /* remappable base address */ unsigned long page_count; /* total remappable size */ spinlock_t lock; @@ -252,29 +252,47 @@ struct smmu_device { unsigned long translation_enable_1; unsigned long translation_enable_2; unsigned long asid_security; + + struct device_node *ahb; }; static struct smmu_device *smmu_handle; /* unique for a system */ /* - * SMMU/AHB register accessors + * SMMU register accessors */ static inline u32 smmu_read(struct smmu_device *smmu, size_t offs) { - return readl(smmu->regs + offs); -} -static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) -{ - writel(val, smmu->regs + offs); + BUG_ON(offs < 0x10); + if (offs < 0x3c) + return readl(smmu->regs[0] + offs - 0x10); + BUG_ON(offs < 0x1f0); + if (offs < 0x200) + return readl(smmu->regs[1] + offs - 0x1f0); + BUG_ON(offs < 0x228); + if (offs < 0x284) + return readl(smmu->regs[2] + offs - 0x228); + BUG(); } -static inline u32 ahb_read(struct smmu_device *smmu, size_t offs) -{ - return readl(smmu->regs_ahbarb + offs); -} -static inline void ahb_write(struct smmu_device *smmu, u32 val, size_t offs) +static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs) { - writel(val, smmu->regs_ahbarb + offs); + BUG_ON(offs < 0x10); + if (offs < 0x3c) { + writel(val, smmu->regs[0] + offs - 0x10); + return; + } + BUG_ON(offs < 0x1f0); + if (offs < 0x200) { + writel(val, smmu->regs[1] + offs - 0x1f0); + return; + } + BUG_ON(offs < 0x228); + if (offs < 0x284) { + writel(val, smmu->regs[2] + offs - 0x228); + return; + } + BUG(); } #define VA_PAGE_TO_PA(va, page) \ @@ -370,7 +388,7 @@ static void smmu_flush_regs(struct smmu_device *smmu, int enable) FLUSH_SMMU_REGS(smmu); } -static void smmu_setup_regs(struct smmu_device *smmu) +static int smmu_setup_regs(struct smmu_device *smmu) { int i; u32 val; @@ -398,10 +416,7 @@ static void smmu_setup_regs(struct smmu_device *smmu) smmu_flush_regs(smmu, 1); - val = ahb_read(smmu, AHB_XBAR_CTRL); - val |= AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE << - AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT; - ahb_write(smmu, val, AHB_XBAR_CTRL); + return tegra_ahb_enable_smmu(smmu->ahb); } static void flush_ptc_and_tlb(struct smmu_device *smmu, @@ -873,52 +888,72 @@ static int tegra_smmu_resume(struct device *dev) { struct smmu_device *smmu = dev_get_drvdata(dev); unsigned long flags; + int err; spin_lock_irqsave(&smmu->lock, flags); - smmu_setup_regs(smmu); + err = smmu_setup_regs(smmu); spin_unlock_irqrestore(&smmu->lock, flags); - return 0; + return err; } static int tegra_smmu_probe(struct platform_device *pdev) { struct smmu_device *smmu; - struct resource *regs, *regs2, *window; struct device *dev = &pdev->dev; - int i, err = 0; + int i, asids, err = 0; + dma_addr_t base; + size_t size; + const void *prop; if (smmu_handle) return -EIO; BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - window = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (!regs || !regs2 || !window) { - dev_err(dev, "No SMMU resources\n"); - return -ENODEV; - } - smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); if (!smmu) { dev_err(dev, "failed to allocate smmu_device\n"); return -ENOMEM; } - smmu->dev = dev; - smmu->num_as = SMMU_NUM_ASIDS; - smmu->iovmm_base = (unsigned long)window->start; - smmu->page_count = resource_size(window) >> SMMU_PAGE_SHIFT; - smmu->regs = devm_ioremap(dev, regs->start, resource_size(regs)); - smmu->regs_ahbarb = devm_ioremap(dev, regs2->start, - resource_size(regs2)); - if (!smmu->regs || !smmu->regs_ahbarb) { - dev_err(dev, "failed to remap SMMU registers\n"); - err = -ENXIO; - goto fail; + for (i = 0; i < ARRAY_SIZE(smmu->regs); i++) { + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) + return -ENODEV; + smmu->regs[i] = devm_request_and_ioremap(&pdev->dev, res); + if (!smmu->regs[i]) + return -EBUSY; } + err = of_get_dma_window(dev->of_node, NULL, 0, NULL, &base, &size); + if (err) + return -ENODEV; + + if (size & SMMU_PAGE_MASK) + return -EINVAL; + + size >>= SMMU_PAGE_SHIFT; + if (!size) + return -EINVAL; + + prop = of_get_property(dev->of_node, "nvidia,#asids", NULL); + if (!prop) + return -ENODEV; + asids = be32_to_cpup(prop); + if (!asids) + return -ENODEV; + + smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); + if (!smmu->ahb) + return -ENODEV; + + smmu->dev = dev; + smmu->num_as = asids; + smmu->iovmm_base = base; + smmu->page_count = size; + smmu->translation_enable_0 = ~0; smmu->translation_enable_1 = ~0; smmu->translation_enable_2 = ~0; @@ -945,7 +980,9 @@ static int tegra_smmu_probe(struct platform_device *pdev) INIT_LIST_HEAD(&as->client); } spin_lock_init(&smmu->lock); - smmu_setup_regs(smmu); + err = smmu_setup_regs(smmu); + if (err) + goto fail; platform_set_drvdata(pdev, smmu); smmu->avp_vector_page = alloc_page(GFP_KERNEL); @@ -958,10 +995,6 @@ static int tegra_smmu_probe(struct platform_device *pdev) fail: if (smmu->avp_vector_page) __free_page(smmu->avp_vector_page); - if (smmu->regs) - devm_iounmap(dev, smmu->regs); - if (smmu->regs_ahbarb) - devm_iounmap(dev, smmu->regs_ahbarb); if (smmu && smmu->as) { for (i = 0; i < smmu->num_as; i++) { if (smmu->as[i].pdir_page) { @@ -993,8 +1026,6 @@ static int tegra_smmu_remove(struct platform_device *pdev) __free_page(smmu->avp_vector_page); if (smmu->regs) devm_iounmap(dev, smmu->regs); - if (smmu->regs_ahbarb) - devm_iounmap(dev, smmu->regs_ahbarb); devm_kfree(dev, smmu); smmu_handle = NULL; return 0; @@ -1005,6 +1036,14 @@ const struct dev_pm_ops tegra_smmu_pm_ops = { .resume = tegra_smmu_resume, }; +#ifdef CONFIG_OF +static struct of_device_id tegra_smmu_of_match[] __devinitdata = { + { .compatible = "nvidia,tegra30-smmu", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_smmu_of_match); +#endif + static struct platform_driver tegra_smmu_driver = { .probe = tegra_smmu_probe, .remove = tegra_smmu_remove, @@ -1012,6 +1051,7 @@ static struct platform_driver tegra_smmu_driver = { .owner = THIS_MODULE, .name = "tegra-smmu", .pm = &tegra_smmu_pm_ops, + .of_match_table = of_match_ptr(tegra_smmu_of_match), }, }; @@ -1031,4 +1071,5 @@ module_exit(tegra_smmu_exit); MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30"); MODULE_AUTHOR("Hiroshi DOYU "); +MODULE_ALIAS("platform:tegra-smmu"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From a3b24915456b63c9002e94152e122b07de5566f2 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:56 +0300 Subject: iommu/tegra: smmu: Simplify allocation at once To simplify the code, alloc necessary data at once. Signed-off-by: Hiroshi DOYU Acked-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 2c92b8c3514e..98fcc7268eaf 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -241,8 +241,6 @@ struct smmu_device { spinlock_t lock; char *name; struct device *dev; - int num_as; - struct smmu_as *as; /* Run-time allocated array */ struct page *avp_vector_page; /* dummy page shared by all AS's */ /* @@ -254,6 +252,9 @@ struct smmu_device { unsigned long asid_security; struct device_node *ahb; + + int num_as; + struct smmu_as as[0]; /* Run-time allocated array */ }; static struct smmu_device *smmu_handle; /* unique for a system */ @@ -902,15 +903,18 @@ static int tegra_smmu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; int i, asids, err = 0; dma_addr_t base; - size_t size; - const void *prop; + size_t bytes, size; if (smmu_handle) return -EIO; BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT); - smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + if (of_property_read_u32(dev->of_node, "nvidia,#asids", &asids)) + return -ENODEV; + + bytes = sizeof(*smmu) + asids * sizeof(*smmu->as); + smmu = devm_kzalloc(dev, bytes, GFP_KERNEL); if (!smmu) { dev_err(dev, "failed to allocate smmu_device\n"); return -ENOMEM; @@ -938,13 +942,6 @@ static int tegra_smmu_probe(struct platform_device *pdev) if (!size) return -EINVAL; - prop = of_get_property(dev->of_node, "nvidia,#asids", NULL); - if (!prop) - return -ENODEV; - asids = be32_to_cpup(prop); - if (!asids) - return -ENODEV; - smmu->ahb = of_parse_phandle(dev->of_node, "nvidia,ahb", 0); if (!smmu->ahb) return -ENODEV; @@ -959,14 +956,6 @@ static int tegra_smmu_probe(struct platform_device *pdev) smmu->translation_enable_2 = ~0; smmu->asid_security = 0; - smmu->as = devm_kzalloc(dev, - sizeof(smmu->as[0]) * smmu->num_as, GFP_KERNEL); - if (!smmu->as) { - dev_err(dev, "failed to allocate smmu_as\n"); - err = -ENOMEM; - goto fail; - } - for (i = 0; i < smmu->num_as; i++) { struct smmu_as *as = &smmu->as[i]; -- cgit v1.2.3 From 0547c2f56e6e4795d5366ba3b8483b668920a947 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:57 +0300 Subject: iommu/tegra: smmu: Remove unnecessary cleanups with devm_*() Remove unnecessary cleanup procedures with devm_*() functions. Signed-off-by: Hiroshi DOYU Acked-by: Stephen Warren Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 98fcc7268eaf..cc6aba45e49a 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -971,51 +971,26 @@ static int tegra_smmu_probe(struct platform_device *pdev) spin_lock_init(&smmu->lock); err = smmu_setup_regs(smmu); if (err) - goto fail; + return err; platform_set_drvdata(pdev, smmu); smmu->avp_vector_page = alloc_page(GFP_KERNEL); if (!smmu->avp_vector_page) - goto fail; + return -ENOMEM; smmu_handle = smmu; return 0; - -fail: - if (smmu->avp_vector_page) - __free_page(smmu->avp_vector_page); - if (smmu && smmu->as) { - for (i = 0; i < smmu->num_as; i++) { - if (smmu->as[i].pdir_page) { - ClearPageReserved(smmu->as[i].pdir_page); - __free_page(smmu->as[i].pdir_page); - } - } - devm_kfree(dev, smmu->as); - } - devm_kfree(dev, smmu); - return err; } static int tegra_smmu_remove(struct platform_device *pdev) { struct smmu_device *smmu = platform_get_drvdata(pdev); - struct device *dev = smmu->dev; + int i; smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG); - platform_set_drvdata(pdev, NULL); - if (smmu->as) { - int i; - - for (i = 0; i < smmu->num_as; i++) - free_pdir(&smmu->as[i]); - devm_kfree(dev, smmu->as); - } - if (smmu->avp_vector_page) - __free_page(smmu->avp_vector_page); - if (smmu->regs) - devm_iounmap(dev, smmu->regs); - devm_kfree(dev, smmu); + for (i = 0; i < smmu->num_as; i++) + free_pdir(&smmu->as[i]); + __free_page(smmu->avp_vector_page); smmu_handle = NULL; return 0; } -- cgit v1.2.3 From ff7636292aac6cae2ee3a553d5424ea49c8a8263 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Mon, 25 Jun 2012 14:23:58 +0300 Subject: iommu/tegra: smmu: Fix uninitialized var warning For the compiler warning, uninitizlized var when getting value by a pointer. Signed-off-by: Hiroshi DOYU Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index cc6aba45e49a..becfc614cf94 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -902,8 +902,8 @@ static int tegra_smmu_probe(struct platform_device *pdev) struct smmu_device *smmu; struct device *dev = &pdev->dev; int i, asids, err = 0; - dma_addr_t base; - size_t bytes, size; + dma_addr_t uninitialized_var(base); + size_t bytes, uninitialized_var(size); if (smmu_handle) return -EIO; -- cgit v1.2.3 From 90e614bb4c581eb588ec26f130fcdd65aa047fa8 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 16 May 2012 11:35:04 -0300 Subject: [media] s5p-fimc: Fix bug in capture node open() When video pipeline initialization fails, the ST_CAPT_BUSY flag needs to be cleared before pm_runtime_put_sync is called. Otherwise the runtime suspend routine tries to suspend device, rather than just turning it off. Also fix potential null pointer dereference in fimc_pipeline_shutdown(). Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 2 +- drivers/media/video/s5p-fimc/fimc-mdevice.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 354574591908..7083107c2b37 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -499,10 +499,10 @@ static int fimc_capture_open(struct file *file) if (ret < 0) { dev_err(&fimc->pdev->dev, "Video pipeline initialization failed\n"); + clear_bit(ST_CAPT_BUSY, &fimc->state); pm_runtime_put_sync(&fimc->pdev->dev); fimc->vid_cap.refcnt--; v4l2_fh_release(file); - clear_bit(ST_CAPT_BUSY, &fimc->state); return ret; } ret = fimc_capture_ctrls_create(fimc); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 6753c45631b8..7450dcdafc87 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -193,9 +193,13 @@ int __fimc_pipeline_shutdown(struct fimc_pipeline *p) int fimc_pipeline_shutdown(struct fimc_pipeline *p) { - struct media_entity *me = &p->subdevs[IDX_SENSOR]->entity; + struct media_entity *me; int ret; + if (!p || !p->subdevs[IDX_SENSOR]) + return -EINVAL; + + me = &p->subdevs[IDX_SENSOR]->entity; mutex_lock(&me->parent->graph_mutex); ret = __fimc_pipeline_shutdown(p); mutex_unlock(&me->parent->graph_mutex); -- cgit v1.2.3 From d0da3c3565a1dd3cdfa374038d6ccae4b90fe142 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 16 May 2012 13:08:30 -0300 Subject: [media] s5p-fimc: Don't create multiple active links to same sink entity The driver is supposed to create active media link from sensor N (or its corresponding s5p-mipi-csis entity) to FIMC.N by default. Instead s5p-mipi-csis.N entity gets always connected by a default active link to FIMC.N, regardless of there are parallel bus sensor entities already connected to FIMC.N. Correct this. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-mdevice.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 7450dcdafc87..dffe4da5eaa0 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -502,12 +502,12 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) * @source: the source entity to create links to all fimc entities from * @sensor: sensor subdev linked to FIMC[fimc_id] entity, may be null * @pad: the source entity pad index - * @fimc_id: index of the fimc device for which link should be enabled + * @link_mask: bitmask of the fimc devices for which link should be enabled */ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, struct media_entity *source, struct v4l2_subdev *sensor, - int pad, int fimc_id) + int pad, int link_mask) { struct fimc_sensor_info *s_info; struct media_entity *sink; @@ -524,7 +524,7 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (!fmd->fimc[i]->variant->has_cam_if) continue; - flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; + flags = ((1 << i) & link_mask) ? MEDIA_LNK_FL_ENABLED : 0; sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, @@ -556,7 +556,10 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, if (!fmd->fimc_lite[i]) continue; - flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; + if (link_mask & (1 << (i + FIMC_MAX_DEVS))) + flags = MEDIA_LNK_FL_ENABLED; + else + flags = 0; sink = &fmd->fimc_lite[i]->subdev.entity; ret = media_entity_create_link(source, pad, sink, @@ -618,9 +621,8 @@ static int fimc_md_create_links(struct fimc_md *fmd) struct s5p_fimc_isp_info *pdata; struct fimc_sensor_info *s_info; struct media_entity *source, *sink; - int i, pad, fimc_id = 0; - int ret = 0; - u32 flags; + int i, pad, fimc_id = 0, ret = 0; + u32 flags, link_mask = 0; for (i = 0; i < fmd->num_sensors; i++) { if (fmd->sensor[i].subdev == NULL) @@ -672,19 +674,20 @@ static int fimc_md_create_links(struct fimc_md *fmd) if (source == NULL) continue; + link_mask = 1 << fimc_id++; ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, - pad, fimc_id++); + pad, link_mask); } - fimc_id = 0; for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { if (fmd->csis[i].sd == NULL) continue; source = &fmd->csis[i].sd->entity; pad = CSIS_PAD_SOURCE; + link_mask = 1 << fimc_id++; ret = __fimc_md_create_fimc_sink_links(fmd, source, NULL, - pad, fimc_id++); + pad, link_mask); } /* Create immutable links between each FIMC's subdev and video node */ -- cgit v1.2.3 From d547ab66e275e2f6bf703572e943018ef7c391c5 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 16 May 2012 15:00:26 -0300 Subject: [media] s5p-fimc: Honour sizeimage in VIDIOC_S_FMT Allow memory buffer size to be increased by means of struct v4l2_pix_plane_format::sizeimage at VIDIOC_S_FMT ioctl. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 7 ++++--- drivers/media/video/s5p-fimc/fimc-core.c | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 7083107c2b37..13df7230c8d8 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -350,7 +350,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, if (pixm) sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); else - sizes[i] = size; + sizes[i] = max_t(u32, size, frame->payload[i]); + allocators[i] = ctx->fimc_dev->alloc_ctx; } @@ -924,10 +925,10 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) pix->width = mf->width; pix->height = mf->height; } + fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix); for (i = 0; i < ff->fmt->colplanes; i++) - ff->payload[i] = - (pix->width * pix->height * ff->fmt->depth[i]) / 8; + ff->payload[i] = pix->plane_fmt[i].sizeimage; set_frame_bounds(ff, pix->width, pix->height); /* Reset the composition rectangle if not yet configured */ diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 92fc5a20fb76..65124a24c30f 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -741,8 +741,8 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, pix->width = width; for (i = 0; i < pix->num_planes; ++i) { - u32 bpl = pix->plane_fmt[i].bytesperline; - u32 *sizeimage = &pix->plane_fmt[i].sizeimage; + struct v4l2_plane_pix_format *plane_fmt = &pix->plane_fmt[i]; + u32 bpl = plane_fmt->bytesperline; if (fmt->colplanes > 1 && (bpl == 0 || bpl < pix->width)) bpl = pix->width; /* Planar */ @@ -754,8 +754,9 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, if (i == 0) /* Same bytesperline for each plane. */ bytesperline = bpl; - pix->plane_fmt[i].bytesperline = bytesperline; - *sizeimage = (pix->width * pix->height * fmt->depth[i]) / 8; + plane_fmt->bytesperline = bytesperline; + plane_fmt->sizeimage = max((pix->width * pix->height * + fmt->depth[i]) / 8, plane_fmt->sizeimage); } } -- cgit v1.2.3 From 0b4b1f199d367762ddb68cb3303431fa6c84a7d6 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Wed, 16 May 2012 15:03:50 -0300 Subject: [media] s5p-fimc: Remove superfluous checks for buffer type The checks are already done at the v4l2 framework. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 13df7230c8d8..0fd12dfbd3db 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -819,9 +819,6 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, struct fimc_dev *fimc = video_drvdata(file); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return -EINVAL; - return fimc_fill_format(&ctx->d_frame, f); } @@ -834,9 +831,6 @@ static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, struct v4l2_mbus_framefmt mf; struct fimc_fmt *ffmt = NULL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return -EINVAL; - if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, @@ -888,8 +882,6 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) struct fimc_fmt *s_fmt = NULL; int ret, i; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return -EINVAL; if (vb2_is_busy(&fimc->vid_cap.vbq)) return -EBUSY; -- cgit v1.2.3 From e3fc82e8b9f550d28f05600b98bcc8c26beef2ef Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 17 May 2012 14:22:10 -0300 Subject: [media] s5p-fimc: Prevent lock-up in multiple sensor systems The camera clocks managed by the driver were improperly reference counted and remained disabled when multiple video nodes were opened simultaneously. It manifested itself with following warning: [12.920000] WARNING: at drivers/media/video/s5p-fimc/fimc-mdevice.c:787 __fimc_md_set_camclk+0x1c0/0x1dc() [13.005000] Modules linked in: [13.005000] Backtrace: [13.040000] [] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c) [13.070000] r7:00000009 r6:00000313 r5:c02d576c r4:00000000 [13.155000] [] (dump_stack+0x0/0x1c) from [] (warn_slowpath_common+0x54/0x6c) [13.285000] [] (warn_slowpath_common+0x0/0x6c) from [] (warn_slowpath_null+0x24/0x2c) [13.360000] r9:e1981010 r8:00000000 r7:c061d3fc r6:e1981010 r5:e1981030 [13.430000] r4:00000000 [13.430000] [] (warn_slowpath_null+0x0/0x2c) from [] (__fimc_md_set_camclk+0x1c0/0x1dc) [13.550000] [] (__fimc_md_set_camclk+0x0/0x1dc) from [] (fimc_md_set_camclk+0x28/0x2c) [13.630000] [] (fimc_md_set_camclk+0x0/0x2c) from [] (__fimc_pipeline_shutdown+0x34/0x50) [13.705000] [] (__fimc_pipeline_shutdown+0x0/0x50) from [] (fimc_pipeline_shutdown+0x40/0x58) [13.765000] r5:e2391200 r4:e2357704 [13.805000] [] (fimc_pipeline_shutdown+0x0/0x58) from [] (fimc_capture_close+0xcc/0xe4) [13.915000] r5:e1b396c0 r4:e2357410 [13.915000] [] (fimc_capture_close+0x0/0xe4) from [] (v4l2_release+0x5c/0x80) [13.970000] r7:00000010 r6:e1d2d990 r5:e1b396c0 r4:e2394800 [14.000000] [] (v4l2_release+0x0/0x80) from [] (fput+0xc0/0x22c) [14.015000] r5:c157ef30 r4:e1b396c0 [14.015000] [] (fput+0x0/0x22c) from [] (filp_close+0x60/0x80) [14.080000] [] (filp_close+0x0/0x80) from [] (sys_close+0xb8/0xf4) [14.125000] r7:00000001 r6:e1b396c0 r5:c1400340 r4:c1400300 [14.125000] [] (sys_close+0x0/0xf4) from [] (ret_fast_syscall+0x0/0x30) [14.205000] r7:00000006 r6:beee5b94 r5:00000003 r4:b6f64fac Fix this, as well as potential memory leaks due to not calling v4l2_fh_release() on some error paths. Also remove some error logs printed for events that aren't critical and are normal conditions for some system configurations. Also check if the device have been properly run-time enabled during video node open. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 46 +++++++++++++++-------------- drivers/media/video/s5p-fimc/fimc-lite.c | 14 +++++---- drivers/media/video/s5p-fimc/fimc-mdevice.c | 19 ++++-------- drivers/media/video/s5p-fimc/fimc-mdevice.h | 2 -- 4 files changed, 38 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 0fd12dfbd3db..71e483847a17 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -480,37 +480,39 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc); static int fimc_capture_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); - int ret = v4l2_fh_open(file); - - if (ret) - return ret; + int ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - /* Return if the corresponding video mem2mem node is already opened. */ if (fimc_m2m_active(fimc)) return -EBUSY; set_bit(ST_CAPT_BUSY, &fimc->state); - pm_runtime_get_sync(&fimc->pdev->dev); - - if (++fimc->vid_cap.refcnt == 1) { - ret = fimc_pipeline_initialize(&fimc->pipeline, - &fimc->vid_cap.vfd->entity, true); - if (ret < 0) { - dev_err(&fimc->pdev->dev, - "Video pipeline initialization failed\n"); - clear_bit(ST_CAPT_BUSY, &fimc->state); - pm_runtime_put_sync(&fimc->pdev->dev); - fimc->vid_cap.refcnt--; - v4l2_fh_release(file); - return ret; - } - ret = fimc_capture_ctrls_create(fimc); + ret = pm_runtime_get_sync(&fimc->pdev->dev); + if (ret < 0) + return ret; + + ret = v4l2_fh_open(file); + if (ret) + return ret; + + if (++fimc->vid_cap.refcnt != 1) + return 0; - if (!ret && !fimc->vid_cap.user_subdev_api) - ret = fimc_capture_set_default_format(fimc); + ret = fimc_pipeline_initialize(&fimc->pipeline, + &fimc->vid_cap.vfd->entity, true); + if (ret < 0) { + clear_bit(ST_CAPT_BUSY, &fimc->state); + pm_runtime_put_sync(&fimc->pdev->dev); + fimc->vid_cap.refcnt--; + v4l2_fh_release(file); + return ret; } + ret = fimc_capture_ctrls_create(fimc); + + if (!ret && !fimc->vid_cap.user_subdev_api) + ret = fimc_capture_set_default_format(fimc); + return ret; } diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 400d701aef04..bbe93e4a8731 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -451,21 +451,23 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) static int fimc_lite_open(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); - int ret = v4l2_fh_open(file); - - if (ret) - return ret; + int ret; set_bit(ST_FLITE_IN_USE, &fimc->state); - pm_runtime_get_sync(&fimc->pdev->dev); + ret = pm_runtime_get_sync(&fimc->pdev->dev); + if (ret < 0) + return ret; if (++fimc->ref_count != 1 || fimc->out_path != FIMC_IO_DMA) + return 0; + + ret = v4l2_fh_open(file); + if (ret < 0) return ret; ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, true); if (ret < 0) { - v4l2_err(fimc->vfd, "Video pipeline initialization failed\n"); pm_runtime_put_sync(&fimc->pdev->dev); fimc->ref_count--; v4l2_fh_release(file); diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index dffe4da5eaa0..52cef4865423 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -741,8 +741,8 @@ static void fimc_md_put_clocks(struct fimc_md *fmd) } static int __fimc_md_set_camclk(struct fimc_md *fmd, - struct fimc_sensor_info *s_info, - bool on) + struct fimc_sensor_info *s_info, + bool on) { struct s5p_fimc_isp_info *pdata = s_info->pdata; struct fimc_camclk_info *camclk; @@ -751,12 +751,10 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, if (WARN_ON(pdata->clk_id >= FIMC_MAX_CAMCLKS) || fmd == NULL) return -EINVAL; - if (s_info->clk_on == on) - return 0; camclk = &fmd->camclk[pdata->clk_id]; - dbg("camclk %d, f: %lu, clk: %p, on: %d", - pdata->clk_id, pdata->clk_frequency, camclk, on); + dbg("camclk %d, f: %lu, use_count: %d, on: %d", + pdata->clk_id, pdata->clk_frequency, camclk->use_count, on); if (on) { if (camclk->use_count > 0 && @@ -767,11 +765,9 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, clk_set_rate(camclk->clock, pdata->clk_frequency); camclk->frequency = pdata->clk_frequency; ret = clk_enable(camclk->clock); + dbg("Enabled camclk %d: f: %lu", pdata->clk_id, + clk_get_rate(camclk->clock)); } - s_info->clk_on = 1; - dbg("Enabled camclk %d: f: %lu", pdata->clk_id, - clk_get_rate(camclk->clock)); - return ret; } @@ -780,7 +776,6 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, if (--camclk->use_count == 0) { clk_disable(camclk->clock); - s_info->clk_on = 0; dbg("Disabled camclk %d", pdata->clk_id); } return ret; @@ -796,8 +791,6 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, * devices to which sensors can be attached, either directly or through * the MIPI CSI receiver. The clock is allowed here to be used by * multiple sensors concurrently if they use same frequency. - * The per sensor subdev clk_on attribute helps to synchronize accesses - * to the sclk_cam clocks from the video and media device nodes. * This function should only be called when the graph mutex is held. */ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h index 3b8a3492a176..1f5dbaff5442 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -47,7 +47,6 @@ struct fimc_camclk_info { * @pdata: sensor's atrributes passed as media device's platform data * @subdev: image sensor v4l2 subdev * @host: fimc device the sensor is currently linked to - * @clk_on: sclk_cam clock's state associated with this subdev * * This data structure applies to image sensor and the writeback subdevs. */ @@ -55,7 +54,6 @@ struct fimc_sensor_info { struct s5p_fimc_isp_info *pdata; struct v4l2_subdev *subdev; struct fimc_dev *host; - bool clk_on; }; /** -- cgit v1.2.3 From 316efab3e949e4fbdacb0975bf33adbad89115db Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 18 May 2012 13:31:28 -0300 Subject: [media] s5p-fimc: Fix fimc-lite system wide suspend procedure Only suspend the video pipeline devices if they were active before the pm.suspend() helper is called. This patch prevents following error: /# echo mem > /sys/power/state [ 34.965000] PM: Syncing filesystems ... done. [ 35.035000] Freezing user space processes ... (elapsed 0.01 seconds) done. ... [ 35.105000] dpm_run_callback(): platform_pm_suspend+0x0/0x5c returns -22 [ 35.105000] PM: Device exynos-fimc-lite.1 failed to suspend: error -22 [ 35.105000] PM: Some devices failed to suspend Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-lite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index bbe93e4a8731..b0a8ce61ebf2 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -1510,7 +1510,7 @@ static int fimc_lite_suspend(struct device *dev) return 0; ret = fimc_lite_stop_capture(fimc, suspend); - if (ret) + if (ret < 0 || !fimc_lite_active(fimc)) return ret; return fimc_pipeline_shutdown(&fimc->pipeline); -- cgit v1.2.3 From 0a198bcd511fcc60dc5daa203d221aafc95e0471 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 22 May 2012 13:50:14 -0300 Subject: [media] s5p-fimc: Shorten pixel formats description Shorten pixel format descriptions that exceed 32 characters so they're not being truncated when queried from user space. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 65124a24c30f..987bff20cc34 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -153,7 +153,7 @@ static struct fimc_fmt fimc_formats[] = { .colplanes = 2, .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr", + .name = "YUV 4:2:0 non-contig. 2p, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV12M, .color = FIMC_FMT_YCBCR420, .depth = { 8, 4 }, @@ -161,7 +161,7 @@ static struct fimc_fmt fimc_formats[] = { .colplanes = 2, .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr", + .name = "YUV 4:2:0 non-contig. 3p, Y/Cb/Cr", .fourcc = V4L2_PIX_FMT_YUV420M, .color = FIMC_FMT_YCBCR420, .depth = { 8, 2, 2 }, @@ -169,7 +169,7 @@ static struct fimc_fmt fimc_formats[] = { .colplanes = 3, .flags = FMT_FLAGS_M2M, }, { - .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled", + .name = "YUV 4:2:0 non-contig. 2p, tiled", .fourcc = V4L2_PIX_FMT_NV12MT, .color = FIMC_FMT_YCBCR420, .depth = { 8, 4 }, -- cgit v1.2.3 From 8183e7a7e2a4b542bd3044c5e3b0e116b0a7d2a1 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Fri, 25 May 2012 07:04:01 -0300 Subject: [media] s5p-fimc: Update to the control handler lock changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 77e7c4e624404c6edb5686b3d5f873c6008ed6b0 "v4l: Allow changing control handler lock" changed the lock field of struct v4l2_ctrl_handler to a pointer and this driver wasn't updated properly. This patch fixes following warning: drivers/media/video/s5p-fimc/fimc-core.c: In function ‘fimc_ctrls_activate’: drivers/media/video/s5p-fimc/fimc-core.c:644: warning: passing argument 1 of ‘mutex_lock’ from incompatible pointer type include/linux/mutex.h:152: note: expected ‘struct mutex *’ but argument is of type ‘struct mutex **’ drivers/media/video/s5p-fimc/fimc-core.c:663: warning: passing argument 1 of ‘mutex_unlock’ from incompatible pointer type include/linux/mutex.h:169: note: expected ‘struct mutex *’ but argument is of type ‘struct mutex **’ Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 987bff20cc34..a4646ca1d56f 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -641,7 +641,7 @@ void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active) if (!ctrls->ready) return; - mutex_lock(&ctrls->handler.lock); + mutex_lock(ctrls->handler.lock); v4l2_ctrl_activate(ctrls->rotate, active); v4l2_ctrl_activate(ctrls->hflip, active); v4l2_ctrl_activate(ctrls->vflip, active); @@ -660,7 +660,7 @@ void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active) ctx->hflip = 0; ctx->vflip = 0; } - mutex_unlock(&ctrls->handler.lock); + mutex_unlock(ctrls->handler.lock); } /* Update maximum value of the alpha color control */ -- cgit v1.2.3 From a60a295986edcbca6603cd42b4e38d7c83d87fb6 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 25 May 2012 07:08:05 -0300 Subject: [media] s5p-fimc: media_entity_pipeline_start() may fail Take into account media_entity_pipeline_start() may fail. [s.nawrocki: rebased onto latest tree] Signed-off-by: Sakari Ailus Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 71e483847a17..7ace324d30cd 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1045,8 +1045,10 @@ static int fimc_cap_streamon(struct file *file, void *priv, if (fimc_capture_active(fimc)) return -EBUSY; - media_entity_pipeline_start(&p->subdevs[IDX_SENSOR]->entity, + ret = media_entity_pipeline_start(&p->subdevs[IDX_SENSOR]->entity, p->m_pipeline); + if (ret < 0) + return ret; if (fimc->vid_cap.user_subdev_api) { ret = fimc_pipeline_validate(fimc); -- cgit v1.2.3 From a1a5861bd9ca3a6cb7ab89dd836da8cd158986b0 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 25 May 2012 03:29:38 -0300 Subject: [media] s5p-fimc: Fix compiler warning in fimc-lite.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch is an update to changed media_entity_pipeline_start() signature in commit af88be3887c1a0b20d0792c3c237a67c73ef3286, "media: Add link_validate() op to check links to the sink pad" It fixes the following warning: drivers/media/video/s5p-fimc/fimc-lite.c: In function ‘fimc_lite_streamon’: drivers/media/video/s5p-fimc/fimc-lite.c:765:29: warning: ignoring return value of ‘media_entity_pipeline_start’, declared with attribute warn_unused_result [-Wunused-result] Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-lite.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index b0a8ce61ebf2..4d269b8fa1ef 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -764,7 +764,9 @@ static int fimc_lite_streamon(struct file *file, void *priv, if (fimc_lite_active(fimc)) return -EBUSY; - media_entity_pipeline_start(&sensor->entity, p->m_pipeline); + ret = media_entity_pipeline_start(&sensor->entity, p->m_pipeline); + if (ret < 0) + return ret; ret = fimc_pipeline_validate(fimc); if (ret) { -- cgit v1.2.3 From f676fa068819357f716953920b764554fe116b3c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 25 May 2012 03:29:40 -0300 Subject: [media] s5p-fimc: Stop media entity pipeline if fimc_pipeline_validate fails Stops the media entity pipeline which was started earlier if fimc_pipeline_validate fails. [s.nawrocki: reworked to not exceed 80 characters line length] Signed-off-by: Sachin Kamat Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-capture.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 7ace324d30cd..725812aa0c30 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1040,20 +1040,22 @@ static int fimc_cap_streamon(struct file *file, void *priv, { struct fimc_dev *fimc = video_drvdata(file); struct fimc_pipeline *p = &fimc->pipeline; + struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR]; int ret; if (fimc_capture_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(&p->subdevs[IDX_SENSOR]->entity, - p->m_pipeline); + ret = media_entity_pipeline_start(&sd->entity, p->m_pipeline); if (ret < 0) return ret; if (fimc->vid_cap.user_subdev_api) { ret = fimc_pipeline_validate(fimc); - if (ret) + if (ret < 0) { + media_entity_pipeline_stop(&sd->entity); return ret; + } } return vb2_streamon(&fimc->vid_cap.vbq, type); } -- cgit v1.2.3 From b3a6d2e0e727c3b126093c9b5fcdf41dd12bc4fd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 22 Jun 2012 15:57:55 +0200 Subject: iwlwifi: limit dwell time more strictly The dwell time for scanning is currently limited so that it fits into the timings inside the ucode when that is tracking DTIM/beacon periods for the AP(s) it's connected to. However, when it's connected to two APs, those may be in lockstep, for example if they both have a DTIM interval of 100 TU, then one could be 50 TU after the other, leaving only 50 TU free to be used by scanning. Since we can't know how far apart they are the only option is to restrict to 1/2 of the minium of the two APs. In theory, it would be possible to not use 1/2 of the minimum but take into account that if they have different intervals then there will be a bit more time since they can't be in lockstep, but as they will have 100 TU intervals in practice that complex calculation will probably just result in hard-to-find bugs. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/scan.c | 37 ++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index 6633074258c7..e3467fa86899 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -396,15 +396,21 @@ static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) { struct iwl_rxon_context *ctx; + int limits[NUM_IWL_RXON_CTX] = {}; + int n_active = 0; + u16 limit; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); /* * If we're associated, we clamp the dwell time 98% - * of the smallest beacon interval (minus 2 * channel - * tune time) + * of the beacon interval (minus 2 * channel tune time) + * If both contexts are active, we have to restrict to + * 1/2 of the minimum of them, because they might be in + * lock-step with the time inbetween only half of what + * time we'd have in each of them. */ for_each_context(priv, ctx) { - u16 value; - switch (ctx->staging.dev_type) { case RXON_DEV_TYPE_P2P: /* no timing constraints */ @@ -424,14 +430,25 @@ static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) break; } - value = ctx->beacon_int; - if (!value) - value = IWL_PASSIVE_DWELL_BASE; - value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; - dwell_time = min(value, dwell_time); + limits[n_active++] = ctx->beacon_int ?: IWL_PASSIVE_DWELL_BASE; } - return dwell_time; + switch (n_active) { + case 0: + return dwell_time; + case 2: + limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + limit /= 2; + dwell_time = min(limit, dwell_time); + /* fall through to limit further */ + case 1: + limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + limit /= n_active; + return min(limit, dwell_time); + default: + WARN_ON_ONCE(1); + return dwell_time; + } } static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, -- cgit v1.2.3 From 020c876738bf6ea55687543bfd2c0227ddbd3126 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 24 Jun 2012 15:51:59 +0300 Subject: iwlwifi: disable the watchdog for queues by default I saw that when the watchdog triggers, the packets do go through if we wait enough time. So we still have an issue where packets are blocked in the Tx queue for a short while and this needs to be debugged separately. For now, don't restart the driver when it is not needed. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-drv.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 095547b37a27..6322e2a692cc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -1011,6 +1011,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .power_level = IWL_POWER_INDEX_1, .bt_ch_announce = true, .auto_agg = true, + .wd_disable = true, /* the rest are 0 by default */ }; EXPORT_SYMBOL_GPL(iwlwifi_mod_params); -- cgit v1.2.3 From d0f718c1c0dfcb67f3af47a1fc4de7784974d1f7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 25 Jun 2012 14:46:44 +0200 Subject: mac80211_hwsim: fix smatch/sparse complaints The code is fine in both cases as-is, but we can write it slightly differently to fix smatch/sparse complaints: * compare the skb pointer (which we use as a cookie) by casting the skb to unsigned long rather than the cookie to a pointer (fixes "different address spaces") * when transmitting, data->channel must be assigned, don't check it (fixes "dereferenced before check") Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 4c9336cee817..eacfe020d0b7 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -678,8 +678,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, continue; if (data2->idle || !data2->started || - !hwsim_ps_rx_ok(data2, skb) || - !data->channel || !data2->channel || + !hwsim_ps_rx_ok(data2, skb) || !data2->channel || data->channel->center_freq != data2->channel->center_freq || !(data->group & data2->group)) continue; @@ -1486,7 +1485,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, struct mac80211_hwsim_data *data2; struct ieee80211_tx_info *txi; struct hwsim_tx_rate *tx_attempts; - struct sk_buff __user *ret_skb; + unsigned long ret_skb_ptr; struct sk_buff *skb, *tmp; struct mac_address *src; unsigned int hwsim_flags; @@ -1504,8 +1503,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); - ret_skb = (struct sk_buff __user *) - (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); + ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); data2 = get_hwsim_data_ref_from_addr(src); @@ -1514,7 +1512,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, /* look for the skb matching the cookie passed back from user */ skb_queue_walk_safe(&data2->pending, skb, tmp) { - if (skb == ret_skb) { + if ((unsigned long)skb == ret_skb_ptr) { skb_unlink(skb, &data2->pending); found = true; break; -- cgit v1.2.3 From 4ad33411308596f2f918603509729922a1ec4411 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 22 Jun 2012 10:58:06 -0700 Subject: x86, cpufeature: Rename X86_FEATURE_DTS to X86_FEATURE_DTHERM It makes sense to label "Digital Thermal Sensor" as "DTS", but unfortunately the string "dts" was already used for "Debug Store", and /proc/cpuinfo is a user space ABI. Therefore, rename this to "dtherm". This conflict went into mainline via the hwmon tree without any x86 maintainer ack, and without any kind of hint in the subject. a4659053 x86/hwmon: fix initialization of coretemp Reported-by: Jean Delvare Link: http://lkml.kernel.org/r/4FE34BCB.5050305@linux.intel.com Cc: Jan Beulich Cc: v2.6.36..v3.4 Signed-off-by: H. Peter Anvin --- drivers/hwmon/coretemp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index b9d512331ed4..0f52799973d4 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -664,7 +664,7 @@ static void __cpuinit get_core_online(unsigned int cpu) * sensors. We check this bit only, all the early CPUs * without thermal sensors will be filtered out. */ - if (!cpu_has(c, X86_FEATURE_DTS)) + if (!cpu_has(c, X86_FEATURE_DTHERM)) return; if (!pdev) { @@ -765,7 +765,7 @@ static struct notifier_block coretemp_cpu_notifier __refdata = { }; static const struct x86_cpu_id coretemp_ids[] = { - { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTS }, + { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, {} }; MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); -- cgit v1.2.3 From bed3d9c0b71f9afbfec905cb6db3b9f16be29d4d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sat, 23 Jun 2012 19:23:31 +0200 Subject: ath9k: fix dynamic WEP related regression commit 7a532fe7131216a02c81a6c1b1f8632da1195a58 ath9k_hw: fix interpretation of the rx KeyMiss flag This commit used the rx key miss indication to detect packets that were passed from the hardware without being decrypted, however it seems that this bit is not only undefined in the static WEP case, but also for dynamically allocated WEP keys. This caused a regression when using WEP-LEAP. This patch fixes the regression by keeping track of which key indexes refer to CCMP keys and only using the key miss indication for those. Reported-by: Stanislaw Gruszka Signed-off-by: Felix Fietkau Cc: stable@vger.kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath.h | 1 + drivers/net/wireless/ath/ath9k/recv.c | 3 ++- drivers/net/wireless/ath/key.c | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index c54b7d37bff1..420d69b2674c 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -143,6 +143,7 @@ struct ath_common { u32 keymax; DECLARE_BITMAP(keymap, ATH_KEYMAX); DECLARE_BITMAP(tkip_keymap, ATH_KEYMAX); + DECLARE_BITMAP(ccmp_keymap, ATH_KEYMAX); enum ath_crypt_caps crypt_caps; unsigned int clockrate; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index e1fcc68124dc..599667ababee 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -822,7 +822,8 @@ static bool ath9k_rx_accept(struct ath_common *common, * descriptor does contain a valid key index. This has been observed * mostly with CCMP encryption. */ - if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID) + if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID || + !test_bit(rx_stats->rs_keyix, common->ccmp_keymap)) rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; if (!rx_stats->rs_datalen) { diff --git a/drivers/net/wireless/ath/key.c b/drivers/net/wireless/ath/key.c index 0e81904956cf..5c54aa43ca2d 100644 --- a/drivers/net/wireless/ath/key.c +++ b/drivers/net/wireless/ath/key.c @@ -556,6 +556,9 @@ int ath_key_config(struct ath_common *common, return -EIO; set_bit(idx, common->keymap); + if (key->cipher == WLAN_CIPHER_SUITE_CCMP) + set_bit(idx, common->ccmp_keymap); + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) { set_bit(idx + 64, common->keymap); set_bit(idx, common->tkip_keymap); @@ -582,6 +585,7 @@ void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key) return; clear_bit(key->hw_key_idx, common->keymap); + clear_bit(key->hw_key_idx, common->ccmp_keymap); if (key->cipher != WLAN_CIPHER_SUITE_TKIP) return; -- cgit v1.2.3 From ff0b804632f025b072f81fc0cd585102b0a43534 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 24 Jun 2012 19:17:00 -0700 Subject: wlcore: drop INET dependency Mainline build reports: warning: (WL12XX) selects WLCORE which has unmet direct dependencies (NETDEVICES && WLAN && WL_TI && GENERIC_HARDIRQS && MAC80211 && INET) The INET dependency was added in commit 3c6af5b54fe74b6e56efadc22927e4055d00e9fc: wl1271_main.c:(.text+0x271052): undefined reference to `unregister_inetaddr_ notifier' wl1271_main.c:(.text+0x2714d7): undefined reference to `register_inetaddr_no tifier' Driver is doing some filtering based on IP addresses... but this driver no longer has that code and it builds fine even when CONFIG_INET is not enabled, so drop that dependency and eliminate the kconfig warning message. Signed-off-by: Randy Dunlap Cc: Luciano Coelho Cc: John W. Linville Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig index 54156b0b5c2d..d7b907e67170 100644 --- a/drivers/net/wireless/ti/wlcore/Kconfig +++ b/drivers/net/wireless/ti/wlcore/Kconfig @@ -1,7 +1,6 @@ config WLCORE tristate "TI wlcore support" depends on WL_TI && GENERIC_HARDIRQS && MAC80211 - depends on INET select FW_LOADER ---help--- This module contains the main code for TI WLAN chips. It abstracts -- cgit v1.2.3 From eac9ac6d1f5d0e9d33e4ded682187b630e7606cd Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 25 Jun 2012 09:36:41 +0200 Subject: iwlwifi: fix activating inactive stations When authentication/association timed out, the driver would complain bitterly, printing the message ACTIVATE a non DRIVER active station id ... addr ... The cause turns out to be that when the AP station is added but we don't associate, the IWL_STA_UCODE_INPROGRESS is set but never cleared. This then causes iwl_restore_stations() to attempt to resend it because it uses the flag internally and uploads even if it didn't set it itself. To fix this issue and not upload the station again when it has already been removed by mac80211, clear the flag after adding it in case we add it only for association. Cc: stable@vger.kernel.org Reviewed-by: Meenakshi Venkataraman Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-mac80211.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c index 3ee23134c02b..013680332f07 100644 --- a/drivers/net/wireless/iwlwifi/iwl-mac80211.c +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -796,6 +796,18 @@ int iwlagn_mac_sta_state(struct ieee80211_hw *hw, switch (op) { case ADD: ret = iwlagn_mac_sta_add(hw, vif, sta); + if (ret) + break; + /* + * Clear the in-progress flag, the AP station entry was added + * but we'll initialize LQ only when we've associated (which + * would also clear the in-progress flag). This is necessary + * in case we never initialize LQ because association fails. + */ + spin_lock_bh(&priv->sta_lock); + priv->stations[iwl_sta_id(sta)].used &= + ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_bh(&priv->sta_lock); break; case REMOVE: ret = iwlagn_mac_sta_remove(hw, vif, sta); -- cgit v1.2.3 From d8140b2fa09cceb947ac1ac49e0958eb137d0648 Mon Sep 17 00:00:00 2001 From: Santosh Nayak Date: Tue, 19 Jun 2012 21:27:39 +0000 Subject: netxen: Error return off by one in 'netxen_nic_set_pauseparam()'. There are 'NETXEN_NIU_MAX_GBE_PORTS' GBE ports. Port indexing starts from zero. Hence we should also return error for "port == NETXEN_NIU_MAX_GBE_PORTS" Signed-off-by: Santosh Nayak --- drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index 39730403782f..d4f179f96dcb 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -489,7 +489,7 @@ netxen_nic_get_pauseparam(struct net_device *dev, int port = adapter->physical_port; if (adapter->ahw.port_type == NETXEN_NIC_GBE) { - if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + if ((port < 0) || (port >= NETXEN_NIU_MAX_GBE_PORTS)) return; /* get flow control settings */ val = NXRD32(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port)); @@ -534,7 +534,7 @@ netxen_nic_set_pauseparam(struct net_device *dev, int port = adapter->physical_port; /* read mode */ if (adapter->ahw.port_type == NETXEN_NIC_GBE) { - if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + if ((port < 0) || (port >= NETXEN_NIU_MAX_GBE_PORTS)) return -EIO; /* set flow control */ val = NXRD32(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port)); -- cgit v1.2.3 From ed3b856b69a7f3748d6917e42d462c962aaa39b8 Mon Sep 17 00:00:00 2001 From: Santosh Nayak Date: Wed, 20 Jun 2012 00:52:58 +0000 Subject: netxen: Error return off by one for XG port. There are NETXEN_NIU_MAX_XG_PORTS ports. Port indexing starts from zero. Hence we should also return error for 'port == NETXEN_NIU_MAX_XG_PORTS'. Signed-off-by: Santosh Nayak --- drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c | 4 ++-- drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index d4f179f96dcb..9103e3e36c28 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -511,7 +511,7 @@ netxen_nic_get_pauseparam(struct net_device *dev, break; } } else if (adapter->ahw.port_type == NETXEN_NIC_XGBE) { - if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) + if ((port < 0) || (port >= NETXEN_NIU_MAX_XG_PORTS)) return; pause->rx_pause = 1; val = NXRD32(adapter, NETXEN_NIU_XG_PAUSE_CTL); @@ -577,7 +577,7 @@ netxen_nic_set_pauseparam(struct net_device *dev, } NXWR32(adapter, NETXEN_NIU_GB_PAUSE_CTL, val); } else if (adapter->ahw.port_type == NETXEN_NIC_XGBE) { - if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) + if ((port < 0) || (port >= NETXEN_NIU_MAX_XG_PORTS)) return -EIO; val = NXRD32(adapter, NETXEN_NIU_XG_PAUSE_CTL); if (port == 0) { diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c index de96a948bb7f..946160fa5843 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c @@ -365,7 +365,7 @@ static int netxen_niu_disable_xg_port(struct netxen_adapter *adapter) if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) return 0; - if (port > NETXEN_NIU_MAX_XG_PORTS) + if (port >= NETXEN_NIU_MAX_XG_PORTS) return -EINVAL; mac_cfg = 0; @@ -392,7 +392,7 @@ static int netxen_p2_nic_set_promisc(struct netxen_adapter *adapter, u32 mode) u32 port = adapter->physical_port; u16 board_type = adapter->ahw.board_type; - if (port > NETXEN_NIU_MAX_XG_PORTS) + if (port >= NETXEN_NIU_MAX_XG_PORTS) return -EINVAL; mac_cfg = NXRD32(adapter, NETXEN_NIU_XGE_CONFIG_0 + (0x10000 * port)); -- cgit v1.2.3 From 89b1f496d2b659eaaac5a8d80b801661ab03854f Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Sun, 24 Jun 2012 19:40:55 +0000 Subject: be2net: Explicitly clear the reserved field in the Tx Descriptor Signed-off-by: Somnath Kotur Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index cbd245afd8ab..c60782e299db 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -558,6 +558,7 @@ static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len) wrb->frag_pa_hi = upper_32_bits(addr); wrb->frag_pa_lo = addr & 0xFFFFFFFF; wrb->frag_len = len & ETH_WRB_FRAG_LEN_MASK; + wrb->rsvd0 = 0; } static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, -- cgit v1.2.3 From 7665de15642dfb1709177517aa0488162727342c Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Sun, 24 Jun 2012 19:42:00 +0000 Subject: be2net: Regression bug wherein VFs creation broken for multiple cards. Fix be_find_vfs() to check for matching bus number as well along with devfn Signed-off-by: Somnath Kotur Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index c60782e299db..a28896d4411d 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1059,7 +1059,8 @@ static int be_find_vfs(struct be_adapter *adapter, int vf_state) dev = pci_get_device(pdev->vendor, PCI_ANY_ID, NULL); while (dev) { vf_fn = (pdev->devfn + offset + stride * vfs) & 0xFFFF; - if (dev->is_virtfn && dev->devfn == vf_fn) { + if (dev->is_virtfn && dev->devfn == vf_fn && + dev->bus->number == pdev->bus->number) { vfs++; if (dev->dev_flags & PCI_DEV_FLAGS_ASSIGNED) assigned_vfs++; -- cgit v1.2.3 From 4e7bb59d49fb00d4cf13484386d0400783f2c826 Mon Sep 17 00:00:00 2001 From: Kim Lilliestierna XX Date: Mon, 25 Jun 2012 07:49:36 +0000 Subject: caif-hsi: Removed dead code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify logic and remove dead code. Signed-off-by: Kim Lilliestierna Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 1520814c77c7..f81f61fb39bc 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -680,12 +680,11 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) if (desc_pld_len < 0) goto out_of_sync; - if (desc_pld_len > 0) + if (desc_pld_len > 0) { rx_len = desc_pld_len; - - if (desc_pld_len > 0 && - (piggy_desc->header & CFHSI_PIGGY_DESC)) - rx_len += CFHSI_DESC_SZ; + if (piggy_desc->header & CFHSI_PIGGY_DESC) + rx_len += CFHSI_DESC_SZ; + } /* * Copy needed information from the piggy-backed @@ -695,8 +694,6 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) CFHSI_DESC_SHORT_SZ); /* Mark no embedded frame here */ piggy_desc->offset = 0; - if (desc_pld_len == -EPROTO) - goto out_of_sync; } } -- cgit v1.2.3 From a5c96b518c373fba35a8dc2e4e5fead24aeefb1c Mon Sep 17 00:00:00 2001 From: Kim Lilliestierna XX Date: Mon, 25 Jun 2012 07:49:37 +0000 Subject: caif-hsi: changed test on aggregation_timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aggregation_timeout is an unsigned long, a test for less than zero can never become true, compare with zero instead. Signed-off-by: Kim Lilliestierna Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index f81f61fb39bc..56cc94120677 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -115,7 +115,7 @@ static bool cfhsi_can_send_aggregate(struct cfhsi *cfhsi) { int i; - if (cfhsi->aggregation_timeout < 0) + if (cfhsi->aggregation_timeout == 0) return true; for (i = 0; i < CFHSI_PRIO_BEBK; ++i) { -- cgit v1.2.3 From 90de9bab9123d7fe905cb885c1e3ed32dc516f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Mon, 25 Jun 2012 07:49:38 +0000 Subject: caif-hsi: Use netdev_X instead of dev_X for printing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace dev_X with the corresponding netdev_X print function when applicable. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 94 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 56cc94120677..f7997a7b5aed 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -85,7 +85,7 @@ static void cfhsi_inactivity_tout(unsigned long arg) { struct cfhsi *cfhsi = (struct cfhsi *)arg; - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); /* Schedule power down work queue. */ @@ -181,14 +181,14 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) size_t fifo_occupancy; int ret; - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); do { ret = cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, &fifo_occupancy); if (ret) { - dev_warn(&cfhsi->ndev->dev, + netdev_warn(cfhsi->ndev, "%s: can't get FIFO occupancy: %d.\n", __func__, ret); break; @@ -202,7 +202,7 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) cfhsi->dev); if (ret) { clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); - dev_warn(&cfhsi->ndev->dev, + netdev_warn(cfhsi->ndev, "%s: can't read data: %d.\n", __func__, ret); break; @@ -213,13 +213,13 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret); if (ret < 0) { - dev_warn(&cfhsi->ndev->dev, + netdev_warn(cfhsi->ndev, "%s: can't wait for flush complete: %d.\n", __func__, ret); break; } else if (!ret) { ret = -ETIMEDOUT; - dev_warn(&cfhsi->ndev->dev, + netdev_warn(cfhsi->ndev, "%s: timeout waiting for flush complete.\n", __func__); break; @@ -348,7 +348,7 @@ static void cfhsi_start_tx(struct cfhsi *cfhsi) struct cfhsi_desc *desc = (struct cfhsi_desc *)cfhsi->tx_buf; int len, res; - dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; @@ -374,14 +374,14 @@ static void cfhsi_start_tx(struct cfhsi *cfhsi) /* Set up new transfer. */ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); if (WARN_ON(res < 0)) - dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n", + netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); } while (res < 0); } static void cfhsi_tx_done(struct cfhsi *cfhsi) { - dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; @@ -416,7 +416,7 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) struct cfhsi *cfhsi; cfhsi = container_of(drv, struct cfhsi, drv); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) @@ -433,7 +433,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) if ((desc->header & ~CFHSI_PIGGY_DESC) || (desc->offset > CFHSI_MAX_EMB_FRM_SZ)) { - dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", + netdev_err(cfhsi->ndev, "%s: Invalid descriptor.\n", __func__); return -EPROTO; } @@ -455,7 +455,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Sanity check length of CAIF frame. */ if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { - dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", + netdev_err(cfhsi->ndev, "%s: Invalid length.\n", __func__); return -EPROTO; } @@ -463,7 +463,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Allocate SKB (OK even in IRQ context). */ skb = alloc_skb(len + 1, GFP_ATOMIC); if (!skb) { - dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n", + netdev_err(cfhsi->ndev, "%s: Out of memory !\n", __func__); return -ENOMEM; } @@ -504,7 +504,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) xfer_sz += CFHSI_DESC_SZ; if ((xfer_sz % 4) || (xfer_sz > (CFHSI_BUF_SZ_RX - CFHSI_DESC_SZ))) { - dev_err(&cfhsi->ndev->dev, + netdev_err(cfhsi->ndev, "%s: Invalid payload len: %d, ignored.\n", __func__, xfer_sz); return -EPROTO; @@ -551,7 +551,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Sanity check header and offset. */ if (WARN_ON((desc->header & ~CFHSI_PIGGY_DESC) || (desc->offset > CFHSI_MAX_EMB_FRM_SZ))) { - dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n", + netdev_err(cfhsi->ndev, "%s: Invalid descriptor.\n", __func__); return -EPROTO; } @@ -585,7 +585,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Sanity check length of CAIF frames. */ if (unlikely(len > CFHSI_MAX_CAIF_FRAME_SZ)) { - dev_err(&cfhsi->ndev->dev, "%s: Invalid length.\n", + netdev_err(cfhsi->ndev, "%s: Invalid length.\n", __func__); return -EPROTO; } @@ -593,7 +593,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Allocate SKB (OK even in IRQ context). */ skb = alloc_skb(len + 1, GFP_ATOMIC); if (!skb) { - dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n", + netdev_err(cfhsi->ndev, "%s: Out of memory !\n", __func__); cfhsi->rx_state.nfrms = nfrms; return -ENOMEM; @@ -639,7 +639,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) desc = (struct cfhsi_desc *)cfhsi->rx_buf; - dev_dbg(&cfhsi->ndev->dev, "%s\n", __func__); + netdev_dbg(cfhsi->ndev, "%s\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; @@ -709,13 +709,13 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) /* Initiate next read */ if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) { /* Set up new transfer. */ - dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", + netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); res = cfhsi->dev->cfhsi_rx(rx_ptr, rx_len, cfhsi->dev); if (WARN_ON(res < 0)) { - dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n", + netdev_err(cfhsi->ndev, "%s: RX error %d.\n", __func__, res); cfhsi->ndev->stats.rx_errors++; cfhsi->ndev->stats.rx_dropped++; @@ -750,7 +750,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) return; out_of_sync: - dev_err(&cfhsi->ndev->dev, "%s: Out of sync.\n", __func__); + netdev_err(cfhsi->ndev, "%s: Out of sync.\n", __func__); print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, cfhsi->rx_buf, CFHSI_DESC_SZ); schedule_work(&cfhsi->out_of_sync_work); @@ -760,7 +760,7 @@ static void cfhsi_rx_slowpath(unsigned long arg) { struct cfhsi *cfhsi = (struct cfhsi *)arg; - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); cfhsi_rx_done(cfhsi); @@ -771,7 +771,7 @@ static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) struct cfhsi *cfhsi; cfhsi = container_of(drv, struct cfhsi, drv); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) @@ -806,7 +806,7 @@ static void cfhsi_wake_up(struct work_struct *work) /* Activate wake line. */ cfhsi->dev->cfhsi_wake_up(cfhsi->dev); - dev_dbg(&cfhsi->ndev->dev, "%s: Start waiting.\n", + netdev_dbg(cfhsi->ndev, "%s: Start waiting.\n", __func__); /* Wait for acknowledge. */ @@ -816,7 +816,7 @@ static void cfhsi_wake_up(struct work_struct *work) &cfhsi->bits), ret); if (unlikely(ret < 0)) { /* Interrupted by signal. */ - dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", + netdev_err(cfhsi->ndev, "%s: Signalled: %ld.\n", __func__, ret); clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); @@ -827,14 +827,14 @@ static void cfhsi_wake_up(struct work_struct *work) size_t fifo_occupancy = 0; /* Wakeup timeout */ - dev_dbg(&cfhsi->ndev->dev, "%s: Timeout.\n", + netdev_dbg(cfhsi->ndev, "%s: Timeout.\n", __func__); /* Check FIFO to check if modem has sent something. */ WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, &fifo_occupancy)); - dev_dbg(&cfhsi->ndev->dev, "%s: Bytes in FIFO: %u.\n", + netdev_dbg(cfhsi->ndev, "%s: Bytes in FIFO: %u.\n", __func__, (unsigned) fifo_occupancy); /* Check if we misssed the interrupt. */ @@ -842,7 +842,7 @@ static void cfhsi_wake_up(struct work_struct *work) &ca_wake)); if (ca_wake) { - dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n", + netdev_err(cfhsi->ndev, "%s: CA Wake missed !.\n", __func__); /* Clear the CFHSI_WAKE_UP_ACK bit to prevent race. */ @@ -857,7 +857,7 @@ static void cfhsi_wake_up(struct work_struct *work) return; } wake_ack: - dev_dbg(&cfhsi->ndev->dev, "%s: Woken.\n", + netdev_dbg(cfhsi->ndev, "%s: Woken.\n", __func__); /* Clear power up bit. */ @@ -865,11 +865,11 @@ wake_ack: clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); /* Resume read operation. */ - dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", __func__); + netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev); if (WARN_ON(res < 0)) - dev_err(&cfhsi->ndev->dev, "%s: RX err %d.\n", __func__, res); + netdev_err(cfhsi->ndev, "%s: RX err %d.\n", __func__, res); /* Clear power up acknowledment. */ clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); @@ -878,7 +878,7 @@ wake_ack: /* Resume transmit if queues are not empty. */ if (!cfhsi_tx_queue_len(cfhsi)) { - dev_dbg(&cfhsi->ndev->dev, "%s: Peer wake, start timer.\n", + netdev_dbg(cfhsi->ndev, "%s: Peer wake, start timer.\n", __func__); /* Start inactivity timer. */ mod_timer(&cfhsi->inactivity_timer, @@ -887,7 +887,7 @@ wake_ack: return; } - dev_dbg(&cfhsi->ndev->dev, "%s: Host wake.\n", + netdev_dbg(cfhsi->ndev, "%s: Host wake.\n", __func__); spin_unlock_bh(&cfhsi->lock); @@ -899,12 +899,12 @@ wake_ack: /* Set up new transfer. */ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); if (WARN_ON(res < 0)) { - dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n", + netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); cfhsi_abort_tx(cfhsi); } } else { - dev_err(&cfhsi->ndev->dev, + netdev_err(cfhsi->ndev, "%s: Failed to create HSI frame: %d.\n", __func__, len); } @@ -918,7 +918,7 @@ static void cfhsi_wake_down(struct work_struct *work) int retry = CFHSI_WAKE_TOUT; cfhsi = container_of(work, struct cfhsi, wake_down_work); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__); + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) return; @@ -933,20 +933,20 @@ static void cfhsi_wake_down(struct work_struct *work) &cfhsi->bits), ret); if (ret < 0) { /* Interrupted by signal. */ - dev_err(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n", + netdev_err(cfhsi->ndev, "%s: Signalled: %ld.\n", __func__, ret); return; } else if (!ret) { bool ca_wake = true; /* Timeout */ - dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__); + netdev_err(cfhsi->ndev, "%s: Timeout.\n", __func__); /* Check if we misssed the interrupt. */ WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, &ca_wake)); if (!ca_wake) - dev_err(&cfhsi->ndev->dev, "%s: CA Wake missed !.\n", + netdev_err(cfhsi->ndev, "%s: CA Wake missed !.\n", __func__); } @@ -964,7 +964,7 @@ static void cfhsi_wake_down(struct work_struct *work) } if (!retry) - dev_err(&cfhsi->ndev->dev, "%s: FIFO Timeout.\n", __func__); + netdev_err(cfhsi->ndev, "%s: FIFO Timeout.\n", __func__); /* Clear AWAKE condition. */ clear_bit(CFHSI_AWAKE, &cfhsi->bits); @@ -990,7 +990,7 @@ static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) struct cfhsi *cfhsi = NULL; cfhsi = container_of(drv, struct cfhsi, drv); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); set_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits); @@ -1009,7 +1009,7 @@ static void cfhsi_wake_down_cb(struct cfhsi_drv *drv) struct cfhsi *cfhsi = NULL; cfhsi = container_of(drv, struct cfhsi, drv); - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); /* Initiating low power is only permitted by the host (us). */ @@ -1021,7 +1021,7 @@ static void cfhsi_aggregation_tout(unsigned long arg) { struct cfhsi *cfhsi = (struct cfhsi *)arg; - dev_dbg(&cfhsi->ndev->dev, "%s.\n", + netdev_dbg(cfhsi->ndev, "%s.\n", __func__); cfhsi_start_tx(cfhsi); @@ -1113,7 +1113,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) /* Set up new transfer. */ res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); if (WARN_ON(res < 0)) { - dev_err(&cfhsi->ndev->dev, "%s: TX error %d.\n", + netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); cfhsi_abort_tx(cfhsi); } @@ -1269,7 +1269,7 @@ static int cfhsi_open(struct net_device *ndev) /* Create work thread. */ cfhsi->wq = create_singlethread_workqueue(cfhsi->pdev->name); if (!cfhsi->wq) { - dev_err(&cfhsi->ndev->dev, "%s: Failed to create work queue.\n", + netdev_err(cfhsi->ndev, "%s: Failed to create work queue.\n", __func__); res = -ENODEV; goto err_create_wq; @@ -1296,7 +1296,7 @@ static int cfhsi_open(struct net_device *ndev) /* Activate HSI interface. */ res = cfhsi->dev->cfhsi_up(cfhsi->dev); if (res) { - dev_err(&cfhsi->ndev->dev, + netdev_err(cfhsi->ndev, "%s: can't activate HSI interface: %d.\n", __func__, res); goto err_activate; @@ -1305,7 +1305,7 @@ static int cfhsi_open(struct net_device *ndev) /* Flush FIFO */ res = cfhsi_flush_fifo(cfhsi); if (res) { - dev_err(&cfhsi->ndev->dev, "%s: Can't flush FIFO: %d.\n", + netdev_err(cfhsi->ndev, "%s: Can't flush FIFO: %d.\n", __func__, res); goto err_net_reg; } -- cgit v1.2.3 From b42f7b5cfda6f7dac298da2d9a8855f6364e35d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Mon, 25 Jun 2012 07:49:39 +0000 Subject: caif-hsi: Remove uncecessary assignments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove assignment at declaration when not needed. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index f7997a7b5aed..d80759e58532 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -246,8 +246,8 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Check if we can embed a CAIF frame. */ if (skb->len < CFHSI_MAX_EMB_FRM_SZ) { struct caif_payload_info *info; - int hpad = 0; - int tpad = 0; + int hpad; + int tpad; /* Calculate needed head alignment and tail alignment. */ info = (struct caif_payload_info *)&skb->cb; @@ -282,8 +282,8 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ; while (nfrms < CFHSI_MAX_PKTS) { struct caif_payload_info *info; - int hpad = 0; - int tpad = 0; + int hpad; + int tpad; if (!skb) skb = cfhsi_dequeue(cfhsi); @@ -573,7 +573,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) struct sk_buff *skb; u8 *dst = NULL; u8 *pcffrm = NULL; - int len = 0; + int len; /* CAIF frame starts after head padding. */ pcffrm = pfrm + *pfrm + 1; -- cgit v1.2.3 From c41254006377842013922fb1e407391f24c59522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Mon, 25 Jun 2012 07:49:41 +0000 Subject: caif-hsi: Add rtnl support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add RTNL support for managing the caif hsi interface. The HSI HW interface is no longer registering as a device, instead we use symbol_get to get hold of the HSI API. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 226 +++++++++++++++++++++++++++----------------- 1 file changed, 137 insertions(+), 89 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index d80759e58532..a14f85c0f0e8 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -79,7 +79,6 @@ MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON)."); #define HIGH_WATER_MARK hsi_high_threshold static LIST_HEAD(cfhsi_list); -static spinlock_t cfhsi_list_lock; static void cfhsi_inactivity_tout(unsigned long arg) { @@ -1148,42 +1147,6 @@ static void cfhsi_setup(struct net_device *dev) cfhsi->ndev = dev; } -int cfhsi_probe(struct platform_device *pdev) -{ - struct cfhsi *cfhsi = NULL; - struct net_device *ndev; - - int res; - - ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup); - if (!ndev) - return -ENODEV; - - cfhsi = netdev_priv(ndev); - cfhsi->ndev = ndev; - cfhsi->pdev = pdev; - - /* Assign the HSI device. */ - cfhsi->dev = pdev->dev.platform_data; - - /* Assign the driver to this HSI device. */ - cfhsi->dev->drv = &cfhsi->drv; - - /* Register network device. */ - res = register_netdev(ndev); - if (res) { - dev_err(&ndev->dev, "%s: Registration error: %d.\n", - __func__, res); - free_netdev(ndev); - } - /* Add CAIF HSI device to list. */ - spin_lock(&cfhsi_list_lock); - list_add_tail(&cfhsi->list, &cfhsi_list); - spin_unlock(&cfhsi_list_lock); - - return res; -} - static int cfhsi_open(struct net_device *ndev) { struct cfhsi *cfhsi = netdev_priv(ndev); @@ -1364,85 +1327,170 @@ static int cfhsi_close(struct net_device *ndev) return 0; } +static void cfhsi_uninit(struct net_device *dev) +{ + struct cfhsi *cfhsi = netdev_priv(dev); + ASSERT_RTNL(); + symbol_put(cfhsi_get_device); + list_del(&cfhsi->list); +} + static const struct net_device_ops cfhsi_ops = { + .ndo_uninit = cfhsi_uninit, .ndo_open = cfhsi_open, .ndo_stop = cfhsi_close, .ndo_start_xmit = cfhsi_xmit }; -int cfhsi_remove(struct platform_device *pdev) +static void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi) { - struct list_head *list_node; - struct list_head *n; - struct cfhsi *cfhsi = NULL; - struct cfhsi_dev *dev; + int i; - dev = (struct cfhsi_dev *)pdev->dev.platform_data; - spin_lock(&cfhsi_list_lock); - list_for_each_safe(list_node, n, &cfhsi_list) { - cfhsi = list_entry(list_node, struct cfhsi, list); - /* Find the corresponding device. */ - if (cfhsi->dev == dev) { - /* Remove from list. */ - list_del(list_node); - spin_unlock(&cfhsi_list_lock); - return 0; - } + if (!data) { + pr_debug("no params data found\n"); + return; } - spin_unlock(&cfhsi_list_lock); - return -ENODEV; + + i = __IFLA_CAIF_HSI_INACTIVITY_TOUT; + if (data[i]) + inactivity_timeout = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_AGGREGATION_TOUT; + if (data[i]) + aggregation_timeout = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_HEAD_ALIGN; + if (data[i]) + hsi_head_align = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_TAIL_ALIGN; + if (data[i]) + hsi_tail_align = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_QHIGH_WATERMARK; + if (data[i]) + hsi_high_threshold = nla_get_u32(data[i]); +} + +static int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + cfhsi_netlink_parms(data, netdev_priv(dev)); + netdev_state_change(dev); + return 0; } -struct platform_driver cfhsi_plat_drv = { - .probe = cfhsi_probe, - .remove = cfhsi_remove, - .driver = { - .name = "cfhsi", - .owner = THIS_MODULE, - }, +static const struct nla_policy caif_hsi_policy[__IFLA_CAIF_HSI_MAX + 1] = { + [__IFLA_CAIF_HSI_INACTIVITY_TOUT] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_AGGREGATION_TOUT] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_HEAD_ALIGN] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_TAIL_ALIGN] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_QHIGH_WATERMARK] = { .type = NLA_U32, .len = 4 }, + [__IFLA_CAIF_HSI_QLOW_WATERMARK] = { .type = NLA_U32, .len = 4 }, }; -static void __exit cfhsi_exit_module(void) +static size_t caif_hsi_get_size(const struct net_device *dev) +{ + int i; + size_t s = 0; + for (i = __IFLA_CAIF_HSI_UNSPEC + 1; i < __IFLA_CAIF_HSI_MAX; i++) + s += nla_total_size(caif_hsi_policy[i].len); + return s; +} + +static int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT, + inactivity_timeout) || + nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT, + aggregation_timeout) || + nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, hsi_head_align) || + nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, hsi_tail_align) || + nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK, + hsi_high_threshold) || + nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK, + hsi_low_threshold)) + return -EMSGSIZE; + + return 0; +} + +static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) { - struct list_head *list_node; - struct list_head *n; struct cfhsi *cfhsi = NULL; + struct platform_device *(*get_dev)(void); - spin_lock(&cfhsi_list_lock); - list_for_each_safe(list_node, n, &cfhsi_list) { - cfhsi = list_entry(list_node, struct cfhsi, list); + ASSERT_RTNL(); + + cfhsi = netdev_priv(dev); + cfhsi_netlink_parms(data, cfhsi); + dev_net_set(cfhsi->ndev, src_net); + + get_dev = symbol_get(cfhsi_get_device); + if (!get_dev) { + pr_err("%s: failed to get the cfhsi device symbol\n", __func__); + return -ENODEV; + } + + /* Assign the HSI device. */ + cfhsi->pdev = (*get_dev)(); + if (!cfhsi->pdev) { + pr_err("%s: failed to get the cfhsi device\n", __func__); + goto err; + } - /* Remove from list. */ - list_del(list_node); - spin_unlock(&cfhsi_list_lock); + /* Assign the HSI device. */ + cfhsi->dev = cfhsi->pdev->dev.platform_data; + + /* Assign the driver to this HSI device. */ + cfhsi->dev->drv = &cfhsi->drv; - unregister_netdevice(cfhsi->ndev); + if (register_netdevice(dev)) { + pr_warn("%s: device rtml registration failed\n", __func__); + goto err; - spin_lock(&cfhsi_list_lock); } - spin_unlock(&cfhsi_list_lock); + /* Add CAIF HSI device to list. */ + list_add_tail(&cfhsi->list, &cfhsi_list); - /* Unregister platform driver. */ - platform_driver_unregister(&cfhsi_plat_drv); + return 0; +err: + symbol_put(cfhsi_get_device); + return -ENODEV; } -static int __init cfhsi_init_module(void) +static struct rtnl_link_ops caif_hsi_link_ops __read_mostly = { + .kind = "cfhsi", + .priv_size = sizeof(struct cfhsi), + .setup = cfhsi_setup, + .maxtype = __IFLA_CAIF_HSI_MAX, + .policy = caif_hsi_policy, + .newlink = caif_hsi_newlink, + .changelink = caif_hsi_changelink, + .get_size = caif_hsi_get_size, + .fill_info = caif_hsi_fill_info, +}; + +static void __exit cfhsi_exit_module(void) { - int result; + struct list_head *list_node; + struct list_head *n; + struct cfhsi *cfhsi; - /* Initialize spin lock. */ - spin_lock_init(&cfhsi_list_lock); + rtnl_link_unregister(&caif_hsi_link_ops); - /* Register platform driver. */ - result = platform_driver_register(&cfhsi_plat_drv); - if (result) { - printk(KERN_ERR "Could not register platform HSI driver: %d.\n", - result); - goto err_dev_register; + rtnl_lock(); + list_for_each_safe(list_node, n, &cfhsi_list) { + cfhsi = list_entry(list_node, struct cfhsi, list); + unregister_netdev(cfhsi->ndev); } + rtnl_unlock(); +} - err_dev_register: - return result; +static int __init cfhsi_init_module(void) +{ + return rtnl_link_register(&caif_hsi_link_ops); } module_init(cfhsi_init_module); -- cgit v1.2.3 From 1c385f1fdf6f9c66d982802cd74349c040980b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Mon, 25 Jun 2012 07:49:42 +0000 Subject: caif-hsi: Replace platform device with ops structure. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove use of struct platform_device, and replace it with struct cfhsi_ops. Updated variable names in the same spirit: cfhsi_get_dev to cfhsi_get_ops, cfhsi->dev to cfhsi->ops and, cfhsi->dev.drv to cfhsi->ops->cb_ops. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 109 +++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index a14f85c0f0e8..0927c108bd14 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -184,7 +183,7 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) __func__); do { - ret = cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + ret = cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, &fifo_occupancy); if (ret) { netdev_warn(cfhsi->ndev, @@ -197,8 +196,8 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi) fifo_occupancy = min(sizeof(buffer), fifo_occupancy); set_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); - ret = cfhsi->dev->cfhsi_rx(buffer, fifo_occupancy, - cfhsi->dev); + ret = cfhsi->ops->cfhsi_rx(buffer, fifo_occupancy, + cfhsi->ops); if (ret) { clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits); netdev_warn(cfhsi->ndev, @@ -371,7 +370,7 @@ static void cfhsi_start_tx(struct cfhsi *cfhsi) } /* Set up new transfer. */ - res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); if (WARN_ON(res < 0)) netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); @@ -410,11 +409,11 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) return; } -static void cfhsi_tx_done_cb(struct cfhsi_drv *drv) +static void cfhsi_tx_done_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -476,8 +475,8 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) skb->dev = cfhsi->ndev; /* - * We are called from a arch specific platform device. - * Unfortunately we don't know what context we're + * We are in a callback handler and + * unfortunately we don't know what context we're * running in. */ if (in_interrupt()) @@ -607,7 +606,7 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) skb->dev = cfhsi->ndev; /* - * We're called from a platform device, + * We're called in callback from HSI * and don't know the context we're running in. */ if (in_interrupt()) @@ -711,8 +710,8 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); - res = cfhsi->dev->cfhsi_rx(rx_ptr, rx_len, - cfhsi->dev); + res = cfhsi->ops->cfhsi_rx(rx_ptr, rx_len, + cfhsi->ops); if (WARN_ON(res < 0)) { netdev_err(cfhsi->ndev, "%s: RX error %d.\n", __func__, res); @@ -765,11 +764,11 @@ static void cfhsi_rx_slowpath(unsigned long arg) cfhsi_rx_done(cfhsi); } -static void cfhsi_rx_done_cb(struct cfhsi_drv *drv) +static void cfhsi_rx_done_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -803,7 +802,7 @@ static void cfhsi_wake_up(struct work_struct *work) } /* Activate wake line. */ - cfhsi->dev->cfhsi_wake_up(cfhsi->dev); + cfhsi->ops->cfhsi_wake_up(cfhsi->ops); netdev_dbg(cfhsi->ndev, "%s: Start waiting.\n", __func__); @@ -819,7 +818,7 @@ static void cfhsi_wake_up(struct work_struct *work) __func__, ret); clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); - cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + cfhsi->ops->cfhsi_wake_down(cfhsi->ops); return; } else if (!ret) { bool ca_wake = false; @@ -830,14 +829,14 @@ static void cfhsi_wake_up(struct work_struct *work) __func__); /* Check FIFO to check if modem has sent something. */ - WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, &fifo_occupancy)); netdev_dbg(cfhsi->ndev, "%s: Bytes in FIFO: %u.\n", __func__, (unsigned) fifo_occupancy); /* Check if we misssed the interrupt. */ - WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_get_peer_wake(cfhsi->ops, &ca_wake)); if (ca_wake) { @@ -852,7 +851,7 @@ static void cfhsi_wake_up(struct work_struct *work) } clear_bit(CFHSI_WAKE_UP, &cfhsi->bits); - cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + cfhsi->ops->cfhsi_wake_down(cfhsi->ops); return; } wake_ack: @@ -865,7 +864,7 @@ wake_ack: /* Resume read operation. */ netdev_dbg(cfhsi->ndev, "%s: Start RX.\n", __func__); - res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev); + res = cfhsi->ops->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->ops); if (WARN_ON(res < 0)) netdev_err(cfhsi->ndev, "%s: RX err %d.\n", __func__, res); @@ -896,7 +895,7 @@ wake_ack: if (likely(len > 0)) { /* Set up new transfer. */ - res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); if (WARN_ON(res < 0)) { netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); @@ -923,7 +922,7 @@ static void cfhsi_wake_down(struct work_struct *work) return; /* Deactivate wake line. */ - cfhsi->dev->cfhsi_wake_down(cfhsi->dev); + cfhsi->ops->cfhsi_wake_down(cfhsi->ops); /* Wait for acknowledge. */ ret = CFHSI_WAKE_TOUT; @@ -942,7 +941,7 @@ static void cfhsi_wake_down(struct work_struct *work) netdev_err(cfhsi->ndev, "%s: Timeout.\n", __func__); /* Check if we misssed the interrupt. */ - WARN_ON(cfhsi->dev->cfhsi_get_peer_wake(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_get_peer_wake(cfhsi->ops, &ca_wake)); if (!ca_wake) netdev_err(cfhsi->ndev, "%s: CA Wake missed !.\n", @@ -951,7 +950,7 @@ static void cfhsi_wake_down(struct work_struct *work) /* Check FIFO occupancy. */ while (retry) { - WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev, + WARN_ON(cfhsi->ops->cfhsi_fifo_occupancy(cfhsi->ops, &fifo_occupancy)); if (!fifo_occupancy) @@ -969,8 +968,7 @@ static void cfhsi_wake_down(struct work_struct *work) clear_bit(CFHSI_AWAKE, &cfhsi->bits); /* Cancel pending RX requests. */ - cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); - + cfhsi->ops->cfhsi_rx_cancel(cfhsi->ops); } static void cfhsi_out_of_sync(struct work_struct *work) @@ -984,11 +982,11 @@ static void cfhsi_out_of_sync(struct work_struct *work) rtnl_unlock(); } -static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) +static void cfhsi_wake_up_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi = NULL; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -1003,11 +1001,11 @@ static void cfhsi_wake_up_cb(struct cfhsi_drv *drv) queue_work(cfhsi->wq, &cfhsi->wake_up_work); } -static void cfhsi_wake_down_cb(struct cfhsi_drv *drv) +static void cfhsi_wake_down_cb(struct cfhsi_cb_ops *cb_ops) { struct cfhsi *cfhsi = NULL; - cfhsi = container_of(drv, struct cfhsi, drv); + cfhsi = container_of(cb_ops, struct cfhsi, cb_ops); netdev_dbg(cfhsi->ndev, "%s.\n", __func__); @@ -1110,7 +1108,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) WARN_ON(!len); /* Set up new transfer. */ - res = cfhsi->dev->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->dev); + res = cfhsi->ops->cfhsi_tx(cfhsi->tx_buf, len, cfhsi->ops); if (WARN_ON(res < 0)) { netdev_err(cfhsi->ndev, "%s: TX error %d.\n", __func__, res); @@ -1125,19 +1123,19 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -static const struct net_device_ops cfhsi_ops; +static const struct net_device_ops cfhsi_netdevops; static void cfhsi_setup(struct net_device *dev) { int i; struct cfhsi *cfhsi = netdev_priv(dev); dev->features = 0; - dev->netdev_ops = &cfhsi_ops; dev->type = ARPHRD_CAIF; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->mtu = CFHSI_MAX_CAIF_FRAME_SZ; dev->tx_queue_len = 0; dev->destructor = free_netdev; + dev->netdev_ops = &cfhsi_netdevops; for (i = 0; i < CFHSI_PRIO_LAST; ++i) skb_queue_head_init(&cfhsi->qhead[i]); cfhsi->cfdev.link_select = CAIF_LINK_HIGH_BANDW; @@ -1213,10 +1211,10 @@ static int cfhsi_open(struct net_device *ndev) spin_lock_init(&cfhsi->lock); /* Set up the driver. */ - cfhsi->drv.tx_done_cb = cfhsi_tx_done_cb; - cfhsi->drv.rx_done_cb = cfhsi_rx_done_cb; - cfhsi->drv.wake_up_cb = cfhsi_wake_up_cb; - cfhsi->drv.wake_down_cb = cfhsi_wake_down_cb; + cfhsi->cb_ops.tx_done_cb = cfhsi_tx_done_cb; + cfhsi->cb_ops.rx_done_cb = cfhsi_rx_done_cb; + cfhsi->cb_ops.wake_up_cb = cfhsi_wake_up_cb; + cfhsi->cb_ops.wake_down_cb = cfhsi_wake_down_cb; /* Initialize the work queues. */ INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up); @@ -1230,7 +1228,7 @@ static int cfhsi_open(struct net_device *ndev) clear_bit(CFHSI_AWAKE, &cfhsi->bits); /* Create work thread. */ - cfhsi->wq = create_singlethread_workqueue(cfhsi->pdev->name); + cfhsi->wq = create_singlethread_workqueue(cfhsi->ndev->name); if (!cfhsi->wq) { netdev_err(cfhsi->ndev, "%s: Failed to create work queue.\n", __func__); @@ -1257,7 +1255,7 @@ static int cfhsi_open(struct net_device *ndev) cfhsi->aggregation_timer.function = cfhsi_aggregation_tout; /* Activate HSI interface. */ - res = cfhsi->dev->cfhsi_up(cfhsi->dev); + res = cfhsi->ops->cfhsi_up(cfhsi->ops); if (res) { netdev_err(cfhsi->ndev, "%s: can't activate HSI interface: %d.\n", @@ -1275,7 +1273,7 @@ static int cfhsi_open(struct net_device *ndev) return res; err_net_reg: - cfhsi->dev->cfhsi_down(cfhsi->dev); + cfhsi->ops->cfhsi_down(cfhsi->ops); err_activate: destroy_workqueue(cfhsi->wq); err_create_wq: @@ -1305,7 +1303,7 @@ static int cfhsi_close(struct net_device *ndev) del_timer_sync(&cfhsi->aggregation_timer); /* Cancel pending RX request (if any) */ - cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev); + cfhsi->ops->cfhsi_rx_cancel(cfhsi->ops); /* Destroy workqueue */ destroy_workqueue(cfhsi->wq); @@ -1318,7 +1316,7 @@ static int cfhsi_close(struct net_device *ndev) cfhsi_abort_tx(cfhsi); /* Deactivate interface */ - cfhsi->dev->cfhsi_down(cfhsi->dev); + cfhsi->ops->cfhsi_down(cfhsi->ops); /* Free buffers. */ kfree(tx_buf); @@ -1335,7 +1333,7 @@ static void cfhsi_uninit(struct net_device *dev) list_del(&cfhsi->list); } -static const struct net_device_ops cfhsi_ops = { +static const struct net_device_ops cfhsi_netdevops = { .ndo_uninit = cfhsi_uninit, .ndo_open = cfhsi_open, .ndo_stop = cfhsi_close, @@ -1419,7 +1417,7 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct cfhsi *cfhsi = NULL; - struct platform_device *(*get_dev)(void); + struct cfhsi_ops *(*get_ops)(void); ASSERT_RTNL(); @@ -1427,36 +1425,31 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, cfhsi_netlink_parms(data, cfhsi); dev_net_set(cfhsi->ndev, src_net); - get_dev = symbol_get(cfhsi_get_device); - if (!get_dev) { - pr_err("%s: failed to get the cfhsi device symbol\n", __func__); + get_ops = symbol_get(cfhsi_get_ops); + if (!get_ops) { + pr_err("%s: failed to get the cfhsi_ops\n", __func__); return -ENODEV; } /* Assign the HSI device. */ - cfhsi->pdev = (*get_dev)(); - if (!cfhsi->pdev) { - pr_err("%s: failed to get the cfhsi device\n", __func__); + cfhsi->ops = (*get_ops)(); + if (!cfhsi->ops) { + pr_err("%s: failed to get the cfhsi_ops\n", __func__); goto err; } - /* Assign the HSI device. */ - cfhsi->dev = cfhsi->pdev->dev.platform_data; - /* Assign the driver to this HSI device. */ - cfhsi->dev->drv = &cfhsi->drv; - + cfhsi->ops->cb_ops = &cfhsi->cb_ops; if (register_netdevice(dev)) { - pr_warn("%s: device rtml registration failed\n", __func__); + pr_warn("%s: caif_hsi device registration failed\n", __func__); goto err; - } /* Add CAIF HSI device to list. */ list_add_tail(&cfhsi->list, &cfhsi_list); return 0; err: - symbol_put(cfhsi_get_device); + symbol_put(cfhsi_get_ops); return -ENODEV; } -- cgit v1.2.3 From 91fa0cbc0c47930f771bf5425109956cc99c6b0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Mon, 25 Jun 2012 07:49:43 +0000 Subject: caif-hsi: Remove use of module parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove use of module parameters on caif hsi device, as rtnl configuration parameters are already supported. All caif hsi configuration data is put in cfhsi_config, and default values in hsi_default_config. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 151 +++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 79 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 0927c108bd14..1c2bd01e1592 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -32,51 +32,39 @@ MODULE_DESCRIPTION("CAIF HSI driver"); #define PAD_POW2(x, pow) ((((x)&((pow)-1)) == 0) ? 0 :\ (((pow)-((x)&((pow)-1))))) -static int inactivity_timeout = 1000; -module_param(inactivity_timeout, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(inactivity_timeout, "Inactivity timeout on HSI, ms."); +static const struct cfhsi_config hsi_default_config = { -static int aggregation_timeout = 1; -module_param(aggregation_timeout, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(aggregation_timeout, "Aggregation timeout on HSI, ms."); + /* Inactivity timeout on HSI, ms */ + .inactivity_timeout = HZ, -/* - * HSI padding options. - * Warning: must be a base of 2 (& operation used) and can not be zero ! - */ -static int hsi_head_align = 4; -module_param(hsi_head_align, int, S_IRUGO); -MODULE_PARM_DESC(hsi_head_align, "HSI head alignment."); + /* Aggregation timeout (ms) of zero means no aggregation is done*/ + .aggregation_timeout = 1, -static int hsi_tail_align = 4; -module_param(hsi_tail_align, int, S_IRUGO); -MODULE_PARM_DESC(hsi_tail_align, "HSI tail alignment."); - -/* - * HSI link layer flowcontrol thresholds. - * Warning: A high threshold value migth increase throughput but it will at - * the same time prevent channel prioritization and increase the risk of - * flooding the modem. The high threshold should be above the low. - */ -static int hsi_high_threshold = 100; -module_param(hsi_high_threshold, int, S_IRUGO); -MODULE_PARM_DESC(hsi_high_threshold, "HSI high threshold (FLOW OFF)."); + /* + * HSI link layer flow-control thresholds. + * Threshold values for the HSI packet queue. Flow-control will be + * asserted when the number of packets exceeds q_high_mark. It will + * not be de-asserted before the number of packets drops below + * q_low_mark. + * Warning: A high threshold value might increase throughput but it + * will at the same time prevent channel prioritization and increase + * the risk of flooding the modem. The high threshold should be above + * the low. + */ + .q_high_mark = 100, + .q_low_mark = 50, -static int hsi_low_threshold = 50; -module_param(hsi_low_threshold, int, S_IRUGO); -MODULE_PARM_DESC(hsi_low_threshold, "HSI high threshold (FLOW ON)."); + /* + * HSI padding options. + * Warning: must be a base of 2 (& operation used) and can not be zero ! + */ + .head_align = 4, + .tail_align = 4, +}; #define ON 1 #define OFF 0 -/* - * Threshold values for the HSI packet queue. Flowcontrol will be asserted - * when the number of packets exceeds HIGH_WATER_MARK. It will not be - * de-asserted before the number of packets drops below LOW_WATER_MARK. - */ -#define LOW_WATER_MARK hsi_low_threshold -#define HIGH_WATER_MARK hsi_high_threshold - static LIST_HEAD(cfhsi_list); static void cfhsi_inactivity_tout(unsigned long arg) @@ -99,8 +87,8 @@ static void cfhsi_update_aggregation_stats(struct cfhsi *cfhsi, int hpad, tpad, len; info = (struct caif_payload_info *)&skb->cb; - hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); - tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); + tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); len = skb->len + hpad + tpad; if (direction > 0) @@ -113,7 +101,7 @@ static bool cfhsi_can_send_aggregate(struct cfhsi *cfhsi) { int i; - if (cfhsi->aggregation_timeout == 0) + if (cfhsi->cfg.aggregation_timeout == 0) return true; for (i = 0; i < CFHSI_PRIO_BEBK; ++i) { @@ -169,7 +157,7 @@ static void cfhsi_abort_tx(struct cfhsi *cfhsi) cfhsi->tx_state = CFHSI_TX_STATE_IDLE; if (!test_bit(CFHSI_SHUTDOWN, &cfhsi->bits)) mod_timer(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); } @@ -250,8 +238,8 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Calculate needed head alignment and tail alignment. */ info = (struct caif_payload_info *)&skb->cb; - hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); - tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); + tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); /* Check if frame still fits with added alignment. */ if ((skb->len + hpad + tpad) <= CFHSI_MAX_EMB_FRM_SZ) { @@ -292,8 +280,8 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi) /* Calculate needed head alignment and tail alignment. */ info = (struct caif_payload_info *)&skb->cb; - hpad = 1 + PAD_POW2((info->hdr_len + 1), hsi_head_align); - tpad = PAD_POW2((skb->len + hpad), hsi_tail_align); + hpad = 1 + PAD_POW2((info->hdr_len + 1), cfhsi->cfg.head_align); + tpad = PAD_POW2((skb->len + hpad), cfhsi->cfg.tail_align); /* Fill in CAIF frame length in descriptor. */ desc->cffrm_len[nfrms] = hpad + skb->len + tpad; @@ -364,7 +352,7 @@ static void cfhsi_start_tx(struct cfhsi *cfhsi) cfhsi->tx_state = CFHSI_TX_STATE_IDLE; /* Start inactivity timer. */ mod_timer(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); break; } @@ -390,7 +378,7 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) */ spin_lock_bh(&cfhsi->lock); if (cfhsi->flow_off_sent && - cfhsi_tx_queue_len(cfhsi) <= cfhsi->q_low_mark && + cfhsi_tx_queue_len(cfhsi) <= cfhsi->cfg.q_low_mark && cfhsi->cfdev.flowctrl) { cfhsi->flow_off_sent = 0; @@ -402,7 +390,7 @@ static void cfhsi_tx_done(struct cfhsi *cfhsi) cfhsi_start_tx(cfhsi); } else { mod_timer(&cfhsi->aggregation_timer, - jiffies + cfhsi->aggregation_timeout); + jiffies + cfhsi->cfg.aggregation_timeout); spin_unlock_bh(&cfhsi->lock); } @@ -645,7 +633,7 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) /* Update inactivity timer if pending. */ spin_lock_bh(&cfhsi->lock); mod_timer_pending(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { @@ -880,7 +868,7 @@ wake_ack: __func__); /* Start inactivity timer. */ mod_timer(&cfhsi->inactivity_timer, - jiffies + cfhsi->inactivity_timeout); + jiffies + cfhsi->cfg.inactivity_timeout); spin_unlock_bh(&cfhsi->lock); return; } @@ -1071,7 +1059,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev) /* Send flow off if number of packets is above high water mark. */ if (!cfhsi->flow_off_sent && - cfhsi_tx_queue_len(cfhsi) > cfhsi->q_high_mark && + cfhsi_tx_queue_len(cfhsi) > cfhsi->cfg.q_high_mark && cfhsi->cfdev.flowctrl) { cfhsi->flow_off_sent = 1; cfhsi->cfdev.flowctrl(cfhsi->ndev, OFF); @@ -1143,6 +1131,7 @@ static void cfhsi_setup(struct net_device *dev) cfhsi->cfdev.use_stx = false; cfhsi->cfdev.use_fcs = false; cfhsi->ndev = dev; + cfhsi->cfg = hsi_default_config; } static int cfhsi_open(struct net_device *ndev) @@ -1158,9 +1147,6 @@ static int cfhsi_open(struct net_device *ndev) /* Set flow info */ cfhsi->flow_off_sent = 0; - cfhsi->q_low_mark = LOW_WATER_MARK; - cfhsi->q_high_mark = HIGH_WATER_MARK; - /* * Allocate a TX buffer with the size of a HSI packet descriptors @@ -1188,20 +1174,8 @@ static int cfhsi_open(struct net_device *ndev) goto err_alloc_rx_flip; } - /* Pre-calculate inactivity timeout. */ - if (inactivity_timeout != -1) { - cfhsi->inactivity_timeout = - inactivity_timeout * HZ / 1000; - if (!cfhsi->inactivity_timeout) - cfhsi->inactivity_timeout = 1; - else if (cfhsi->inactivity_timeout > NEXT_TIMER_MAX_DELTA) - cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA; - } else { - cfhsi->inactivity_timeout = NEXT_TIMER_MAX_DELTA; - } - /* Initialize aggregation timeout */ - cfhsi->aggregation_timeout = aggregation_timeout; + cfhsi->cfg.aggregation_timeout = hsi_default_config.aggregation_timeout; /* Initialize recieve vaiables. */ cfhsi->rx_ptr = cfhsi->rx_buf; @@ -1350,24 +1324,39 @@ static void cfhsi_netlink_parms(struct nlattr *data[], struct cfhsi *cfhsi) } i = __IFLA_CAIF_HSI_INACTIVITY_TOUT; - if (data[i]) - inactivity_timeout = nla_get_u32(data[i]); + /* + * Inactivity timeout in millisecs. Lowest possible value is 1, + * and highest possible is NEXT_TIMER_MAX_DELTA. + */ + if (data[i]) { + u32 inactivity_timeout = nla_get_u32(data[i]); + /* Pre-calculate inactivity timeout. */ + cfhsi->cfg.inactivity_timeout = inactivity_timeout * HZ / 1000; + if (cfhsi->cfg.inactivity_timeout == 0) + cfhsi->cfg.inactivity_timeout = 1; + else if (cfhsi->cfg.inactivity_timeout > NEXT_TIMER_MAX_DELTA) + cfhsi->cfg.inactivity_timeout = NEXT_TIMER_MAX_DELTA; + } i = __IFLA_CAIF_HSI_AGGREGATION_TOUT; if (data[i]) - aggregation_timeout = nla_get_u32(data[i]); + cfhsi->cfg.aggregation_timeout = nla_get_u32(data[i]); i = __IFLA_CAIF_HSI_HEAD_ALIGN; if (data[i]) - hsi_head_align = nla_get_u32(data[i]); + cfhsi->cfg.head_align = nla_get_u32(data[i]); i = __IFLA_CAIF_HSI_TAIL_ALIGN; if (data[i]) - hsi_tail_align = nla_get_u32(data[i]); + cfhsi->cfg.tail_align = nla_get_u32(data[i]); i = __IFLA_CAIF_HSI_QHIGH_WATERMARK; if (data[i]) - hsi_high_threshold = nla_get_u32(data[i]); + cfhsi->cfg.q_high_mark = nla_get_u32(data[i]); + + i = __IFLA_CAIF_HSI_QLOW_WATERMARK; + if (data[i]) + cfhsi->cfg.q_low_mark = nla_get_u32(data[i]); } static int caif_hsi_changelink(struct net_device *dev, struct nlattr *tb[], @@ -1398,16 +1387,20 @@ static size_t caif_hsi_get_size(const struct net_device *dev) static int caif_hsi_fill_info(struct sk_buff *skb, const struct net_device *dev) { + struct cfhsi *cfhsi = netdev_priv(dev); + if (nla_put_u32(skb, __IFLA_CAIF_HSI_INACTIVITY_TOUT, - inactivity_timeout) || + cfhsi->cfg.inactivity_timeout) || nla_put_u32(skb, __IFLA_CAIF_HSI_AGGREGATION_TOUT, - aggregation_timeout) || - nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, hsi_head_align) || - nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, hsi_tail_align) || + cfhsi->cfg.aggregation_timeout) || + nla_put_u32(skb, __IFLA_CAIF_HSI_HEAD_ALIGN, + cfhsi->cfg.head_align) || + nla_put_u32(skb, __IFLA_CAIF_HSI_TAIL_ALIGN, + cfhsi->cfg.tail_align) || nla_put_u32(skb, __IFLA_CAIF_HSI_QHIGH_WATERMARK, - hsi_high_threshold) || + cfhsi->cfg.q_high_mark) || nla_put_u32(skb, __IFLA_CAIF_HSI_QLOW_WATERMARK, - hsi_low_threshold)) + cfhsi->cfg.q_low_mark)) return -EMSGSIZE; return 0; -- cgit v1.2.3 From 5e7a76be0e48217aff6b6f34bdcce4725db999e2 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Mon, 25 Jun 2012 17:34:14 +0000 Subject: net/sh-eth: Add support selecting MII function for SH7734 and R8A7740 Ethernet IP of SH7734 and R8A7740 has selecting MII register. The user needs to change a value according to MII to be used. This adds the function to change the value of this register. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 108 ++++++++++++++++++++-------------- drivers/net/ethernet/renesas/sh_eth.h | 1 + 2 files changed, 66 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 79bf09b41971..8d696e0c082b 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -49,6 +49,34 @@ NETIF_MSG_RX_ERR| \ NETIF_MSG_TX_ERR) +#if defined(CONFIG_CPU_SUBTYPE_SH7734) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ + defined(CONFIG_ARCH_R8A7740) +static void sh_eth_select_mii(struct net_device *ndev) +{ + u32 value = 0x0; + struct sh_eth_private *mdp = netdev_priv(ndev); + + switch (mdp->phy_interface) { + case PHY_INTERFACE_MODE_GMII: + value = 0x2; + break; + case PHY_INTERFACE_MODE_MII: + value = 0x1; + break; + case PHY_INTERFACE_MODE_RMII: + value = 0x0; + break; + default: + pr_warn("PHY interface mode was not setup. Set to MII.\n"); + value = 0x1; + break; + } + + sh_eth_write(ndev, value, RMII_MII); +} +#endif + /* There is CPU dependent code */ #if defined(CONFIG_CPU_SUBTYPE_SH7724) #define SH_ETH_RESET_DEFAULT 1 @@ -283,6 +311,7 @@ static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) #elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) #define SH_ETH_HAS_TSU 1 static void sh_eth_reset_hw_crc(struct net_device *ndev); + static void sh_eth_chip_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -292,35 +321,6 @@ static void sh_eth_chip_reset(struct net_device *ndev) mdelay(1); } -static void sh_eth_reset(struct net_device *ndev) -{ - int cnt = 100; - - sh_eth_write(ndev, EDSR_ENALL, EDSR); - sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - while (cnt > 0) { - if (!(sh_eth_read(ndev, EDMR) & 0x3)) - break; - mdelay(1); - cnt--; - } - if (cnt == 0) - printk(KERN_ERR "Device reset fail\n"); - - /* Table Init */ - sh_eth_write(ndev, 0x0, TDLAR); - sh_eth_write(ndev, 0x0, TDFAR); - sh_eth_write(ndev, 0x0, TDFXR); - sh_eth_write(ndev, 0x0, TDFFR); - sh_eth_write(ndev, 0x0, RDLAR); - sh_eth_write(ndev, 0x0, RDFAR); - sh_eth_write(ndev, 0x0, RDFXR); - sh_eth_write(ndev, 0x0, RDFFR); - - /* Reset HW CRC register */ - sh_eth_reset_hw_crc(ndev); -} - static void sh_eth_set_duplex(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -377,9 +377,43 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .tsu = 1, #if defined(CONFIG_CPU_SUBTYPE_SH7734) .hw_crc = 1, + .select_mii = 1, #endif }; +static void sh_eth_reset(struct net_device *ndev) +{ + int cnt = 100; + + sh_eth_write(ndev, EDSR_ENALL, EDSR); + sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); + while (cnt > 0) { + if (!(sh_eth_read(ndev, EDMR) & 0x3)) + break; + mdelay(1); + cnt--; + } + if (cnt == 0) + printk(KERN_ERR "Device reset fail\n"); + + /* Table Init */ + sh_eth_write(ndev, 0x0, TDLAR); + sh_eth_write(ndev, 0x0, TDFAR); + sh_eth_write(ndev, 0x0, TDFXR); + sh_eth_write(ndev, 0x0, TDFFR); + sh_eth_write(ndev, 0x0, RDLAR); + sh_eth_write(ndev, 0x0, RDFAR); + sh_eth_write(ndev, 0x0, RDFXR); + sh_eth_write(ndev, 0x0, RDFFR); + + /* Reset HW CRC register */ + sh_eth_reset_hw_crc(ndev); + + /* Select MII mode */ + if (sh_eth_my_cpu_data.select_mii) + sh_eth_select_mii(ndev); +} + static void sh_eth_reset_hw_crc(struct net_device *ndev) { if (sh_eth_my_cpu_data.hw_crc) @@ -391,25 +425,12 @@ static void sh_eth_reset_hw_crc(struct net_device *ndev) static void sh_eth_chip_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); - unsigned long mii; /* reset device */ sh_eth_tsu_write(mdp, ARSTR_ARSTR, ARSTR); mdelay(1); - switch (mdp->phy_interface) { - case PHY_INTERFACE_MODE_GMII: - mii = 2; - break; - case PHY_INTERFACE_MODE_MII: - mii = 1; - break; - case PHY_INTERFACE_MODE_RMII: - default: - mii = 0; - break; - } - sh_eth_write(ndev, mii, RMII_MII); + sh_eth_select_mii(ndev); } static void sh_eth_reset(struct net_device *ndev) @@ -492,6 +513,7 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { .no_trimd = 1, .no_ade = 1, .tsu = 1, + .select_mii = 1, }; #elif defined(CONFIG_CPU_SUBTYPE_SH7619) diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 57b8e1fc5d15..d6763b1392d7 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -757,6 +757,7 @@ struct sh_eth_cpu_data { unsigned no_trimd:1; /* E-DMAC DO NOT have TRIMD */ unsigned no_ade:1; /* E-DMAC DO NOT have ADE bit in EESR */ unsigned hw_crc:1; /* E-DMAC have CSMR */ + unsigned select_mii:1; /* EtherC have RMII_MII (MII select register) */ }; struct sh_eth_private { -- cgit v1.2.3 From 5cee1d37c9f565f1aa515408863dbb13db67dab9 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Mon, 25 Jun 2012 17:35:12 +0000 Subject: net/sh-eth: Check return value of sh_eth_reset when chip reset fail The sh_eth_reset function resets chip, but this performs nothing when failed. This changes sh_eth_reset return an error, when this failed in reset. Signed-off-by: Nobuhiro Iwamatsu Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 88 ++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 8d696e0c082b..326cb91abdad 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -130,6 +130,8 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { #elif defined(CONFIG_CPU_SUBTYPE_SH7757) #define SH_ETH_HAS_BOTH_MODULES 1 #define SH_ETH_HAS_TSU 1 +static int sh_eth_check_reset(struct net_device *ndev); + static void sh_eth_set_duplex(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -204,23 +206,19 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev) } static int sh_eth_is_gether(struct sh_eth_private *mdp); -static void sh_eth_reset(struct net_device *ndev) +static int sh_eth_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); - int cnt = 100; + int ret = 0; if (sh_eth_is_gether(mdp)) { sh_eth_write(ndev, 0x03, EDSR); sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - while (cnt > 0) { - if (!(sh_eth_read(ndev, EDMR) & 0x3)) - break; - mdelay(1); - cnt--; - } - if (cnt < 0) - printk(KERN_ERR "Device reset fail\n"); + + ret = sh_eth_check_reset(ndev); + if (ret) + goto out; /* Table Init */ sh_eth_write(ndev, 0x0, TDLAR); @@ -238,6 +236,9 @@ static void sh_eth_reset(struct net_device *ndev) sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, EDMR); } + +out: + return ret; } static void sh_eth_set_duplex_giga(struct net_device *ndev) @@ -310,6 +311,7 @@ static struct sh_eth_cpu_data *sh_eth_get_cpu_data(struct sh_eth_private *mdp) #elif defined(CONFIG_CPU_SUBTYPE_SH7734) || defined(CONFIG_CPU_SUBTYPE_SH7763) #define SH_ETH_HAS_TSU 1 +static int sh_eth_check_reset(struct net_device *ndev); static void sh_eth_reset_hw_crc(struct net_device *ndev); static void sh_eth_chip_reset(struct net_device *ndev) @@ -381,20 +383,16 @@ static struct sh_eth_cpu_data sh_eth_my_cpu_data = { #endif }; -static void sh_eth_reset(struct net_device *ndev) +static int sh_eth_reset(struct net_device *ndev) { - int cnt = 100; + int ret = 0; sh_eth_write(ndev, EDSR_ENALL, EDSR); sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - while (cnt > 0) { - if (!(sh_eth_read(ndev, EDMR) & 0x3)) - break; - mdelay(1); - cnt--; - } - if (cnt == 0) - printk(KERN_ERR "Device reset fail\n"); + + ret = sh_eth_check_reset(ndev); + if (ret) + goto out; /* Table Init */ sh_eth_write(ndev, 0x0, TDLAR); @@ -412,6 +410,8 @@ static void sh_eth_reset(struct net_device *ndev) /* Select MII mode */ if (sh_eth_my_cpu_data.select_mii) sh_eth_select_mii(ndev); +out: + return ret; } static void sh_eth_reset_hw_crc(struct net_device *ndev) @@ -422,6 +422,8 @@ static void sh_eth_reset_hw_crc(struct net_device *ndev) #elif defined(CONFIG_ARCH_R8A7740) #define SH_ETH_HAS_TSU 1 +static int sh_eth_check_reset(struct net_device *ndev); + static void sh_eth_chip_reset(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -433,20 +435,16 @@ static void sh_eth_chip_reset(struct net_device *ndev) sh_eth_select_mii(ndev); } -static void sh_eth_reset(struct net_device *ndev) +static int sh_eth_reset(struct net_device *ndev) { - int cnt = 100; + int ret = 0; sh_eth_write(ndev, EDSR_ENALL, EDSR); sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_GETHER, EDMR); - while (cnt > 0) { - if (!(sh_eth_read(ndev, EDMR) & 0x3)) - break; - mdelay(1); - cnt--; - } - if (cnt == 0) - printk(KERN_ERR "Device reset fail\n"); + + ret = sh_eth_check_reset(ndev); + if (ret) + goto out; /* Table Init */ sh_eth_write(ndev, 0x0, TDLAR); @@ -457,6 +455,9 @@ static void sh_eth_reset(struct net_device *ndev) sh_eth_write(ndev, 0x0, RDFAR); sh_eth_write(ndev, 0x0, RDFXR); sh_eth_write(ndev, 0x0, RDFFR); + +out: + return ret; } static void sh_eth_set_duplex(struct net_device *ndev) @@ -565,11 +566,31 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd) #if defined(SH_ETH_RESET_DEFAULT) /* Chip Reset */ -static void sh_eth_reset(struct net_device *ndev) +static int sh_eth_reset(struct net_device *ndev) { sh_eth_write(ndev, sh_eth_read(ndev, EDMR) | EDMR_SRST_ETHER, EDMR); mdelay(3); sh_eth_write(ndev, sh_eth_read(ndev, EDMR) & ~EDMR_SRST_ETHER, EDMR); + + return 0; +} +#else +static int sh_eth_check_reset(struct net_device *ndev) +{ + int ret = 0; + int cnt = 100; + + while (cnt > 0) { + if (!(sh_eth_read(ndev, EDMR) & 0x3)) + break; + mdelay(1); + cnt--; + } + if (cnt < 0) { + printk(KERN_ERR "Device reset fail\n"); + ret = -ETIMEDOUT; + } + return ret; } #endif @@ -924,7 +945,9 @@ static int sh_eth_dev_init(struct net_device *ndev) u32 val; /* Soft Reset */ - sh_eth_reset(ndev); + ret = sh_eth_reset(ndev); + if (ret) + goto out; /* Descriptor format */ sh_eth_ring_format(ndev); @@ -998,6 +1021,7 @@ static int sh_eth_dev_init(struct net_device *ndev) netif_start_queue(ndev); +out: return ret; } -- cgit v1.2.3 From 7f217d36dce7e3e2789cfbd91ae53a36a98df837 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 26 Jun 2012 18:37:58 +0800 Subject: regulator: arizona-micsupp: Fix choosing selector in arizona_micsupp_map_voltage If min_uV is in the range of: 3250001~3269999, current code uses the equation: selector = DIV_ROUND_UP(min_uV - 1700000, 50000); Then selector will be 32. Then arizona_micsupp_list_voltage returns -EINVAL for this case which is wrong. This patch fixes this issue: If min_uV > 3200000, selector should be ARIZONA_MICSUPP_MAX_SELECTOR. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/arizona-micsupp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 97d85b09a940..fdd7473ada4a 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -57,7 +57,7 @@ static int arizona_micsupp_map_voltage(struct regulator_dev *rdev, if (min_uV < 1700000) min_uV = 1700000; - if (min_uV >= 3300000) + if (min_uV > 3200000) selector = ARIZONA_MICSUPP_MAX_SELECTOR; else selector = DIV_ROUND_UP(min_uV - 1700000, 50000); -- cgit v1.2.3 From e2eb169b1bc207dd1a79109d85b098b241be2e9b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jun 2012 14:25:27 +0800 Subject: regulator: s5m8767: Convert to regulator_list_voltage_linear In current code, .list_voltage and .set_voltage_sel callbacks for BUCK7 and BUCK8 return -EINVAL. This patch adds s5m8767_buck78_ops for BUCK7 and BUCK8 which does not set .list_voltage, .get_voltage_sel and .set_voltage_sel. ( This has the same effect of returning -EINVAL in the callbacks) Then for all the users of s5m8767_list_voltage, we don't need to worry about the case reg_voltage_map[reg_id] is NULL. So we can convert s5m8767_list_voltage to regulator_list_voltage_linear. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 46 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 533bde68e4b7..1ad9c3c0da8d 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -121,27 +121,6 @@ static const struct s5m_voltage_desc *reg_voltage_map[] = { [S5M8767_BUCK9] = &buck_voltage_val3, }; -static int s5m8767_list_voltage(struct regulator_dev *rdev, - unsigned int selector) -{ - const struct s5m_voltage_desc *desc; - int reg_id = rdev_get_id(rdev); - int val; - - if (reg_id >= ARRAY_SIZE(reg_voltage_map) || reg_id < 0) - return -EINVAL; - - desc = reg_voltage_map[reg_id]; - if (desc == NULL) - return -EINVAL; - - val = desc->min + desc->step * selector; - if (val > desc->max) - return -EINVAL; - - return val; -} - static unsigned int s5m8767_opmode_reg[][4] = { /* {OFF, ON, LOWPOWER, SUSPEND} */ /* LDO1 ... LDO28 */ @@ -449,7 +428,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, } static struct regulator_ops s5m8767_ops = { - .list_voltage = s5m8767_list_voltage, + .list_voltage = regulator_list_voltage_linear, .is_enabled = s5m8767_reg_is_enabled, .enable = s5m8767_reg_enable, .disable = s5m8767_reg_disable, @@ -458,6 +437,12 @@ static struct regulator_ops s5m8767_ops = { .set_voltage_time_sel = s5m8767_set_voltage_time_sel, }; +static struct regulator_ops s5m8767_buck78_ops = { + .is_enabled = s5m8767_reg_is_enabled, + .enable = s5m8767_reg_enable, + .disable = s5m8767_reg_disable, +}; + #define s5m8767_regulator_desc(_name) { \ .name = #_name, \ .id = S5M8767_##_name, \ @@ -466,6 +451,14 @@ static struct regulator_ops s5m8767_ops = { .owner = THIS_MODULE, \ } +#define s5m8767_regulator_buck78_desc(_name) { \ + .name = #_name, \ + .id = S5M8767_##_name, \ + .ops = &s5m8767_buck78_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ +} + static struct regulator_desc regulators[] = { s5m8767_regulator_desc(LDO1), s5m8767_regulator_desc(LDO2), @@ -501,8 +494,8 @@ static struct regulator_desc regulators[] = { s5m8767_regulator_desc(BUCK4), s5m8767_regulator_desc(BUCK5), s5m8767_regulator_desc(BUCK6), - s5m8767_regulator_desc(BUCK7), - s5m8767_regulator_desc(BUCK8), + s5m8767_regulator_buck78_desc(BUCK7), + s5m8767_regulator_buck78_desc(BUCK8), s5m8767_regulator_desc(BUCK9), }; @@ -751,9 +744,12 @@ static __devinit int s5m8767_pmic_probe(struct platform_device *pdev) int id = pdata->regulators[i].id; desc = reg_voltage_map[id]; - if (desc) + if (desc) { regulators[id].n_voltages = (desc->max - desc->min) / desc->step + 1; + regulators[id].min_uV = desc->min; + regulators[id].uV_step = desc->step; + } config.dev = s5m8767->dev; config.init_data = pdata->regulators[i].initdata; -- cgit v1.2.3 From 5aff3a8b20c54888e199e863744848007f1f4120 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Jun 2012 11:16:58 +0100 Subject: regulator: core: Check for failed voltage sets before checking for delay There is no need to consider waiting for the voltage to ramp if we didn't manage to set it and looking at the return value is going to be cheaper than is_enabled(). Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 729b20d1c1de..890fa5eb770d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2143,7 +2143,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, best_val = _regulator_get_voltage(rdev); /* Call set_voltage_time_sel if successfully obtained old_selector */ - if (_regulator_is_enabled(rdev) && ret == 0 && old_selector >= 0 && + if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 && rdev->desc->ops->set_voltage_time_sel) { delay = rdev->desc->ops->set_voltage_time_sel(rdev, -- cgit v1.2.3 From 222cc7b1cefbab1287ed5d5e3cce1f45aec60d39 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 Jun 2012 11:39:16 +0100 Subject: regulator: core: Allow multiple requests of a single supply mapping Sometimes it may be useful to allow a device to request a supply multiple times, for example in order to allow framework management of some uses of the supply with some additional driver specific management or in order to allow multiple children of an MFD to work with the supply. Currently this is not possible due to the creation of Solve this by removing the requested_uA entry (we have no current users of this feature anyway) and ignoring errors creating the symlink to the consumer. We should do something nicer than this as this causes sysfs to spew enormous warnings but it allows users to run for now. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 49 +++++------------------------------------------- 1 file changed, 5 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 890fa5eb770d..00c787c01f3b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -303,18 +303,6 @@ static int regulator_check_drms(struct regulator_dev *rdev) return 0; } -static ssize_t device_requested_uA_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct regulator *regulator; - - regulator = get_device_regulator(dev); - if (regulator == NULL) - return 0; - - return sprintf(buf, "%d\n", regulator->uA_load); -} - static ssize_t regulator_uV_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1097,48 +1085,27 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, list_add(®ulator->list, &rdev->consumer_list); if (dev) { - /* create a 'requested_microamps_name' sysfs entry */ - size = scnprintf(buf, REG_STR_SIZE, - "microamps_requested_%s-%s", - dev_name(dev), supply_name); - if (size >= REG_STR_SIZE) - goto overflow_err; - - regulator->dev = dev; - sysfs_attr_init(®ulator->dev_attr.attr); - regulator->dev_attr.attr.name = kstrdup(buf, GFP_KERNEL); - if (regulator->dev_attr.attr.name == NULL) - goto attr_name_err; - - regulator->dev_attr.attr.mode = 0444; - regulator->dev_attr.show = device_requested_uA_show; - err = device_create_file(dev, ®ulator->dev_attr); - if (err < 0) { - rdev_warn(rdev, "could not add regulator_dev requested microamps sysfs entry\n"); - goto attr_name_err; - } - - /* also add a link to the device sysfs entry */ + /* Add a link to the device sysfs entry */ size = scnprintf(buf, REG_STR_SIZE, "%s-%s", dev->kobj.name, supply_name); if (size >= REG_STR_SIZE) - goto attr_err; + goto overflow_err; regulator->supply_name = kstrdup(buf, GFP_KERNEL); if (regulator->supply_name == NULL) - goto attr_err; + goto overflow_err; err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj, buf); if (err) { rdev_warn(rdev, "could not add device link %s err %d\n", dev->kobj.name, err); - goto link_name_err; + /* non-fatal */ } } else { regulator->supply_name = kstrdup(supply_name, GFP_KERNEL); if (regulator->supply_name == NULL) - goto attr_err; + goto overflow_err; } regulator->debugfs = debugfs_create_dir(regulator->supply_name, @@ -1165,12 +1132,6 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, mutex_unlock(&rdev->mutex); return regulator; -link_name_err: - kfree(regulator->supply_name); -attr_err: - device_remove_file(regulator->dev, ®ulator->dev_attr); -attr_name_err: - kfree(regulator->dev_attr.attr.name); overflow_err: list_del(®ulator->list); kfree(regulator); -- cgit v1.2.3 From 2b6f2c3520124e8bad4bffa71f5b98e602b9cf03 Mon Sep 17 00:00:00 2001 From: Myron Stowe Date: Mon, 25 Jun 2012 21:30:57 -0600 Subject: PCI: pull pcibios_setup() up into core Currently, all of the architectures implement their own pcibios_setup() routine. Most of the implementations do nothing so this patch introduces a generic (__weak) routine in the core that can be used by all architectures as a default. If necessary, it can be overridden by architecture-specific code. Signed-off-by: Myron Stowe Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..c87d518acace 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2665,6 +2665,18 @@ static void __pci_set_master(struct pci_dev *dev, bool enable) dev->is_busmaster = enable; } +/** + * pcibios_setup - process "pci=" kernel boot arguments + * @str: string used to pass in "pci=" kernel boot arguments + * + * Process kernel boot arguments. This is the default implementation. + * Architecture specific implementations can override this as necessary. + */ +char * __weak __init pcibios_setup(char *str) +{ + return str; +} + /** * pcibios_set_master - enable PCI bus-mastering for device dev * @dev: the PCI device to enable -- cgit v1.2.3 From e59bec1628654b6dcbad4e64d43c41c1f31d216c Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 25 Jun 2012 14:15:55 +0300 Subject: wl18xx: deprecate PG1 support The new PG2 version of the chip has a few differences in terms of FW API if compared to PG1. PG1 is just a sample that shouldn't be used in real life, so to avoid having to handle both separately, mark the PG1 version as deprecated and bail out during probe. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wl18xx/acx.h | 34 ++++++++++++-------------- drivers/net/wireless/ti/wl18xx/main.c | 45 ++++++----------------------------- 2 files changed, 22 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index ebbaf611e97b..e2609a6b7341 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -32,25 +32,21 @@ enum { /* numbers of bits the length field takes (add 1 for the actual number) */ #define WL18XX_HOST_IF_LEN_SIZE_FIELD 15 -#define WL18XX_ACX_EVENTS_VECTOR_PG1 (WL1271_ACX_INTR_WATCHDOG | \ - WL1271_ACX_INTR_INIT_COMPLETE | \ - WL1271_ACX_INTR_EVENT_A | \ - WL1271_ACX_INTR_EVENT_B | \ - WL1271_ACX_INTR_CMD_COMPLETE | \ - WL1271_ACX_INTR_HW_AVAILABLE | \ - WL1271_ACX_INTR_DATA) - -#define WL18XX_ACX_EVENTS_VECTOR_PG2 (WL18XX_ACX_EVENTS_VECTOR_PG1 | \ - WL1271_ACX_SW_INTR_WATCHDOG) - -#define WL18XX_INTR_MASK_PG1 (WL1271_ACX_INTR_WATCHDOG | \ - WL1271_ACX_INTR_EVENT_A | \ - WL1271_ACX_INTR_EVENT_B | \ - WL1271_ACX_INTR_HW_AVAILABLE | \ - WL1271_ACX_INTR_DATA) - -#define WL18XX_INTR_MASK_PG2 (WL18XX_INTR_MASK_PG1 | \ - WL1271_ACX_SW_INTR_WATCHDOG) +#define WL18XX_ACX_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_INIT_COMPLETE | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_CMD_COMPLETE | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA | \ + WL1271_ACX_SW_INTR_WATCHDOG) + +#define WL18XX_INTR_MASK (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA | \ + WL1271_ACX_SW_INTR_WATCHDOG) struct wl18xx_acx_host_config_bitmap { struct acx_header header; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index c25b960faa29..5e583be8f674 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -612,20 +612,11 @@ static int wl18xx_identify_chip(struct wl1271 *wl) WLCORE_QUIRK_TX_PAD_LAST_FRAME; break; case CHIP_ID_185x_PG10: - wl1271_debug(DEBUG_BOOT, "chip id 0x%x (185x PG10)", - wl->chip.id); - wl->sr_fw_name = WL18XX_FW_NAME; - /* wl18xx uses the same firmware for PLT */ - wl->plt_fw_name = WL18XX_FW_NAME; - wl->quirks |= WLCORE_QUIRK_NO_ELP | - WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED | - WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN | - WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN; - - /* PG 1.0 has some problems with MCS_13, so disable it */ - wl->ht_cap[IEEE80211_BAND_2GHZ].mcs.rx_mask[1] &= ~BIT(5); + wl1271_warning("chip id 0x%x (185x PG10) is deprecated", + wl->chip.id); + ret = -ENODEV; + goto out; - break; default: wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); ret = -ENODEV; @@ -776,21 +767,14 @@ out: static int wl18xx_set_mac_and_phy(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; - size_t len; int ret; - /* the parameters struct is smaller for PG1 */ - if (wl->chip.id == CHIP_ID_185x_PG10) - len = offsetof(struct wl18xx_mac_and_phy_params, psat) + 1; - else - len = sizeof(struct wl18xx_mac_and_phy_params); - ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]); if (ret < 0) goto out; ret = wlcore_write(wl, WL18XX_PHY_INIT_MEM_ADDR, (u8 *)&priv->conf.phy, - len, false); + sizeof(struct wl18xx_mac_and_phy_params), false); out: return ret; @@ -801,13 +785,8 @@ static int wl18xx_enable_interrupts(struct wl1271 *wl) u32 event_mask, intr_mask; int ret; - if (wl->chip.id == CHIP_ID_185x_PG10) { - event_mask = WL18XX_ACX_EVENTS_VECTOR_PG1; - intr_mask = WL18XX_INTR_MASK_PG1; - } else { - event_mask = WL18XX_ACX_EVENTS_VECTOR_PG2; - intr_mask = WL18XX_INTR_MASK_PG2; - } + event_mask = WL18XX_ACX_EVENTS_VECTOR; + intr_mask = WL18XX_INTR_MASK; ret = wlcore_write_reg(wl, REG_INTERRUPT_MASK, event_mask); if (ret < 0) @@ -1049,16 +1028,6 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, } else if (!strcmp(ht_mode_param, "mimo")) { wl1271_debug(DEBUG_ACX, "using MIMO rate mask"); - /* - * PG 1.0 has some problems with MCS_13, so disable it - * - * TODO: instead of hacking this in here, we should - * make it more general and change a bit in the - * wlvif->rate_set instead. - */ - if (wl->chip.id == CHIP_ID_185x_PG10) - return CONF_TX_MIMO_RATES & ~CONF_HW_BIT_RATE_MCS_13; - return CONF_TX_MIMO_RATES; } else { return 0; -- cgit v1.2.3 From 8b425e62d96a3b3a3cc68e6203267f92d1a01946 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 25 Jun 2012 14:41:20 +0300 Subject: wlcore: fix some failure cases in wlcore_probe() We need to release the IRQ if hw_info() or identify_chip() fails. And we need unregister the HW with mac80211 if there are any failures after it's registered. Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 372ccf277b1e..c5a9ffe6cc91 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5520,12 +5520,12 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) ret = wl12xx_get_hw_info(wl); if (ret < 0) { wl1271_error("couldn't get hw info"); - goto out; + goto out_irq; } ret = wl->ops->identify_chip(wl); if (ret < 0) - goto out; + goto out_irq; ret = wl1271_init_ieee80211(wl); if (ret) @@ -5539,7 +5539,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); if (ret < 0) { wl1271_error("failed to create sysfs file bt_coex_state"); - goto out_irq; + goto out_unreg; } /* Create sysfs file to get HW PG version */ @@ -5564,6 +5564,9 @@ out_hw_pg_ver: out_bt_coex_state: device_remove_file(wl->dev, &dev_attr_bt_coex_state); +out_unreg: + wl1271_unregister_hw(wl); + out_irq: free_irq(wl->irq, wl); -- cgit v1.2.3 From 7a50bdfb81a6bff96100cd2a2c95f8b3cf05bc0c Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 26 Jun 2012 10:41:15 +0300 Subject: wlcore: fix broken TX due to wrong queuing of recovery commit 14bba17b "wl12xx: Propagate errors from wl1271_raw_write32" breaks down TX in certain scenarios. wl1271_irq_locked() propagates errors from wl1271_tx_work_locked however it may return -EBUSY when the FW queues are full which is a legitimate case and not a a real error. In this case a recovery is triggered by wl1271_irq and this keeps repeating itself so TX is completely broken. Fix it by avoiding propagating return values as errors even if they aren't. Only bus (SDIO or SPI) ops failures would be progagated as only these should trigger recovery. Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/tx.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index b5211be229d9..6a28aeecf004 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -352,8 +352,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool is_dummy; bool is_gem = false; - if (!skb) + if (!skb) { + wl1271_error("discarding null skb"); return -EINVAL; + } info = IEEE80211_SKB_CB(skb); @@ -662,6 +664,16 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids) } } +/* + * Returns failure values only in case of failed bus ops within this function. + * wl1271_prepare_tx_frame retvals won't be returned in order to avoid + * triggering recovery by higher layers when not necessary. + * In case a FW command fails within wl1271_prepare_tx_frame fails a recovery + * will be queued in wl1271_cmd_send. -EAGAIN/-EBUSY from prepare_tx_frame + * can occur and are legitimate so don't propagate. -EINVAL will emit a WARNING + * within prepare_tx_frame code but there's nothing we should do about those + * as well. + */ int wlcore_tx_work_locked(struct wl1271 *wl) { struct wl12xx_vif *wlvif; @@ -671,9 +683,10 @@ int wlcore_tx_work_locked(struct wl1271 *wl) bool sent_packets = false; unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; int ret = 0; + int bus_ret = 0; if (unlikely(wl->state == WL1271_STATE_OFF)) - return -EIO; + return 0; while ((skb = wl1271_skb_dequeue(wl))) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -694,9 +707,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl) buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); - ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, - wl->aggr_buf, buf_offset, true); - if (ret < 0) + bus_ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, + wl->aggr_buf, buf_offset, true); + if (bus_ret < 0) goto out; sent_packets = true; @@ -734,9 +747,9 @@ int wlcore_tx_work_locked(struct wl1271 *wl) out_ack: if (buf_offset) { buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset, last_len); - ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, - buf_offset, true); - if (ret < 0) + bus_ret = wlcore_write_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf, + buf_offset, true); + if (bus_ret < 0) goto out; sent_packets = true; @@ -747,9 +760,9 @@ out_ack: * required for older hardware revisions */ if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION) { - ret = wlcore_write32(wl, WL12XX_HOST_WR_ACCESS, + bus_ret = wlcore_write32(wl, WL12XX_HOST_WR_ACCESS, wl->tx_packets_count); - if (ret < 0) + if (bus_ret < 0) goto out; } @@ -758,7 +771,7 @@ out_ack: wl12xx_rearm_rx_streaming(wl, active_hlids); out: - return ret; + return bus_ret; } void wl1271_tx_work(struct work_struct *work) -- cgit v1.2.3 From 6c15c1aae206dc275a948a5e50f965c2382c6866 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 26 Jun 2012 10:41:16 +0300 Subject: wlcore: queue recovery in case of bus errors during cmd_remove_peer Following the addition of propagating errors from the bus ops there's a need to distinguish between bus errors (including timeout) and a legitimate timeout occuring in cmd_wait_for_event_or_timeout. In case of real bus errors we need to queue recovery even in cases where a timeout on a response from the FW to a command is acceptable. Reported-by: Arik Nemtsov Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 84dd808f65fa..ef139383ee93 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -133,24 +133,27 @@ fail: * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs) */ -static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask) +static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, + u32 mask, bool *timeout) { u32 *events_vector; u32 event; - unsigned long timeout; + unsigned long timeout_time; int ret = 0; + *timeout = false; + events_vector = kmalloc(sizeof(*events_vector), GFP_KERNEL | GFP_DMA); if (!events_vector) return -ENOMEM; - timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); + timeout_time = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); do { - if (time_after(jiffies, timeout)) { + if (time_after(jiffies, timeout_time)) { wl1271_debug(DEBUG_CMD, "timeout waiting for event %d", (int)mask); - ret = -ETIMEDOUT; + *timeout = true; goto out; } @@ -180,9 +183,10 @@ out: static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) { int ret; + bool timeout = false; - ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask); - if (ret != 0) { + ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask, &timeout); + if (ret != 0 || timeout) { wl12xx_queue_recovery_work(wl); return ret; } @@ -1435,6 +1439,7 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) { struct wl12xx_cmd_remove_peer *cmd; int ret; + bool timeout = false; wl1271_debug(DEBUG_CMD, "cmd remove peer %d", (int)hlid); @@ -1455,12 +1460,16 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) goto out_free; } + ret = wl1271_cmd_wait_for_event_or_timeout(wl, + PEER_REMOVE_COMPLETE_EVENT_ID, + &timeout); /* * We are ok with a timeout here. The event is sometimes not sent - * due to a firmware bug. + * due to a firmware bug. In case of another error (like SDIO timeout) + * queue a recovery. */ - wl1271_cmd_wait_for_event_or_timeout(wl, - PEER_REMOVE_COMPLETE_EVENT_ID); + if (ret) + wl12xx_queue_recovery_work(wl); out_free: kfree(cmd); -- cgit v1.2.3 From 680c6055b9bebdf07fc2d5ebe816a14c7daecdc1 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 26 Jun 2012 10:41:17 +0300 Subject: wlcore: print stack trace in every recovery As recovery queuing can now occur from multiple code paths it's convenient to know what triggered it in all cases other than an intended recovery which is part of the switch between single role to multi role. Signed-off-by: Eyal Shapira Signed-off-by: Luciano Coelho --- drivers/net/wireless/ti/wlcore/cmd.c | 1 - drivers/net/wireless/ti/wlcore/main.c | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index ef139383ee93..56c7a2342fdf 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -124,7 +124,6 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, return 0; fail: - WARN_ON(1); wl12xx_queue_recovery_work(wl); return ret; } diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index c5a9ffe6cc91..2240cca597ac 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -772,6 +772,8 @@ out: void wl12xx_queue_recovery_work(struct wl1271 *wl) { + WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); + /* Avoid a recursive recovery */ if (!test_and_set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) { wlcore_disable_interrupts_nosync(wl); -- cgit v1.2.3 From 5a2766abe42a0fa10a6a7177bece2a205f66bdb9 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 24 Jun 2012 11:06:29 -0500 Subject: rtlwifi: Fix IRQ disabled warning The PCI-based drivers can generate the following warning: [ 9497.776350] ------------[ cut here ]------------ [ 9497.776366] WARNING: at kernel/softirq.c:159 local_bh_enable_ip+0x7a/0xa0() [ 9497.776370] Hardware name: 05794NC [ 9497.776597] Pid: 6413, comm: hostapd Not tainted 3.3.0-4.fc16.x86_64 #1 [ 9497.776601] Call Trace: [ 9497.776612] [] warn_slowpath_common+0x7f/0xc0 [ 9497.776633] [] ? rtl_pci_reset_trx_ring+0x199/0x230 [rtlwifi] [ 9497.776640] [] warn_slowpath_null+0x1a/0x20 [ 9497.776646] [] local_bh_enable_ip+0x7a/0xa0 [ 9497.776654] [] _raw_spin_unlock_bh+0x16/0x20 [ 9497.776671] [] destroy_conntrack+0x9e/0x120 [nf_conntrack] [ 9497.776681] [] nf_conntrack_destroy+0x17/0x20 [ 9497.776689] [] skb_release_head_state+0xe5/0x120 [ 9497.776695] [] __kfree_skb+0x16/0xa0 [ 9497.776700] [] kfree_skb+0x45/0xc0 [ 9497.776717] [] rtl_pci_reset_trx_ring+0x199/0x230 [rtlwifi] [ 9497.776734] [] rtl_pci_start+0x25/0x1d0 [rtlwifi] [ 9497.776750] [] rtl_op_start+0x55/0x90 [rtlwifi] [ 9497.776785] [] ieee80211_do_open+0x296/0xa10 [mac80211] [ 9497.776794] [] ? notifier_call_chain+0x4d/0x70 [ 9497.776828] [] ieee80211_open+0x6d/0x80 [mac80211] [ 9497.776836] [] __dev_open+0x8f/0xe0 [ 9497.776842] [] __dev_change_flags+0xa1/0x180 [ 9497.776847] [] dev_change_flags+0x28/0x70 [ 9497.776856] [] devinet_ioctl+0x61d/0x7b0 [ 9497.776863] [] inet_ioctl+0x75/0x90 [ 9497.776870] [] sock_do_ioctl+0x30/0x70 [ 9497.776876] [] sock_ioctl+0x79/0x2f0 [ 9497.776885] [] do_vfs_ioctl+0x98/0x550 [ 9497.776891] [] sys_ioctl+0x91/0xa0 [ 9497.776897] [] system_call_fastpath+0x16/0x1b [ 9497.776902] ---[ end trace 22886c442489082d ]--- The cause is due to calling kfree_skb() with interrupts disabled. This bug is discussed in https://bugzilla.redhat.com/show_bug.cgi?id=797709. Reported-and-Tested by: Ivan Ivanovich Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/pci.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 2062ea1d7c80..36bffbc4519e 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1273,17 +1273,18 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) *after reset, release previous pending packet, *and force the tx idx to the first one */ - spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); for (i = 0; i < RTL_PCI_MAX_TX_QUEUE_COUNT; i++) { if (rtlpci->tx_ring[i].desc) { struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[i]; while (skb_queue_len(&ring->queue)) { - struct rtl_tx_desc *entry = - &ring->desc[ring->idx]; - struct sk_buff *skb = - __skb_dequeue(&ring->queue); + struct rtl_tx_desc *entry; + struct sk_buff *skb; + spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, + flags); + entry = &ring->desc[ring->idx]; + skb = __skb_dequeue(&ring->queue); pci_unmap_single(rtlpci->pdev, rtlpriv->cfg->ops-> get_desc((u8 *) @@ -1291,15 +1292,15 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); ring->idx = (ring->idx + 1) % ring->entries; + spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, + flags); + kfree_skb(skb); } ring->idx = 0; } } - spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); - return 0; } -- cgit v1.2.3 From 46e5129ba6e00ff110df17338bc8fe5ee8f2f8b2 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 24 Jun 2012 14:44:36 -0500 Subject: rtlwifi: rtl8192se: Fix double inclusion of header pci.h The command "make includecheck" yields the following for the rtlwifi tree: /home/finger/linux-2.6/drivers/net/wireless/rtlwifi/rtl8192se/sw.c: ../pci.h is included more than once. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192se/sw.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index 730bcc919529..ad4b4803482d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -29,7 +29,6 @@ #include "../wifi.h" #include "../core.h" -#include "../pci.h" #include "../base.h" #include "../pci.h" #include "reg.h" -- cgit v1.2.3 From 313eb87f1e2f93d83836de7f5c1947b75d7752c8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Mon, 25 Jun 2012 07:15:22 +0200 Subject: ath9k: raise aggregation limit to 64k for HT IBSS mac80211 adds stations in HT IBSS as soon as a frame comes by, even if the HT capabilities are not known yet (they are often received later, e.g. in beacons). So far, ampdu factor/density are only calculated when the station is initially added. This patch changes this to update ampdu factor/density settings when starting a blockack session. Using this patch, we had performance boosts from 60 to 150 MBit/s between two 2x2 Atheros devices in 5 GHz HT IBSS mode. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 1 + drivers/net/wireless/ath/ath9k/main.c | 6 ++++-- drivers/net/wireless/ath/ath9k/xmit.c | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a8c050085648..4473278efc5e 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -721,6 +721,7 @@ extern int ath9k_modparam_nohwcrypt; extern int led_blink; extern bool is_ath9k_unloaded; +u8 ath9k_parse_mpdudensity(u8 mpdudensity); irqreturn_t ath_isr(int irq, void *dev); int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 85f9ab4fa26e..e2e69585d674 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -19,7 +19,7 @@ #include "ath9k.h" #include "btcoex.h" -static u8 parse_mpdudensity(u8 mpdudensity) +u8 ath9k_parse_mpdudensity(u8 mpdudensity) { /* * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": @@ -320,6 +320,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, struct ieee80211_vif *vif) { struct ath_node *an; + u8 density; an = (struct ath_node *)sta->drv_priv; #ifdef CONFIG_ATH9K_DEBUGFS @@ -334,7 +335,8 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, ath_tx_node_init(sc, an); an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); - an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density); + density = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density); + an->mpdudensity = density; } } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index f777ddcd1172..2afc5e289df5 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1175,6 +1175,7 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, { struct ath_atx_tid *txtid; struct ath_node *an; + u8 density; an = (struct ath_node *)sta->drv_priv; txtid = ATH_AN_2_TID(an, tid); @@ -1182,6 +1183,17 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE)) return -EAGAIN; + /* update ampdu factor/density, they may have changed. This may happen + * in HT IBSS when a beacon with HT-info is received after the station + * has already been added. + */ + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + sta->ht_cap.ampdu_factor); + density = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density); + an->mpdudensity = density; + } + txtid->state |= AGGR_ADDBA_PROGRESS; txtid->paused = true; *ssn = txtid->seq_start = txtid->seq_next; -- cgit v1.2.3 From fad29cd2f59949581050a937786c2c9bc78b2f04 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 25 Jun 2012 13:54:22 +0530 Subject: ath9k: Fix lockdep splat Cancel the MCI work only when MCI is actually enabled. Fixes this: [96833.124051] Call Trace: [96833.124060] [] __lock_acquire+0x1518/0x1e40 [96833.124065] [] ? mark_held_locks+0x86/0x110 [96833.124069] [] ? trace_hardirqs_on+0xd/0x10 [96833.124073] [] ? _raw_spin_unlock_irq+0x30/0x70 [96833.124078] [] ? wait_on_cpu_work+0x98/0xc0 [96833.124082] [] lock_acquire+0xa1/0x150 [96833.124085] [] ? wait_on_cpu_work+0xc0/0xc0 [96833.124088] [] ? wait_on_cpu_work+0xc0/0xc0 [96833.124092] [] wait_on_work+0x52/0x120 [96833.124095] [] ? wait_on_cpu_work+0xc0/0xc0 [96833.124099] [] ? del_timer+0x7f/0x110 [96833.124102] [] __cancel_work_timer+0x83/0x130 [96833.124106] [] cancel_work_sync+0x10/0x20 [96833.124113] [] __ath_cancel_work+0x4d/0x60 [ath9k] [96833.124119] [] ath9k_config+0x458/0x680 [ath9k] [96833.124125] [] ? ath9k_flush+0x6e/0x1d0 [ath9k] [96833.124129] [] ? __mutex_unlock_slowpath+0x10d/0x190 [96833.124146] [] ieee80211_hw_config+0x135/0x2a0 [mac80211] [96833.124163] [] ieee80211_do_open+0x67b/0xc50 [mac80211] [96833.124178] [] ieee80211_open+0x6d/0x80 [mac80211] [96833.124183] [] __dev_open+0x9f/0xf0 [96833.124187] [] __dev_change_flags+0xa1/0x180 [96833.124190] [] dev_change_flags+0x28/0x70 [96833.124195] [] devinet_ioctl+0x659/0x780 [96833.124199] [] ? dev_ioctl+0x210/0x6d0 [96833.124203] [] inet_ioctl+0x75/0x90 [96833.124208] [] sock_do_ioctl+0x30/0x70 [96833.124211] [] sock_ioctl+0x7d/0x2c0 [96833.124218] [] do_vfs_ioctl+0x99/0x580 [96833.124222] [] ? sysret_check+0x22/0x5d [96833.124226] [] sys_ioctl+0x99/0xa0 [96833.124230] [] system_call_fastpath+0x16/0x1b Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index e2e69585d674..c14cf5aa403b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -150,9 +150,9 @@ static void __ath_cancel_work(struct ath_softc *sc) cancel_work_sync(&sc->hw_check_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); -#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT - cancel_work_sync(&sc->mci_work); -#endif + + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + cancel_work_sync(&sc->mci_work); } static void ath_cancel_work(struct ath_softc *sc) -- cgit v1.2.3 From fb1c078edb50376c14e049b22b10768b8245428a Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 25 Jun 2012 13:54:30 +0530 Subject: ath9k_htc: Change default listen interval to 1 Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 25213d521bc2..cac6ff4cd14f 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -718,7 +718,7 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, hw->queues = 4; hw->channel_change_time = 5000; - hw->max_listen_interval = 10; + hw->max_listen_interval = 1; hw->vif_data_size = sizeof(struct ath9k_htc_vif); hw->sta_data_size = sizeof(struct ath9k_htc_sta); -- cgit v1.2.3 From d8a2c51cdcaee0131c88f49d64b84f1c7361d72c Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 25 Jun 2012 13:54:41 +0530 Subject: ath9k_htc: Use atomic operations for op_flags Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc.h | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_beacon.c | 10 ++++----- drivers/net/wireless/ath/ath9k/htc_drv_gpio.c | 30 ++++++++++++------------- drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 28 +++++++++++------------ drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +- 6 files changed, 37 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 135795257d95..1ee6548c7125 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -453,7 +453,6 @@ struct ath9k_htc_priv { u8 num_sta_assoc_vif; u8 num_ap_vif; - u16 op_flags; u16 curtxpow; u16 txpowlimit; u16 nvifs; @@ -461,6 +460,7 @@ struct ath9k_htc_priv { bool rearm_ani; bool reconfig_beacon; unsigned int rxfilter; + unsigned long op_flags; struct ath9k_hw_cal_data caldata; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 2eadffb7971c..77d541feb910 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -207,9 +207,9 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, else priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; - if (priv->op_flags & OP_TSF_RESET) { + if (test_bit(OP_TSF_RESET, &priv->op_flags)) { ath9k_hw_reset_tsf(priv->ah); - priv->op_flags &= ~OP_TSF_RESET; + clear_bit(OP_TSF_RESET, &priv->op_flags); } else { /* * Pull nexttbtt forward to reflect the current TSF. @@ -221,7 +221,7 @@ static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, } while (nexttbtt < tsftu); } - if (priv->op_flags & OP_ENABLE_BEACON) + if (test_bit(OP_ENABLE_BEACON, &priv->op_flags)) imask |= ATH9K_INT_SWBA; ath_dbg(common, CONFIG, @@ -269,7 +269,7 @@ static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, else priv->ah->config.sw_beacon_response_time = MIN_SWBA_RESPONSE; - if (priv->op_flags & OP_ENABLE_BEACON) + if (test_bit(OP_ENABLE_BEACON, &priv->op_flags)) imask |= ATH9K_INT_SWBA; ath_dbg(common, CONFIG, @@ -365,7 +365,7 @@ static void ath9k_htc_send_beacon(struct ath9k_htc_priv *priv, vif = priv->cur_beacon_conf.bslot[slot]; avp = (struct ath9k_htc_vif *)vif->drv_priv; - if (unlikely(priv->op_flags & OP_SCANNING)) { + if (unlikely(test_bit(OP_SCANNING, &priv->op_flags))) { spin_unlock_bh(&priv->beacon_lock); return; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 1c10e2e5c237..3688bbad87eb 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -37,17 +37,18 @@ static void ath_detect_bt_priority(struct ath9k_htc_priv *priv) if (time_after(jiffies, btcoex->bt_priority_time + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { - priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); + clear_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); + clear_bit(OP_BT_SCAN, &priv->op_flags); /* Detect if colocated bt started scanning */ if (btcoex->bt_priority_cnt >= ATH_BT_CNT_SCAN_THRESHOLD) { ath_dbg(ath9k_hw_common(ah), BTCOEX, "BT scan detected\n"); - priv->op_flags |= (OP_BT_SCAN | - OP_BT_PRIORITY_DETECTED); + set_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); + set_bit(OP_BT_SCAN, &priv->op_flags); } else if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { ath_dbg(ath9k_hw_common(ah), BTCOEX, "BT priority traffic detected\n"); - priv->op_flags |= OP_BT_PRIORITY_DETECTED; + set_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); } btcoex->bt_priority_cnt = 0; @@ -67,26 +68,23 @@ static void ath_btcoex_period_work(struct work_struct *work) struct ath_btcoex *btcoex = &priv->btcoex; struct ath_common *common = ath9k_hw_common(priv->ah); u32 timer_period; - bool is_btscan; int ret; ath_detect_bt_priority(priv); - is_btscan = !!(priv->op_flags & OP_BT_SCAN); - ret = ath9k_htc_update_cap_target(priv, - !!(priv->op_flags & OP_BT_PRIORITY_DETECTED)); + test_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags)); if (ret) { ath_err(common, "Unable to set BTCOEX parameters\n"); return; } - ath9k_hw_btcoex_bt_stomp(priv->ah, is_btscan ? ATH_BTCOEX_STOMP_ALL : - btcoex->bt_stomp_type); + ath9k_hw_btcoex_bt_stomp(priv->ah, test_bit(OP_BT_SCAN, &priv->op_flags) ? + ATH_BTCOEX_STOMP_ALL : btcoex->bt_stomp_type); ath9k_hw_btcoex_enable(priv->ah); - timer_period = is_btscan ? btcoex->btscan_no_stomp : - btcoex->btcoex_no_stomp; + timer_period = test_bit(OP_BT_SCAN, &priv->op_flags) ? + btcoex->btscan_no_stomp : btcoex->btcoex_no_stomp; ieee80211_queue_delayed_work(priv->hw, &priv->duty_cycle_work, msecs_to_jiffies(timer_period)); ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, @@ -104,14 +102,15 @@ static void ath_btcoex_duty_cycle_work(struct work_struct *work) struct ath_hw *ah = priv->ah; struct ath_btcoex *btcoex = &priv->btcoex; struct ath_common *common = ath9k_hw_common(ah); - bool is_btscan = priv->op_flags & OP_BT_SCAN; ath_dbg(common, BTCOEX, "time slice work for bt and wlan\n"); - if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || is_btscan) + if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW || + test_bit(OP_BT_SCAN, &priv->op_flags)) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); + ath9k_hw_btcoex_enable(priv->ah); } @@ -141,7 +140,8 @@ static void ath_htc_resume_btcoex_work(struct ath9k_htc_priv *priv) btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; - priv->op_flags &= ~(OP_BT_PRIORITY_DETECTED | OP_BT_SCAN); + clear_bit(OP_BT_PRIORITY_DETECTED, &priv->op_flags); + clear_bit(OP_BT_SCAN, &priv->op_flags); ieee80211_queue_delayed_work(priv->hw, &priv->coex_period_work, 0); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index cac6ff4cd14f..a035a380d669 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -611,7 +611,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, struct ath_common *common; int i, ret = 0, csz = 0; - priv->op_flags |= OP_INVALID; + set_bit(OP_INVALID, &priv->op_flags); ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); if (!ah) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 2b8f61c210e1..0fc0d35414c0 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -250,7 +250,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, u8 cmd_rsp; int ret; - if (priv->op_flags & OP_INVALID) + if (test_bit(OP_INVALID, &priv->op_flags)) return -EIO; fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); @@ -304,7 +304,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, htc_start(priv->htc); - if (!(priv->op_flags & OP_SCANNING) && + if (!test_bit(OP_SCANNING, &priv->op_flags) && !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) ath9k_htc_vif_reconfig(priv); @@ -750,7 +750,7 @@ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) common->ani.shortcal_timer = timestamp; common->ani.checkani_timer = timestamp; - priv->op_flags |= OP_ANI_RUNNING; + set_bit(OP_ANI_RUNNING, &priv->op_flags); ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); @@ -759,7 +759,7 @@ void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv) { cancel_delayed_work_sync(&priv->ani_work); - priv->op_flags &= ~OP_ANI_RUNNING; + clear_bit(OP_ANI_RUNNING, &priv->op_flags); } void ath9k_htc_ani_work(struct work_struct *work) @@ -944,7 +944,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ath_dbg(common, CONFIG, "Failed to update capability in target\n"); - priv->op_flags &= ~OP_INVALID; + clear_bit(OP_INVALID, &priv->op_flags); htc_start(priv->htc); spin_lock_bh(&priv->tx.tx_lock); @@ -973,7 +973,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); - if (priv->op_flags & OP_INVALID) { + if (test_bit(OP_INVALID, &priv->op_flags)) { ath_dbg(common, ANY, "Device not present\n"); mutex_unlock(&priv->mutex); return; @@ -1015,7 +1015,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) ath9k_htc_ps_restore(priv); ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); - priv->op_flags |= OP_INVALID; + set_bit(OP_INVALID, &priv->op_flags); ath_dbg(common, CONFIG, "Driver halt\n"); mutex_unlock(&priv->mutex); @@ -1105,7 +1105,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, ath9k_htc_set_opmode(priv); if ((priv->ah->opmode == NL80211_IFTYPE_AP) && - !(priv->op_flags & OP_ANI_RUNNING)) { + !test_bit(OP_ANI_RUNNING, &priv->op_flags)) { ath9k_hw_set_tsfadjust(priv->ah, 1); ath9k_htc_start_ani(priv); } @@ -1285,7 +1285,7 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, changed_flags &= SUPPORTED_FILTERS; *total_flags &= SUPPORTED_FILTERS; - if (priv->op_flags & OP_INVALID) { + if (test_bit(OP_INVALID, &priv->op_flags)) { ath_dbg(ath9k_hw_common(priv->ah), ANY, "Unable to configure filter on invalid state\n"); mutex_unlock(&priv->mutex); @@ -1517,7 +1517,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n", bss_conf->bssid); ath9k_htc_set_tsfadjust(priv, vif); - priv->op_flags |= OP_ENABLE_BEACON; + set_bit(OP_ENABLE_BEACON, &priv->op_flags); ath9k_htc_beacon_config(priv, vif); } @@ -1530,7 +1530,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, ath_dbg(common, CONFIG, "Beacon disabled for BSS: %pM\n", bss_conf->bssid); - priv->op_flags &= ~OP_ENABLE_BEACON; + clear_bit(OP_ENABLE_BEACON, &priv->op_flags); ath9k_htc_beacon_config(priv, vif); } } @@ -1543,7 +1543,7 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, (priv->nvifs == 1) && (priv->num_ap_vif == 1) && (vif->type == NL80211_IFTYPE_AP)) { - priv->op_flags |= OP_TSF_RESET; + set_bit(OP_TSF_RESET, &priv->op_flags); } ath_dbg(common, CONFIG, "Beacon interval changed for BSS: %pM\n", @@ -1655,7 +1655,7 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); spin_lock_bh(&priv->beacon_lock); - priv->op_flags |= OP_SCANNING; + set_bit(OP_SCANNING, &priv->op_flags); spin_unlock_bh(&priv->beacon_lock); cancel_work_sync(&priv->ps_work); ath9k_htc_stop_ani(priv); @@ -1668,7 +1668,7 @@ static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); spin_lock_bh(&priv->beacon_lock); - priv->op_flags &= ~OP_SCANNING; + clear_bit(OP_SCANNING, &priv->op_flags); spin_unlock_bh(&priv->beacon_lock); ath9k_htc_ps_wakeup(priv); ath9k_htc_vif_reconfig(priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 3e40a6461512..47e61d0da33b 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -916,7 +916,7 @@ void ath9k_host_rx_init(struct ath9k_htc_priv *priv) { ath9k_hw_rxena(priv->ah); ath9k_htc_opmode_init(priv); - ath9k_hw_startpcureceive(priv->ah, (priv->op_flags & OP_SCANNING)); + ath9k_hw_startpcureceive(priv->ah, test_bit(OP_SCANNING, &priv->op_flags)); priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER; } -- cgit v1.2.3 From 6bcfe67f9865fb51ec78fc9b09887375db7e08b5 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 25 Jun 2012 13:54:49 +0530 Subject: ath9k_htc: Fix IDLE power save Remove the radio enable/disable stuff and fix the transition to FULL_SLEEP mode when the device is idle. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/htc.h | 2 - drivers/net/wireless/ath/ath9k/htc_drv_gpio.c | 92 --------------------------- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 50 ++++++--------- 3 files changed, 20 insertions(+), 124 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 1ee6548c7125..936e920fb88e 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -572,8 +572,6 @@ bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv); void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw); -void ath9k_htc_radio_enable(struct ieee80211_hw *hw); -void ath9k_htc_radio_disable(struct ieee80211_hw *hw); #ifdef CONFIG_MAC80211_LEDS void ath9k_init_leds(struct ath9k_htc_priv *priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 3688bbad87eb..07df279c8d46 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -310,95 +310,3 @@ void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) wiphy_rfkill_start_polling(priv->hw->wiphy); } - -void ath9k_htc_radio_enable(struct ieee80211_hw *hw) -{ - struct ath9k_htc_priv *priv = hw->priv; - struct ath_hw *ah = priv->ah; - struct ath_common *common = ath9k_hw_common(ah); - int ret; - u8 cmd_rsp; - - if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); - - /* Reset the HW */ - ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); - if (ret) { - ath_err(common, - "Unable to reset hardware; reset status %d (freq %u MHz)\n", - ret, ah->curchan->channel); - } - - ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit, - &priv->curtxpow); - - /* Start RX */ - WMI_CMD(WMI_START_RECV_CMDID); - ath9k_host_rx_init(priv); - - /* Start TX */ - htc_start(priv->htc); - spin_lock_bh(&priv->tx.tx_lock); - priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP; - spin_unlock_bh(&priv->tx.tx_lock); - ieee80211_wake_queues(hw); - - WMI_CMD(WMI_ENABLE_INTR_CMDID); - - /* Enable LED */ - ath9k_hw_cfg_output(ah, ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - ath9k_hw_set_gpio(ah, ah->led_pin, 0); -} - -void ath9k_htc_radio_disable(struct ieee80211_hw *hw) -{ - struct ath9k_htc_priv *priv = hw->priv; - struct ath_hw *ah = priv->ah; - struct ath_common *common = ath9k_hw_common(ah); - int ret; - u8 cmd_rsp; - - ath9k_htc_ps_wakeup(priv); - - /* Disable LED */ - ath9k_hw_set_gpio(ah, ah->led_pin, 1); - ath9k_hw_cfg_gpio_input(ah, ah->led_pin); - - WMI_CMD(WMI_DISABLE_INTR_CMDID); - - /* Stop TX */ - ieee80211_stop_queues(hw); - ath9k_htc_tx_drain(priv); - WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); - - /* Stop RX */ - WMI_CMD(WMI_STOP_RECV_CMDID); - - /* Clear the WMI event queue */ - ath9k_wmi_event_drain(priv); - - /* - * The MIB counters have to be disabled here, - * since the target doesn't do it. - */ - ath9k_hw_disable_mib_counters(ah); - - if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); - - /* Reset the HW */ - ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); - if (ret) { - ath_err(common, - "Unable to reset hardware; reset status %d (freq %u MHz)\n", - ret, ah->curchan->channel); - } - - /* Disable the PHY */ - ath9k_hw_phy_disable(ah); - - ath9k_htc_ps_restore(priv); - ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP); -} diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 0fc0d35414c0..fc8cd8284108 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -75,14 +75,19 @@ unlock: void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv) { + bool reset; + mutex_lock(&priv->htc_pm_lock); if (--priv->ps_usecount != 0) goto unlock; - if (priv->ps_idle) + if (priv->ps_idle) { + ath9k_hw_setrxabort(priv->ah, true); + ath9k_hw_stopdmarecv(priv->ah, &reset); ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP); - else if (priv->ps_enabled) + } else if (priv->ps_enabled) { ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP); + } unlock: mutex_unlock(&priv->htc_pm_lock); @@ -1178,24 +1183,20 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); struct ieee80211_conf *conf = &hw->conf; + bool chip_reset = false; + int ret = 0; mutex_lock(&priv->mutex); + ath9k_htc_ps_wakeup(priv); if (changed & IEEE80211_CONF_CHANGE_IDLE) { - bool enable_radio = false; - bool idle = !!(conf->flags & IEEE80211_CONF_IDLE); - mutex_lock(&priv->htc_pm_lock); - if (!idle && priv->ps_idle) - enable_radio = true; - priv->ps_idle = idle; - mutex_unlock(&priv->htc_pm_lock); - if (enable_radio) { - ath_dbg(common, CONFIG, "not-idle: enabling radio\n"); - ath9k_htc_setpower(priv, ATH9K_PM_AWAKE); - ath9k_htc_radio_enable(hw); - } + priv->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE); + if (priv->ps_idle) + chip_reset = true; + + mutex_unlock(&priv->htc_pm_lock); } /* @@ -1210,7 +1211,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) ath9k_htc_remove_monitor_interface(priv); } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; @@ -1223,8 +1224,8 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); - mutex_unlock(&priv->mutex); - return -EINVAL; + ret = -EINVAL; + goto out; } } @@ -1246,21 +1247,10 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) priv->txpowlimit, &priv->curtxpow); } - if (changed & IEEE80211_CONF_CHANGE_IDLE) { - mutex_lock(&priv->htc_pm_lock); - if (!priv->ps_idle) { - mutex_unlock(&priv->htc_pm_lock); - goto out; - } - mutex_unlock(&priv->htc_pm_lock); - - ath_dbg(common, CONFIG, "idle: disabling radio\n"); - ath9k_htc_radio_disable(hw); - } - out: + ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); - return 0; + return ret; } #define SUPPORTED_FILTERS \ -- cgit v1.2.3 From 4069d6f86bebce1a1e3456ef721511b4b81958f8 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 9 Jun 2012 13:18:08 +0900 Subject: sony-laptop: use an enum for SNC event types Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 210d4ae547c2..615ab06b6cd3 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1172,6 +1172,10 @@ static int sony_nc_hotkeys_decode(u32 event, unsigned int handle) /* * ACPI callbacks */ +enum event_types { + HOTKEY = 1, + KILLSWITCH +}; static void sony_nc_notify(struct acpi_device *device, u32 event) { u32 real_ev = event; @@ -1196,7 +1200,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) /* hotkey event */ case 0x0100: case 0x0127: - ev_type = 1; + ev_type = HOTKEY; real_ev = sony_nc_hotkeys_decode(event, handle); if (real_ev > 0) @@ -1216,7 +1220,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) * update the rfkill device status when the * switch is moved. */ - ev_type = 2; + ev_type = KILLSWITCH; sony_call_snc_handle(handle, 0x0100, &result); real_ev = result & 0x03; @@ -1238,7 +1242,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) } else { /* old style event */ - ev_type = 1; + ev_type = HOTKEY; sony_laptop_report_input_event(real_ev); } -- cgit v1.2.3 From bb384b5295323ed58260aeaff22d8bbe32988396 Mon Sep 17 00:00:00 2001 From: Marco Chiappero Date: Sat, 9 Jun 2012 13:18:09 +0900 Subject: sony-laptop: notify userspace of GFX switch position changes Some Vaios come with both integrated and discrete graphics, plus a switch for choosing one of the two. When the switch position is changed, a notification is generated. Signed-off-by: Marco Chiappero Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 615ab06b6cd3..4f42e5661bf5 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1174,7 +1174,8 @@ static int sony_nc_hotkeys_decode(u32 event, unsigned int handle) */ enum event_types { HOTKEY = 1, - KILLSWITCH + KILLSWITCH, + GFX_SWITCH }; static void sony_nc_notify(struct acpi_device *device, u32 event) { @@ -1230,6 +1231,24 @@ static void sony_nc_notify(struct acpi_device *device, u32 event) break; + case 0x0128: + case 0x0146: + /* Hybrid GFX switching */ + sony_call_snc_handle(handle, 0x0000, &result); + dprintk("GFX switch event received (reason: %s)\n", + (result & 0x01) ? + "switch change" : "unknown"); + + /* verify the switch state + * 1: discrete GFX + * 0: integrated GFX + */ + sony_call_snc_handle(handle, 0x0100, &result); + + ev_type = GFX_SWITCH; + real_ev = result & 0xff; + break; + default: dprintk("Unknown event 0x%x for handle 0x%x\n", event, handle); -- cgit v1.2.3 From 15aa5c75468a103cdee1a0e0ec26aad979bf71a5 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Mon, 11 Jun 2012 07:18:31 +0900 Subject: sony-laptop: store battery care limits on batteries Some models offer the option to store the limits on the battery (firmware?). Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 4f42e5661bf5..f045e3e59cd6 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1916,32 +1916,33 @@ static ssize_t sony_nc_battery_care_limit_store(struct device *dev, * bits 4,5: store the limit into the EC * bits 6,7: store the limit into the battery */ + cmd = 0; - /* - * handle 0x0115 should allow storing on battery too; - * handle 0x0136 same as 0x0115 + health status; - * handle 0x013f, same as 0x0136 but no storing on the battery - * - * Store only inside the EC for now, regardless the handle number - */ - if (value == 0) - /* disable limits */ - cmd = 0x0; + if (value > 0) { + if (value <= 50) + cmd = 0x20; - else if (value <= 50) - cmd = 0x21; + else if (value <= 80) + cmd = 0x10; - else if (value <= 80) - cmd = 0x11; + else if (value <= 100) + cmd = 0x30; + + else + return -EINVAL; - else if (value <= 100) - cmd = 0x31; + /* + * handle 0x0115 should allow storing on battery too; + * handle 0x0136 same as 0x0115 + health status; + * handle 0x013f, same as 0x0136 but no storing on the battery + */ + if (bcare_ctl->handle != 0x013f) + cmd = cmd | (cmd << 2); - else - return -EINVAL; + cmd = (cmd | 0x1) << 0x10; + } - if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100, - &result)) + if (sony_call_snc_handle(bcare_ctl->handle, cmd | 0x0100, &result)) return -EIO; return count; -- cgit v1.2.3 From 014fc8fbece33d42e2aa92d289fffa213a159321 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 9 Jun 2012 13:18:11 +0900 Subject: sony-laptop: add lid backlight support for handle 0x143 Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index f045e3e59cd6..a94e13fe1f42 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1010,6 +1010,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, struct sony_backlight_props { struct backlight_device *dev; int handle; + int cmd_base; u8 offset; u8 maxlvl; }; @@ -1037,7 +1038,7 @@ static int sony_nc_get_brightness_ng(struct backlight_device *bd) struct sony_backlight_props *sdev = (struct sony_backlight_props *)bl_get_data(bd); - sony_call_snc_handle(sdev->handle, 0x0200, &result); + sony_call_snc_handle(sdev->handle, sdev->cmd_base + 0x100, &result); return (result & 0xff) - sdev->offset; } @@ -1049,7 +1050,8 @@ static int sony_nc_update_status_ng(struct backlight_device *bd) (struct sony_backlight_props *)bl_get_data(bd); value = bd->props.brightness + sdev->offset; - if (sony_call_snc_handle(sdev->handle, 0x0100 | (value << 16), &result)) + if (sony_call_snc_handle(sdev->handle, sdev->cmd_base | (value << 0x10), + &result)) return -EIO; return value; @@ -2496,6 +2498,7 @@ static void sony_nc_backlight_ng_read_limits(int handle, { u64 offset; int i; + int lvl_table_len = 0; u8 min = 0xff, max = 0x00; unsigned char buffer[32] = { 0 }; @@ -2515,11 +2518,21 @@ static void sony_nc_backlight_ng_read_limits(int handle, if (i < 0) return; + switch (handle) { + case 0x012f: + case 0x0137: + lvl_table_len = 9; + break; + case 0x143: + lvl_table_len = 16; + break; + } + /* the buffer lists brightness levels available, brightness levels are * from position 0 to 8 in the array, other values are used by ALS * control. */ - for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) { + for (i = 0; i < lvl_table_len && i < ARRAY_SIZE(buffer); i++) { dprintk("Brightness level: %d\n", buffer[i]); @@ -2546,14 +2559,22 @@ static void sony_nc_backlight_setup(void) if (sony_find_snc_handle(0x12f) != -1) { ops = &sony_backlight_ng_ops; + sony_bl_props.cmd_base = 0x0100; sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props); max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; } else if (sony_find_snc_handle(0x137) != -1) { ops = &sony_backlight_ng_ops; + sony_bl_props.cmd_base = 0x0100; sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props); max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; + } else if (sony_find_snc_handle(0x143) != -1) { + ops = &sony_backlight_ng_ops; + sony_bl_props.cmd_base = 0x3000; + sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props); + max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; + } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &unused))) { ops = &sony_backlight_ops; -- cgit v1.2.3 From ca3c2c706de39b3400e57254dce054bf7350efa2 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Sat, 9 Jun 2012 13:18:12 +0900 Subject: sony-laptop: input initialization should be done before SNC SNC needs input devices so better have those ready before starting handle events. Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index a94e13fe1f42..89ff6d845f34 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2642,6 +2642,12 @@ static int sony_nc_add(struct acpi_device *device) } } + result = sony_laptop_setup_input(device); + if (result) { + pr_err("Unable to create input devices\n"); + goto outplatform; + } + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", &handle))) { int arg = 1; @@ -2659,12 +2665,6 @@ static int sony_nc_add(struct acpi_device *device) } /* setup input devices and helper fifo */ - result = sony_laptop_setup_input(device); - if (result) { - pr_err("Unable to create input devices\n"); - goto outsnc; - } - if (acpi_video_backlight_support()) { pr_info("brightness ignored, must be controlled by ACPI video driver\n"); } else { @@ -2712,22 +2712,21 @@ static int sony_nc_add(struct acpi_device *device) return 0; - out_sysfs: +out_sysfs: for (item = sony_nc_values; item->name; ++item) { device_remove_file(&sony_pf_device->dev, &item->devattr); } sony_nc_backlight_cleanup(); - - sony_laptop_remove_input(); - - outsnc: sony_nc_function_cleanup(sony_pf_device); sony_nc_handles_cleanup(sony_pf_device); - outpresent: +outplatform: + sony_laptop_remove_input(); + +outpresent: sony_pf_remove(); - outwalk: +outwalk: sony_nc_rfkill_cleanup(); return result; } -- cgit v1.2.3 From c7a2918373983b32db3ca35823d930641747e26f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 9 Jun 2012 13:18:13 +0900 Subject: sony-laptop: fix sony_nc_sysfs_store() We made this an unsigned long and it causes a bug on 64 bit big endian systems when we try to pass the value to sony_nc_int_call(). Also value has to be signed because validate() returns negative error codes. Signed-off-by: Dan Carpenter Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 89ff6d845f34..78e6389d2cae 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -973,7 +973,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { - unsigned long value = 0; + int value; int ret = 0; struct sony_nc_value *item = container_of(attr, struct sony_nc_value, devattr); @@ -984,7 +984,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, if (count > 31) return -EINVAL; - if (kstrtoul(buffer, 10, &value)) + if (kstrtoint(buffer, 10, &value)) return -EINVAL; if (item->validate) @@ -994,7 +994,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev, return value; ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset, - (int *)&value, NULL); + &value, NULL); if (ret < 0) return -EIO; -- cgit v1.2.3 From 56f4a9f76d8ce7c7cef92905c909aad0c7e5c9db Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 9 Jun 2012 13:18:14 +0900 Subject: sony-laptop: fix a couple signedness bugs This needs to be signed to handle negative error codes. Remove a redundant check, read_limits is always called with a valid handle. Signed-off-by: Dan Carpenter Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 78e6389d2cae..b9499b65c1cd 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2139,7 +2139,7 @@ static ssize_t sony_nc_thermal_mode_show(struct device *dev, struct device_attribute *attr, char *buffer) { ssize_t count = 0; - unsigned int mode = sony_nc_thermal_mode_get(); + int mode = sony_nc_thermal_mode_get(); if (mode < 0) return mode; @@ -2507,8 +2507,6 @@ static void sony_nc_backlight_ng_read_limits(int handle, props->maxlvl = 0xff; offset = sony_find_snc_handle(handle); - if (offset < 0) - return; /* try to read the boundaries from ACPI tables, if we fail the above * defaults should be reasonable -- cgit v1.2.3 From a1071a5abf1f4d4a7f8324e21b8b32b1342013c7 Mon Sep 17 00:00:00 2001 From: Mattia Dongili Date: Thu, 14 Jun 2012 06:36:02 +0900 Subject: sony-laptop: correct find_snc_handle failure checks Since bab7084c745bf4d75b760728387f375fd34dc683, find_snc_handle returns -EINVAL, not -1. Signed-off-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index b9499b65c1cd..d456ff0c73b7 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -2555,19 +2555,19 @@ static void sony_nc_backlight_setup(void) const struct backlight_ops *ops = NULL; struct backlight_properties props; - if (sony_find_snc_handle(0x12f) != -1) { + if (sony_find_snc_handle(0x12f) >= 0) { ops = &sony_backlight_ng_ops; sony_bl_props.cmd_base = 0x0100; sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props); max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; - } else if (sony_find_snc_handle(0x137) != -1) { + } else if (sony_find_snc_handle(0x137) >= 0) { ops = &sony_backlight_ng_ops; sony_bl_props.cmd_base = 0x0100; sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props); max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; - } else if (sony_find_snc_handle(0x143) != -1) { + } else if (sony_find_snc_handle(0x143) >= 0) { ops = &sony_backlight_ng_ops; sony_bl_props.cmd_base = 0x3000; sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props); -- cgit v1.2.3 From 57f9616b79549e772cf4dd3aa1d2df5b6c8acdfa Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 Jun 2012 19:28:50 +0300 Subject: ideapad: uninitialized data in ideapad_acpi_add() We only initialize the high bits of "cfg". It probably doesn't cause a problem given that this is platform specific code and doesn't have to worry about endianness etc. But it's sort of messy. Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/ideapad-laptop.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 4f20f8dd3d7c..17f6dfd8dbfb 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -694,10 +694,10 @@ MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); static int __devinit ideapad_acpi_add(struct acpi_device *adevice) { int ret, i; - unsigned long cfg; + int cfg; struct ideapad_private *priv; - if (read_method_int(adevice->handle, "_CFG", (int *)&cfg)) + if (read_method_int(adevice->handle, "_CFG", &cfg)) return -ENODEV; priv = kzalloc(sizeof(*priv), GFP_KERNEL); @@ -721,7 +721,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) goto input_failed; for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { - if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg)) + if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) ideapad_register_rfkill(adevice, i); else priv->rfk[i] = NULL; -- cgit v1.2.3 From 88ca518b0bb4161e5f20f8a1d9cc477cae294e54 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 25 Jun 2012 15:07:17 +0200 Subject: intel_ips: blacklist HP ProBook laptops intel_ips driver spews the warning message "ME failed to update for more than 1s, likely hung" at each second endlessly on HP ProBook laptops with IronLake. As this has never worked, better to blacklist the driver for now. Cc: Signed-off-by: Takashi Iwai Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_ips.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 0ffdb3cde2bb..9af4257d4901 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -1485,6 +1486,24 @@ static DEFINE_PCI_DEVICE_TABLE(ips_id_table) = { MODULE_DEVICE_TABLE(pci, ips_id_table); +static int ips_blacklist_callback(const struct dmi_system_id *id) +{ + pr_info("Blacklisted intel_ips for %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id ips_blacklist[] = { + { + .callback = ips_blacklist_callback, + .ident = "HP ProBook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook"), + }, + }, + { } /* terminating entry */ +}; + static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) { u64 platform_info; @@ -1494,6 +1513,9 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id) u16 htshi, trc, trc_required_mask; u8 tse; + if (dmi_check_system(ips_blacklist)) + return -ENODEV; + ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL); if (!ips) return -ENOMEM; -- cgit v1.2.3 From 5ac24979dcb3418a295e11823c1f2506df1d9926 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 Jun 2012 22:39:45 +0000 Subject: net: qmi_wwan: simplify a check in qmi_wwan_bind() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This code is easier to read if we specify which flags we want at the condition instead of at the top of the function. Signed-off-by: Dan Carpenter Acked-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 68ca6769210f..484eaa5c8a5d 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -129,7 +129,6 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; struct usb_cdc_union_desc *cdc_union = NULL; struct usb_cdc_ether_desc *cdc_ether = NULL; - u32 required = 1 << USB_CDC_HEADER_TYPE | 1 << USB_CDC_UNION_TYPE; u32 found = 0; struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; @@ -197,7 +196,8 @@ next_desc: } /* did we find all the required ones? */ - if ((found & required) != required) { + if (!(found & (1 << USB_CDC_HEADER_TYPE)) || + !(found & (1 << USB_CDC_UNION_TYPE))) { dev_err(&intf->dev, "CDC functional descriptors missing\n"); goto err; } -- cgit v1.2.3 From 9e303f228c24d529bf479196d290eedc2a04cd5e Mon Sep 17 00:00:00 2001 From: Grazvydas Ignotas Date: Sat, 16 Jun 2012 22:01:25 +0300 Subject: gpio/omap: fix irq loss while in idle with debounce on It seems that currently GPIO module is not working correctly during idle when debounce is enabled - the system almost never responds to button presses (observed on OMAP3530 ES2.1 and OMAP3630 ES1.2 pandora boards). Even though wakeups are probably working, it seems that the GPIO module itself is unable to detect input events and generate interrupts. OMAP35x TRM also states that: "If the debounce clock is inactive, the debounce cell gates all input signals and thus cannot be used." So whenever we are disabling debounce clocks (for PM or other reasons), be sure the module's debounce feature is disabled too. Cc: Kevin Hilman Signed-off-by: Grazvydas Ignotas Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index c4ed1722734c..ff213e70fa5a 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -174,12 +174,22 @@ static inline void _gpio_dbck_enable(struct gpio_bank *bank) if (bank->dbck_enable_mask && !bank->dbck_enabled) { clk_enable(bank->dbck); bank->dbck_enabled = true; + + __raw_writel(bank->dbck_enable_mask, + bank->base + bank->regs->debounce_en); } } static inline void _gpio_dbck_disable(struct gpio_bank *bank) { if (bank->dbck_enable_mask && bank->dbck_enabled) { + /* + * Disable debounce before cutting it's clock. If debounce is + * enabled but the clock is not, GPIO module seems to be unable + * to detect events and generate interrupts at least on OMAP3. + */ + __raw_writel(0, bank->base + bank->regs->debounce_en); + clk_disable(bank->dbck); bank->dbck_enabled = false; } -- cgit v1.2.3 From 7b8e19b67c1b171a04f6bd2f973d0b38cb496bf6 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Mon, 25 Jun 2012 23:24:53 +0000 Subject: drivers/ieee802154: add support for the at86rf230/231 transceivers The AT86RF231 is a feature rich, low-power 2.4 GHz radio transceiver designed for industrial and consumer ZigBee/IEEE 802.15.4, 6LoWPAN, RF4CE and high data rate 2.4 GHz ISM band applications. This patch adds support for the Atmel RF230/231 radio transceivers. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- drivers/ieee802154/Kconfig | 6 + drivers/ieee802154/Makefile | 1 + drivers/ieee802154/at86rf230.c | 965 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 972 insertions(+) create mode 100644 drivers/ieee802154/at86rf230.c (limited to 'drivers') diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig index 15c064073701..1fc4eefc20ed 100644 --- a/drivers/ieee802154/Kconfig +++ b/drivers/ieee802154/Kconfig @@ -19,6 +19,7 @@ config IEEE802154_FAKEHARD This driver can also be built as a module. To do so say M here. The module will be called 'fakehard'. + config IEEE802154_FAKELB depends on IEEE802154_DRIVERS && MAC802154 tristate "IEEE 802.15.4 loopback driver" @@ -28,3 +29,8 @@ config IEEE802154_FAKELB This driver can also be built as a module. To do so say M here. The module will be called 'fakelb'. + +config IEEE802154_AT86RF230 + depends on IEEE802154_DRIVERS && MAC802154 + tristate "AT86RF230/231 transceiver driver" + depends on SPI diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile index ea784ea6f0f8..4f4371d3aa7d 100644 --- a/drivers/ieee802154/Makefile +++ b/drivers/ieee802154/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o +obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o diff --git a/drivers/ieee802154/at86rf230.c b/drivers/ieee802154/at86rf230.c new file mode 100644 index 000000000000..4d033d4c4ddc --- /dev/null +++ b/drivers/ieee802154/at86rf230.c @@ -0,0 +1,965 @@ +/* + * AT86RF230/RF231 driver + * + * Copyright (C) 2009-2012 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Written by: + * Dmitry Eremin-Solenikov + * Alexander Smirnov + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct at86rf230_local { + struct spi_device *spi; + int rstn, slp_tr, dig2; + + u8 part; + u8 vers; + + u8 buf[2]; + struct mutex bmux; + + struct work_struct irqwork; + struct completion tx_complete; + + struct ieee802154_dev *dev; + + spinlock_t lock; + bool irq_disabled; + bool is_tx; +}; + +#define RG_TRX_STATUS (0x01) +#define SR_TRX_STATUS 0x01, 0x1f, 0 +#define SR_RESERVED_01_3 0x01, 0x20, 5 +#define SR_CCA_STATUS 0x01, 0x40, 6 +#define SR_CCA_DONE 0x01, 0x80, 7 +#define RG_TRX_STATE (0x02) +#define SR_TRX_CMD 0x02, 0x1f, 0 +#define SR_TRAC_STATUS 0x02, 0xe0, 5 +#define RG_TRX_CTRL_0 (0x03) +#define SR_CLKM_CTRL 0x03, 0x07, 0 +#define SR_CLKM_SHA_SEL 0x03, 0x08, 3 +#define SR_PAD_IO_CLKM 0x03, 0x30, 4 +#define SR_PAD_IO 0x03, 0xc0, 6 +#define RG_TRX_CTRL_1 (0x04) +#define SR_IRQ_POLARITY 0x04, 0x01, 0 +#define SR_IRQ_MASK_MODE 0x04, 0x02, 1 +#define SR_SPI_CMD_MODE 0x04, 0x0c, 2 +#define SR_RX_BL_CTRL 0x04, 0x10, 4 +#define SR_TX_AUTO_CRC_ON 0x04, 0x20, 5 +#define SR_IRQ_2_EXT_EN 0x04, 0x40, 6 +#define SR_PA_EXT_EN 0x04, 0x80, 7 +#define RG_PHY_TX_PWR (0x05) +#define SR_TX_PWR 0x05, 0x0f, 0 +#define SR_PA_LT 0x05, 0x30, 4 +#define SR_PA_BUF_LT 0x05, 0xc0, 6 +#define RG_PHY_RSSI (0x06) +#define SR_RSSI 0x06, 0x1f, 0 +#define SR_RND_VALUE 0x06, 0x60, 5 +#define SR_RX_CRC_VALID 0x06, 0x80, 7 +#define RG_PHY_ED_LEVEL (0x07) +#define SR_ED_LEVEL 0x07, 0xff, 0 +#define RG_PHY_CC_CCA (0x08) +#define SR_CHANNEL 0x08, 0x1f, 0 +#define SR_CCA_MODE 0x08, 0x60, 5 +#define SR_CCA_REQUEST 0x08, 0x80, 7 +#define RG_CCA_THRES (0x09) +#define SR_CCA_ED_THRES 0x09, 0x0f, 0 +#define SR_RESERVED_09_1 0x09, 0xf0, 4 +#define RG_RX_CTRL (0x0a) +#define SR_PDT_THRES 0x0a, 0x0f, 0 +#define SR_RESERVED_0a_1 0x0a, 0xf0, 4 +#define RG_SFD_VALUE (0x0b) +#define SR_SFD_VALUE 0x0b, 0xff, 0 +#define RG_TRX_CTRL_2 (0x0c) +#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0 +#define SR_RESERVED_0c_2 0x0c, 0x7c, 2 +#define SR_RX_SAFE_MODE 0x0c, 0x80, 7 +#define RG_ANT_DIV (0x0d) +#define SR_ANT_CTRL 0x0d, 0x03, 0 +#define SR_ANT_EXT_SW_EN 0x0d, 0x04, 2 +#define SR_ANT_DIV_EN 0x0d, 0x08, 3 +#define SR_RESERVED_0d_2 0x0d, 0x70, 4 +#define SR_ANT_SEL 0x0d, 0x80, 7 +#define RG_IRQ_MASK (0x0e) +#define SR_IRQ_MASK 0x0e, 0xff, 0 +#define RG_IRQ_STATUS (0x0f) +#define SR_IRQ_0_PLL_LOCK 0x0f, 0x01, 0 +#define SR_IRQ_1_PLL_UNLOCK 0x0f, 0x02, 1 +#define SR_IRQ_2_RX_START 0x0f, 0x04, 2 +#define SR_IRQ_3_TRX_END 0x0f, 0x08, 3 +#define SR_IRQ_4_CCA_ED_DONE 0x0f, 0x10, 4 +#define SR_IRQ_5_AMI 0x0f, 0x20, 5 +#define SR_IRQ_6_TRX_UR 0x0f, 0x40, 6 +#define SR_IRQ_7_BAT_LOW 0x0f, 0x80, 7 +#define RG_VREG_CTRL (0x10) +#define SR_RESERVED_10_6 0x10, 0x03, 0 +#define SR_DVDD_OK 0x10, 0x04, 2 +#define SR_DVREG_EXT 0x10, 0x08, 3 +#define SR_RESERVED_10_3 0x10, 0x30, 4 +#define SR_AVDD_OK 0x10, 0x40, 6 +#define SR_AVREG_EXT 0x10, 0x80, 7 +#define RG_BATMON (0x11) +#define SR_BATMON_VTH 0x11, 0x0f, 0 +#define SR_BATMON_HR 0x11, 0x10, 4 +#define SR_BATMON_OK 0x11, 0x20, 5 +#define SR_RESERVED_11_1 0x11, 0xc0, 6 +#define RG_XOSC_CTRL (0x12) +#define SR_XTAL_TRIM 0x12, 0x0f, 0 +#define SR_XTAL_MODE 0x12, 0xf0, 4 +#define RG_RX_SYN (0x15) +#define SR_RX_PDT_LEVEL 0x15, 0x0f, 0 +#define SR_RESERVED_15_2 0x15, 0x70, 4 +#define SR_RX_PDT_DIS 0x15, 0x80, 7 +#define RG_XAH_CTRL_1 (0x17) +#define SR_RESERVED_17_8 0x17, 0x01, 0 +#define SR_AACK_PROM_MODE 0x17, 0x02, 1 +#define SR_AACK_ACK_TIME 0x17, 0x04, 2 +#define SR_RESERVED_17_5 0x17, 0x08, 3 +#define SR_AACK_UPLD_RES_FT 0x17, 0x10, 4 +#define SR_AACK_FLTR_RES_FT 0x17, 0x20, 5 +#define SR_RESERVED_17_2 0x17, 0x40, 6 +#define SR_RESERVED_17_1 0x17, 0x80, 7 +#define RG_FTN_CTRL (0x18) +#define SR_RESERVED_18_2 0x18, 0x7f, 0 +#define SR_FTN_START 0x18, 0x80, 7 +#define RG_PLL_CF (0x1a) +#define SR_RESERVED_1a_2 0x1a, 0x7f, 0 +#define SR_PLL_CF_START 0x1a, 0x80, 7 +#define RG_PLL_DCU (0x1b) +#define SR_RESERVED_1b_3 0x1b, 0x3f, 0 +#define SR_RESERVED_1b_2 0x1b, 0x40, 6 +#define SR_PLL_DCU_START 0x1b, 0x80, 7 +#define RG_PART_NUM (0x1c) +#define SR_PART_NUM 0x1c, 0xff, 0 +#define RG_VERSION_NUM (0x1d) +#define SR_VERSION_NUM 0x1d, 0xff, 0 +#define RG_MAN_ID_0 (0x1e) +#define SR_MAN_ID_0 0x1e, 0xff, 0 +#define RG_MAN_ID_1 (0x1f) +#define SR_MAN_ID_1 0x1f, 0xff, 0 +#define RG_SHORT_ADDR_0 (0x20) +#define SR_SHORT_ADDR_0 0x20, 0xff, 0 +#define RG_SHORT_ADDR_1 (0x21) +#define SR_SHORT_ADDR_1 0x21, 0xff, 0 +#define RG_PAN_ID_0 (0x22) +#define SR_PAN_ID_0 0x22, 0xff, 0 +#define RG_PAN_ID_1 (0x23) +#define SR_PAN_ID_1 0x23, 0xff, 0 +#define RG_IEEE_ADDR_0 (0x24) +#define SR_IEEE_ADDR_0 0x24, 0xff, 0 +#define RG_IEEE_ADDR_1 (0x25) +#define SR_IEEE_ADDR_1 0x25, 0xff, 0 +#define RG_IEEE_ADDR_2 (0x26) +#define SR_IEEE_ADDR_2 0x26, 0xff, 0 +#define RG_IEEE_ADDR_3 (0x27) +#define SR_IEEE_ADDR_3 0x27, 0xff, 0 +#define RG_IEEE_ADDR_4 (0x28) +#define SR_IEEE_ADDR_4 0x28, 0xff, 0 +#define RG_IEEE_ADDR_5 (0x29) +#define SR_IEEE_ADDR_5 0x29, 0xff, 0 +#define RG_IEEE_ADDR_6 (0x2a) +#define SR_IEEE_ADDR_6 0x2a, 0xff, 0 +#define RG_IEEE_ADDR_7 (0x2b) +#define SR_IEEE_ADDR_7 0x2b, 0xff, 0 +#define RG_XAH_CTRL_0 (0x2c) +#define SR_SLOTTED_OPERATION 0x2c, 0x01, 0 +#define SR_MAX_CSMA_RETRIES 0x2c, 0x0e, 1 +#define SR_MAX_FRAME_RETRIES 0x2c, 0xf0, 4 +#define RG_CSMA_SEED_0 (0x2d) +#define SR_CSMA_SEED_0 0x2d, 0xff, 0 +#define RG_CSMA_SEED_1 (0x2e) +#define SR_CSMA_SEED_1 0x2e, 0x07, 0 +#define SR_AACK_I_AM_COORD 0x2e, 0x08, 3 +#define SR_AACK_DIS_ACK 0x2e, 0x10, 4 +#define SR_AACK_SET_PD 0x2e, 0x20, 5 +#define SR_AACK_FVN_MODE 0x2e, 0xc0, 6 +#define RG_CSMA_BE (0x2f) +#define SR_MIN_BE 0x2f, 0x0f, 0 +#define SR_MAX_BE 0x2f, 0xf0, 4 + +#define CMD_REG 0x80 +#define CMD_REG_MASK 0x3f +#define CMD_WRITE 0x40 +#define CMD_FB 0x20 + +#define IRQ_BAT_LOW (1 << 7) +#define IRQ_TRX_UR (1 << 6) +#define IRQ_AMI (1 << 5) +#define IRQ_CCA_ED (1 << 4) +#define IRQ_TRX_END (1 << 3) +#define IRQ_RX_START (1 << 2) +#define IRQ_PLL_UNL (1 << 1) +#define IRQ_PLL_LOCK (1 << 0) + +#define STATE_P_ON 0x00 /* BUSY */ +#define STATE_BUSY_RX 0x01 +#define STATE_BUSY_TX 0x02 +#define STATE_FORCE_TRX_OFF 0x03 +#define STATE_FORCE_TX_ON 0x04 /* IDLE */ +/* 0x05 */ /* INVALID_PARAMETER */ +#define STATE_RX_ON 0x06 +/* 0x07 */ /* SUCCESS */ +#define STATE_TRX_OFF 0x08 +#define STATE_TX_ON 0x09 +/* 0x0a - 0x0e */ /* 0x0a - UNSUPPORTED_ATTRIBUTE */ +#define STATE_SLEEP 0x0F +#define STATE_BUSY_RX_AACK 0x11 +#define STATE_BUSY_TX_ARET 0x12 +#define STATE_BUSY_RX_AACK_ON 0x16 +#define STATE_BUSY_TX_ARET_ON 0x19 +#define STATE_RX_ON_NOCLK 0x1C +#define STATE_RX_AACK_ON_NOCLK 0x1D +#define STATE_BUSY_RX_AACK_NOCLK 0x1E +#define STATE_TRANSITION_IN_PROGRESS 0x1F + +static int +__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + }; + + buf[0] = (addr & CMD_REG_MASK) | CMD_REG | CMD_WRITE; + buf[1] = data; + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + return status; +} + +static int +__at86rf230_read_subreg(struct at86rf230_local *lp, + u8 addr, u8 mask, int shift, u8 *data) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer = { + .len = 2, + .tx_buf = buf, + .rx_buf = buf, + }; + + buf[0] = (addr & CMD_REG_MASK) | CMD_REG; + buf[1] = 0xff; + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + if (status == 0) + *data = buf[1]; + + return status; +} + +static int +at86rf230_read_subreg(struct at86rf230_local *lp, + u8 addr, u8 mask, int shift, u8 *data) +{ + int status; + + mutex_lock(&lp->bmux); + status = __at86rf230_read_subreg(lp, addr, mask, shift, data); + mutex_unlock(&lp->bmux); + + return status; +} + +static int +at86rf230_write_subreg(struct at86rf230_local *lp, + u8 addr, u8 mask, int shift, u8 data) +{ + int status; + u8 val; + + mutex_lock(&lp->bmux); + status = __at86rf230_read_subreg(lp, addr, 0xff, 0, &val); + if (status) + goto out; + + val &= ~mask; + val |= (data << shift) & mask; + + status = __at86rf230_write(lp, addr, val); +out: + mutex_unlock(&lp->bmux); + + return status; +} + +static int +at86rf230_write_fbuf(struct at86rf230_local *lp, u8 *data, u8 len) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 2, + .tx_buf = buf, + + }; + struct spi_transfer xfer_buf = { + .len = len, + .tx_buf = data, + }; + + mutex_lock(&lp->bmux); + buf[0] = CMD_WRITE | CMD_FB; + buf[1] = len + 2; /* 2 bytes for CRC that isn't written */ + + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + mutex_unlock(&lp->bmux); + return status; +} + +static int +at86rf230_read_fbuf(struct at86rf230_local *lp, u8 *data, u8 *len, u8 *lqi) +{ + u8 *buf = lp->buf; + int status; + struct spi_message msg; + struct spi_transfer xfer_head = { + .len = 2, + .tx_buf = buf, + .rx_buf = buf, + }; + struct spi_transfer xfer_head1 = { + .len = 2, + .tx_buf = buf, + .rx_buf = buf, + }; + struct spi_transfer xfer_buf = { + .len = 0, + .rx_buf = data, + }; + + mutex_lock(&lp->bmux); + + buf[0] = CMD_FB; + buf[1] = 0x00; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head, &msg); + + status = spi_sync(lp->spi, &msg); + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + + xfer_buf.len = *(buf + 1) + 1; + *len = buf[1]; + + buf[0] = CMD_FB; + buf[1] = 0x00; + + spi_message_init(&msg); + spi_message_add_tail(&xfer_head1, &msg); + spi_message_add_tail(&xfer_buf, &msg); + + status = spi_sync(lp->spi, &msg); + + if (msg.status) + status = msg.status; + + dev_vdbg(&lp->spi->dev, "status = %d\n", status); + dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]); + dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]); + + if (status) { + if (lqi && (*len > lp->buf[1])) + *lqi = data[lp->buf[1]]; + } + mutex_unlock(&lp->bmux); + + return status; +} + +static int +at86rf230_ed(struct ieee802154_dev *dev, u8 *level) +{ + might_sleep(); + BUG_ON(!level); + *level = 0xbe; + return 0; +} + +static int +at86rf230_state(struct ieee802154_dev *dev, int state) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + u8 val; + u8 desired_status; + + might_sleep(); + + if (state == STATE_FORCE_TX_ON) + desired_status = STATE_TX_ON; + else if (state == STATE_FORCE_TRX_OFF) + desired_status = STATE_TRX_OFF; + else + desired_status = state; + + do { + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); + if (rc) + goto err; + } while (val == STATE_TRANSITION_IN_PROGRESS); + + if (val == desired_status) + return 0; + + /* state is equal to phy states */ + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, state); + if (rc) + goto err; + + do { + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val); + if (rc) + goto err; + } while (val == STATE_TRANSITION_IN_PROGRESS); + + + if (val == desired_status) + return 0; + + pr_err("unexpected state change: %d, asked for %d\n", val, state); + return -EBUSY; + +err: + pr_err("error: %d\n", rc); + return rc; +} + +static int +at86rf230_start(struct ieee802154_dev *dev) +{ + struct at86rf230_local *lp = dev->priv; + u8 rc; + + rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1); + if (rc) + return rc; + + return at86rf230_state(dev, STATE_RX_ON); +} + +static void +at86rf230_stop(struct ieee802154_dev *dev) +{ + at86rf230_state(dev, STATE_FORCE_TRX_OFF); +} + +static int +at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + + might_sleep(); + + if (page != 0 || channel < 11 || channel > 26) { + WARN_ON(1); + return -EINVAL; + } + + rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel); + msleep(1); /* Wait for PLL */ + dev->phy->current_channel = channel; + + return 0; +} + +static int +at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb) +{ + struct at86rf230_local *lp = dev->priv; + int rc; + unsigned long flags; + + might_sleep(); + + rc = at86rf230_state(dev, STATE_FORCE_TX_ON); + if (rc) + goto err; + + spin_lock_irqsave(&lp->lock, flags); + lp->is_tx = 1; + INIT_COMPLETION(lp->tx_complete); + spin_unlock_irqrestore(&lp->lock, flags); + + rc = at86rf230_write_fbuf(lp, skb->data, skb->len); + if (rc) + goto err_rx; + + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX); + if (rc) + goto err_rx; + + rc = wait_for_completion_interruptible(&lp->tx_complete); + if (rc < 0) + goto err_rx; + + rc = at86rf230_start(dev); + + return rc; + +err_rx: + at86rf230_start(dev); +err: + pr_err("error: %d\n", rc); + + spin_lock_irqsave(&lp->lock, flags); + lp->is_tx = 0; + spin_unlock_irqrestore(&lp->lock, flags); + + return rc; +} + +static int at86rf230_rx(struct at86rf230_local *lp) +{ + u8 len = 128, lqi = 0; + int rc; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + + if (!skb) + return -ENOMEM; + + if (at86rf230_write_subreg(lp, SR_RX_PDT_DIS, 1) || + at86rf230_read_fbuf(lp, skb_put(skb, len), &len, &lqi) || + at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1) || + at86rf230_write_subreg(lp, SR_RX_PDT_DIS, 0)) { + goto err; + } + + if (len < 2) + goto err; + + skb_trim(skb, len - 2); /* We do not put CRC into the frame */ + + ieee802154_rx_irqsafe(lp->dev, skb, lqi); + + dev_dbg(&lp->spi->dev, "READ_FBUF: %d %d %x\n", rc, len, lqi); + + return 0; +err: + pr_debug("received frame is too small\n"); + + kfree_skb(skb); + return -EINVAL; +} + +static struct ieee802154_ops at86rf230_ops = { + .owner = THIS_MODULE, + .xmit = at86rf230_xmit, + .ed = at86rf230_ed, + .set_channel = at86rf230_channel, + .start = at86rf230_start, + .stop = at86rf230_stop, +}; + +static void at86rf230_irqwork(struct work_struct *work) +{ + struct at86rf230_local *lp = + container_of(work, struct at86rf230_local, irqwork); + u8 status = 0, val; + int rc; + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val); + status |= val; + + status &= ~IRQ_PLL_LOCK; /* ignore */ + status &= ~IRQ_RX_START; /* ignore */ + status &= ~IRQ_AMI; /* ignore */ + status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/ + + if (status & IRQ_TRX_END) { + status &= ~IRQ_TRX_END; + if (lp->is_tx) { + lp->is_tx = 0; + complete(&lp->tx_complete); + } else { + at86rf230_rx(lp); + } + } + + if (lp->irq_disabled) { + lp->irq_disabled = 0; + enable_irq(lp->spi->irq); + } + spin_unlock_irqrestore(&lp->lock, flags); +} + +static irqreturn_t at86rf230_isr(int irq, void *data) +{ + struct at86rf230_local *lp = data; + + spin_lock(&lp->lock); + if (!lp->irq_disabled) { + disable_irq_nosync(irq); + lp->irq_disabled = 1; + } + spin_unlock(&lp->lock); + + schedule_work(&lp->irqwork); + + return IRQ_HANDLED; +} + + +static int at86rf230_hw_init(struct at86rf230_local *lp) +{ + u8 status; + int rc; + + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + if (rc) + return rc; + + dev_info(&lp->spi->dev, "Status: %02x\n", status); + if (status == STATE_P_ON) { + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF); + if (rc) + return rc; + msleep(1); + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + if (rc) + return rc; + dev_info(&lp->spi->dev, "Status: %02x\n", status); + } + + rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, 0xff); /* IRQ_TRX_UR | + * IRQ_CCA_ED | + * IRQ_TRX_END | + * IRQ_PLL_UNL | + * IRQ_PLL_LOCK + */ + if (rc) + return rc; + + /* CLKM changes are applied immediately */ + rc = at86rf230_write_subreg(lp, SR_CLKM_SHA_SEL, 0x00); + if (rc) + return rc; + + /* Turn CLKM Off */ + rc = at86rf230_write_subreg(lp, SR_CLKM_CTRL, 0x00); + if (rc) + return rc; + /* Wait the next SLEEP cycle */ + msleep(100); + + rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ON); + if (rc) + return rc; + msleep(1); + + rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &status); + if (rc) + return rc; + dev_info(&lp->spi->dev, "Status: %02x\n", status); + + rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &status); + if (rc) + return rc; + if (!status) { + dev_err(&lp->spi->dev, "DVDD error\n"); + return -EINVAL; + } + + rc = at86rf230_read_subreg(lp, SR_AVDD_OK, &status); + if (rc) + return rc; + if (!status) { + dev_err(&lp->spi->dev, "AVDD error\n"); + return -EINVAL; + } + + return 0; +} + +static int at86rf230_suspend(struct spi_device *spi, pm_message_t message) +{ + return 0; +} + +static int at86rf230_resume(struct spi_device *spi) +{ + return 0; +} + +static int at86rf230_fill_data(struct spi_device *spi) +{ + struct at86rf230_local *lp = spi_get_drvdata(spi); + struct at86rf230_platform_data *pdata = spi->dev.platform_data; + + if (!pdata) { + dev_err(&spi->dev, "no platform_data\n"); + return -EINVAL; + } + + lp->rstn = pdata->rstn; + lp->slp_tr = pdata->slp_tr; + lp->dig2 = pdata->dig2; + + return 0; +} + +static int __devinit at86rf230_probe(struct spi_device *spi) +{ + struct ieee802154_dev *dev; + struct at86rf230_local *lp; + u8 man_id_0, man_id_1; + int rc; + const char *chip; + int supported = 0; + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ specified\n"); + return -EINVAL; + } + + dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); + if (!dev) + return -ENOMEM; + + lp = dev->priv; + lp->dev = dev; + + lp->spi = spi; + + dev->priv = lp; + dev->parent = &spi->dev; + dev->extra_tx_headroom = 0; + /* We do support only 2.4 Ghz */ + dev->phy->channels_supported[0] = 0x7FFF800; + dev->flags = IEEE802154_HW_OMIT_CKSUM; + + mutex_init(&lp->bmux); + INIT_WORK(&lp->irqwork, at86rf230_irqwork); + spin_lock_init(&lp->lock); + init_completion(&lp->tx_complete); + + spi_set_drvdata(spi, lp); + + rc = at86rf230_fill_data(spi); + if (rc) + goto err_fill; + + rc = gpio_request(lp->rstn, "rstn"); + if (rc) + goto err_rstn; + + if (gpio_is_valid(lp->slp_tr)) { + rc = gpio_request(lp->slp_tr, "slp_tr"); + if (rc) + goto err_slp_tr; + } + + rc = gpio_direction_output(lp->rstn, 1); + if (rc) + goto err_gpio_dir; + + if (gpio_is_valid(lp->slp_tr)) { + rc = gpio_direction_output(lp->slp_tr, 0); + if (rc) + goto err_gpio_dir; + } + + /* Reset */ + msleep(1); + gpio_set_value(lp->rstn, 0); + msleep(1); + gpio_set_value(lp->rstn, 1); + msleep(1); + + rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0); + if (rc) + goto err_gpio_dir; + rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1); + if (rc) + goto err_gpio_dir; + + if (man_id_1 != 0x00 || man_id_0 != 0x1f) { + dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n", + man_id_1, man_id_0); + rc = -EINVAL; + goto err_gpio_dir; + } + + rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part); + if (rc) + goto err_gpio_dir; + + rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers); + if (rc) + goto err_gpio_dir; + + switch (lp->part) { + case 2: + chip = "at86rf230"; + /* supported = 1; FIXME: should be easy to support; */ + break; + case 3: + chip = "at86rf231"; + supported = 1; + break; + default: + chip = "UNKNOWN"; + break; + } + + dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers); + if (!supported) { + rc = -ENOTSUPP; + goto err_gpio_dir; + } + + rc = at86rf230_hw_init(lp); + if (rc) + goto err_gpio_dir; + + rc = request_irq(spi->irq, at86rf230_isr, IRQF_SHARED, + dev_name(&spi->dev), lp); + if (rc) + goto err_gpio_dir; + + rc = ieee802154_register_device(lp->dev); + if (rc) + goto err_irq; + + return rc; + + ieee802154_unregister_device(lp->dev); +err_irq: + free_irq(spi->irq, lp); + flush_work(&lp->irqwork); +err_gpio_dir: + if (gpio_is_valid(lp->slp_tr)) + gpio_free(lp->slp_tr); +err_slp_tr: + gpio_free(lp->rstn); +err_rstn: +err_fill: + spi_set_drvdata(spi, NULL); + mutex_destroy(&lp->bmux); + ieee802154_free_device(lp->dev); + return rc; +} + +static int __devexit at86rf230_remove(struct spi_device *spi) +{ + struct at86rf230_local *lp = spi_get_drvdata(spi); + + ieee802154_unregister_device(lp->dev); + + free_irq(spi->irq, lp); + flush_work(&lp->irqwork); + + if (gpio_is_valid(lp->slp_tr)) + gpio_free(lp->slp_tr); + gpio_free(lp->rstn); + + spi_set_drvdata(spi, NULL); + mutex_destroy(&lp->bmux); + ieee802154_free_device(lp->dev); + + dev_dbg(&spi->dev, "unregistered at86rf230\n"); + return 0; +} + +static struct spi_driver at86rf230_driver = { + .driver = { + .name = "at86rf230", + .owner = THIS_MODULE, + }, + .probe = at86rf230_probe, + .remove = __devexit_p(at86rf230_remove), + .suspend = at86rf230_suspend, + .resume = at86rf230_resume, +}; + +static int __init at86rf230_init(void) +{ + return spi_register_driver(&at86rf230_driver); +} +module_init(at86rf230_init); + +static void __exit at86rf230_exit(void) +{ + spi_unregister_driver(&at86rf230_driver); +} +module_exit(at86rf230_exit); + +MODULE_DESCRIPTION("AT86RF230 Transceiver Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 122bb046bcfb8f3d2d94d9f7f6b74da187a740f2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 26 Jun 2012 06:52:45 +0000 Subject: team: fix team_adjust_ops with regard to enabled ports team_adjust_ops should check for enabled ports, not all ports. This may lead to division by zero. This patch fixes this. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 3a4a74be52d9..6b4cf6eca238 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -508,26 +508,31 @@ static void team_set_no_mode(struct team *team) team->mode = &__team_no_mode; } -static void team_adjust_ops(struct team *team) +static void __team_adjust_ops(struct team *team, int en_port_count) { /* * To avoid checks in rx/tx skb paths, ensure here that non-null and * correct ops are always set. */ - if (list_empty(&team->port_list) || - !team_is_mode_set(team) || !team->mode->ops->transmit) + if (!en_port_count || !team_is_mode_set(team) || + !team->mode->ops->transmit) team->ops.transmit = team_dummy_transmit; else team->ops.transmit = team->mode->ops->transmit; - if (list_empty(&team->port_list) || - !team_is_mode_set(team) || !team->mode->ops->receive) + if (!en_port_count || !team_is_mode_set(team) || + !team->mode->ops->receive) team->ops.receive = team_dummy_receive; else team->ops.receive = team->mode->ops->receive; } +static void team_adjust_ops(struct team *team) +{ + __team_adjust_ops(team, team->en_port_count); +} + /* * We can benefit from the fact that it's ensured no port is present * at the time of mode change. Therefore no packets are in fly so there's no @@ -687,6 +692,7 @@ static void team_port_enable(struct team *team, port->index = team->en_port_count++; hlist_add_head_rcu(&port->hlist, team_port_index_hash(team, port->index)); + team_adjust_ops(team); if (team->ops.port_enabled) team->ops.port_enabled(team, port); } @@ -708,16 +714,20 @@ static void __reconstruct_port_hlist(struct team *team, int rm_index) static void team_port_disable(struct team *team, struct team_port *port) { - int rm_index = port->index; - if (!team_port_enabled(port)) return; if (team->ops.port_disabled) team->ops.port_disabled(team, port); hlist_del_rcu(&port->hlist); - __reconstruct_port_hlist(team, rm_index); - team->en_port_count--; + __reconstruct_port_hlist(team, port->index); port->index = -1; + __team_adjust_ops(team, team->en_port_count - 1); + /* + * Wait until readers see adjusted ops. This ensures that + * readers never see team->en_port_count == 0 + */ + synchronize_rcu(); + team->en_port_count--; } #define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \ @@ -874,7 +884,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev) port->index = -1; team_port_enable(team, port); list_add_tail_rcu(&port->list, &team->port_list); - team_adjust_ops(team); __team_compute_features(team); __team_port_change_check(port, !!netif_carrier_ok(port_dev)); __team_options_change_check(team); @@ -928,7 +937,6 @@ static int team_port_del(struct team *team, struct net_device *port_dev) __team_port_change_check(port, false); team_port_disable(team, port); list_del_rcu(&port->list); - team_adjust_ops(team); netdev_rx_handler_unregister(port_dev); netdev_set_master(port_dev, NULL); vlan_vids_del_by_dev(port_dev, dev); -- cgit v1.2.3 From 52a4fd77808662a16cd17ad3b0e1ad75e0162d8b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 26 Jun 2012 06:52:46 +0000 Subject: team: do not allow to map disabled ports Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 5 ++--- drivers/net/team/team_mode_loadbalance.c | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 6b4cf6eca238..5350eeaa22ce 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -614,8 +614,6 @@ static int team_change_mode(struct team *team, const char *kind) * Rx path frame handler ************************/ -static bool team_port_enabled(struct team_port *port); - /* note: already called with rcu_read_lock */ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) { @@ -673,10 +671,11 @@ static bool team_port_find(const struct team *team, return false; } -static bool team_port_enabled(struct team_port *port) +bool team_port_enabled(struct team_port *port) { return port->index != -1; } +EXPORT_SYMBOL(team_port_enabled); /* * Enable/disable port by adding to enabled port hashlist and setting diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index c92fa02d6a63..51a4b199c75c 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -359,7 +359,8 @@ static int lb_tx_hash_to_port_mapping_set(struct team *team, unsigned char hash = ctx->info->array_index; list_for_each_entry(port, &team->port_list, list) { - if (ctx->data.u32_val == port->dev->ifindex) { + if (ctx->data.u32_val == port->dev->ifindex && + team_port_enabled(port)) { rcu_assign_pointer(LB_HTPM_PORT_BY_HASH(lb_priv, hash), port); return 0; -- cgit v1.2.3 From 85c931665d822f1bedd69ecaab09a8ba84643020 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 26 Jun 2012 05:41:24 +0000 Subject: connector: use nlmsg_put() instead of NLMSG_PUT() macro. The NLMSG_PUT() macro contains a hidden goto which makes the code hard to audit and very error prone. While been there also use the inline function nlmsg_data() instead of the NLMSG_DATA() macro to do explicit type checking. Signed-off-by: Javier Martinez Canillas Signed-off-by: David S. Miller --- drivers/connector/connector.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index dde6a0fad408..34e0e9e4d913 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -101,19 +101,19 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask) if (!skb) return -ENOMEM; - nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh)); + nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0); + if (!nlh) { + kfree_skb(skb); + return -EMSGSIZE; + } - data = NLMSG_DATA(nlh); + data = nlmsg_data(nlh); memcpy(data, msg, sizeof(*data) + msg->len); NETLINK_CB(skb).dst_group = group; return netlink_broadcast(dev->nls, skb, 0, group, gfp_mask); - -nlmsg_failure: - kfree_skb(skb); - return -EINVAL; } EXPORT_SYMBOL_GPL(cn_netlink_send); -- cgit v1.2.3 From a8edf8a690817ebfe68aa14c4bc482f62699077f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 26 Jun 2012 21:20:52 -0700 Subject: gdm72xx: Move away from NLMSG_PUT(). And use nlmsg_data() while we're here too. Signed-off-by: David S. Miller --- drivers/staging/gdm72xx/netlink_k.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c index 292af0f7f451..d0cb48a932ee 100644 --- a/drivers/staging/gdm72xx/netlink_k.c +++ b/drivers/staging/gdm72xx/netlink_k.c @@ -127,8 +127,12 @@ int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len) } seq++; - nlh = NLMSG_PUT(skb, 0, seq, type, len); - memcpy(NLMSG_DATA(nlh), msg, len); + nlh = nlmsg_put(skb, 0, seq, type, len, 0); + if (!nlh) { + kfree_skb(skb); + return -EMSGSIZE; + } + memcpy(nlmsg_data(nlh), msg, len); NETLINK_CB(skb).pid = 0; NETLINK_CB(skb).dst_group = 0; @@ -144,7 +148,5 @@ int netlink_send(struct sock *sock, int group, u16 type, void *msg, int len) } ret = 0; } - -nlmsg_failure: return ret; } -- cgit v1.2.3 From e05273341c573f7b543f45c06e4a232c5b7c5a59 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 26 Jun 2012 21:43:19 -0700 Subject: infiniband: netlink: Move away from NLMSG_NEW(). And use nlmsg_data() while we're here too. Signed-off-by: David S. Miller --- drivers/infiniband/core/netlink.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index e497dfbee435..1e691dca1820 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -108,12 +108,14 @@ void *ibnl_put_msg(struct sk_buff *skb, struct nlmsghdr **nlh, int seq, unsigned char *prev_tail; prev_tail = skb_tail_pointer(skb); - *nlh = NLMSG_NEW(skb, 0, seq, RDMA_NL_GET_TYPE(client, op), - len, NLM_F_MULTI); + *nlh = nlmsg_put(skb, 0, seq, RDMA_NL_GET_TYPE(client, op), + len, NLM_F_MULTI); + if (!*nlh) + goto out_nlmsg_trim; (*nlh)->nlmsg_len = skb_tail_pointer(skb) - prev_tail; - return NLMSG_DATA(*nlh); + return nlmsg_data(*nlh); -nlmsg_failure: +out_nlmsg_trim: nlmsg_trim(skb, prev_tail); return NULL; } -- cgit v1.2.3 From 2e499d3cc13365a87815266dda59904dcb8c8d6c Mon Sep 17 00:00:00 2001 From: Barak Witkowski Date: Tue, 26 Jun 2012 01:31:19 +0000 Subject: bnx2x, bnx2fc, bnx2i, cnic: Add statistics support and FCoE capabilities advertisement 1. When FCoE offload driver is registered, copy its capabilities to the chip scratchpad. 2. Copy FCoE/iSCSI MAC addresses in aligned manner to chip scratchpad. 3. Add FCoE/iSCSI statistics collection support Signed-off-by: Barak Witkowski Signed-off-by: Eilon Greenstein Signed-off-by: Eddie Wai Signed-off-by: Michael Chan Signed-off-by: Bhanu Prakash Gollapudi Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x.h | 2 + drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h | 120 +-------------- drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 41 ++++- .../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h | 168 +++++++++++++++++++++ drivers/net/ethernet/broadcom/cnic.c | 11 +- drivers/net/ethernet/broadcom/cnic_if.h | 9 ++ drivers/scsi/bnx2fc/bnx2fc.h | 4 + drivers/scsi/bnx2fc/bnx2fc_fcoe.c | 44 ++++++ drivers/scsi/bnx2i/57xx_iscsi_hsi.h | 16 +- drivers/scsi/bnx2i/bnx2i.h | 58 +++++++ drivers/scsi/bnx2i/bnx2i_hwi.c | 35 +++-- drivers/scsi/bnx2i/bnx2i_init.c | 40 +++++ drivers/scsi/bnx2i/bnx2i_iscsi.c | 11 ++ 13 files changed, 420 insertions(+), 139 deletions(-) create mode 100644 drivers/net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 7211cb07426e..362d16f1d256 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -51,6 +51,7 @@ #include "bnx2x_reg.h" #include "bnx2x_fw_defs.h" +#include "bnx2x_mfw_req.h" #include "bnx2x_hsi.h" #include "bnx2x_link.h" #include "bnx2x_sp.h" @@ -1317,6 +1318,7 @@ struct bnx2x { #define NO_ISCSI_FLAG (1 << 14) #define NO_FCOE_FLAG (1 << 15) #define BC_SUPPORTS_PFC_STATS (1 << 17) +#define BC_SUPPORTS_FCOE_FEATURES (1 << 19) #define USING_SINGLE_MSIX_FLAG (1 << 20) #define BC_SUPPORTS_DCBX_MSG_NON_PMF (1 << 21) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index e7c390c66b2a..76b6e65790f8 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -10,6 +10,7 @@ #define BNX2X_HSI_H #include "bnx2x_fw_defs.h" +#include "bnx2x_mfw_req.h" #define FW_ENCODE_32BIT_PATTERN 0x1e1e1e1e @@ -33,12 +34,6 @@ struct license_key { u32 reserved_b[4]; }; - -#define PORT_0 0 -#define PORT_1 1 -#define PORT_MAX 2 -#define NVM_PATH_MAX 2 - /**************************************************************************** * Shared HW configuration * ****************************************************************************/ @@ -1252,6 +1247,7 @@ struct drv_func_mb { #define REQ_BC_VER_4_VRFY_AFEX_SUPPORTED 0x00070002 #define REQ_BC_VER_4_SFP_TX_DISABLE_SUPPORTED 0x00070014 #define REQ_BC_VER_4_PFC_STATS_SUPPORTED 0x00070201 + #define REQ_BC_VER_4_FCOE_FEATURES 0x00070209 #define DRV_MSG_CODE_DCBX_ADMIN_PMF_MSG 0xb0000000 #define DRV_MSG_CODE_DCBX_PMF_DRV_OK 0xb2000000 @@ -2700,118 +2696,6 @@ struct host_func_stats { /* VIC definitions */ #define VICSTATST_UIF_INDEX 2 -/* current drv_info version */ -#define DRV_INFO_CUR_VER 1 - -/* drv_info op codes supported */ -enum drv_info_opcode { - ETH_STATS_OPCODE, - FCOE_STATS_OPCODE, - ISCSI_STATS_OPCODE -}; - -#define ETH_STAT_INFO_VERSION_LEN 12 -/* Per PCI Function Ethernet Statistics required from the driver */ -struct eth_stats_info { - /* Function's Driver Version. padded to 12 */ - u8 version[ETH_STAT_INFO_VERSION_LEN]; - /* Locally Admin Addr. BigEndian EIU48. Actual size is 6 bytes */ - u8 mac_local[8]; - u8 mac_add1[8]; /* Additional Programmed MAC Addr 1. */ - u8 mac_add2[8]; /* Additional Programmed MAC Addr 2. */ - u32 mtu_size; /* MTU Size. Note : Negotiated MTU */ - u32 feature_flags; /* Feature_Flags. */ -#define FEATURE_ETH_CHKSUM_OFFLOAD_MASK 0x01 -#define FEATURE_ETH_LSO_MASK 0x02 -#define FEATURE_ETH_BOOTMODE_MASK 0x1C -#define FEATURE_ETH_BOOTMODE_SHIFT 2 -#define FEATURE_ETH_BOOTMODE_NONE (0x0 << 2) -#define FEATURE_ETH_BOOTMODE_PXE (0x1 << 2) -#define FEATURE_ETH_BOOTMODE_ISCSI (0x2 << 2) -#define FEATURE_ETH_BOOTMODE_FCOE (0x3 << 2) -#define FEATURE_ETH_TOE_MASK 0x20 - u32 lso_max_size; /* LSO MaxOffloadSize. */ - u32 lso_min_seg_cnt; /* LSO MinSegmentCount. */ - /* Num Offloaded Connections TCP_IPv4. */ - u32 ipv4_ofld_cnt; - /* Num Offloaded Connections TCP_IPv6. */ - u32 ipv6_ofld_cnt; - u32 promiscuous_mode; /* Promiscuous Mode. non-zero true */ - u32 txq_size; /* TX Descriptors Queue Size */ - u32 rxq_size; /* RX Descriptors Queue Size */ - /* TX Descriptor Queue Avg Depth. % Avg Queue Depth since last poll */ - u32 txq_avg_depth; - /* RX Descriptors Queue Avg Depth. % Avg Queue Depth since last poll */ - u32 rxq_avg_depth; - /* IOV_Offload. 0=none; 1=MultiQueue, 2=VEB 3= VEPA*/ - u32 iov_offload; - /* Number of NetQueue/VMQ Config'd. */ - u32 netq_cnt; - u32 vf_cnt; /* Num VF assigned to this PF. */ -}; - -/* Per PCI Function FCOE Statistics required from the driver */ -struct fcoe_stats_info { - u8 version[12]; /* Function's Driver Version. */ - u8 mac_local[8]; /* Locally Admin Addr. */ - u8 mac_add1[8]; /* Additional Programmed MAC Addr 1. */ - u8 mac_add2[8]; /* Additional Programmed MAC Addr 2. */ - /* QoS Priority (per 802.1p). 0-7255 */ - u32 qos_priority; - u32 txq_size; /* FCoE TX Descriptors Queue Size. */ - u32 rxq_size; /* FCoE RX Descriptors Queue Size. */ - /* FCoE TX Descriptor Queue Avg Depth. */ - u32 txq_avg_depth; - /* FCoE RX Descriptors Queue Avg Depth. */ - u32 rxq_avg_depth; - u32 rx_frames_lo; /* FCoE RX Frames received. */ - u32 rx_frames_hi; /* FCoE RX Frames received. */ - u32 rx_bytes_lo; /* FCoE RX Bytes received. */ - u32 rx_bytes_hi; /* FCoE RX Bytes received. */ - u32 tx_frames_lo; /* FCoE TX Frames sent. */ - u32 tx_frames_hi; /* FCoE TX Frames sent. */ - u32 tx_bytes_lo; /* FCoE TX Bytes sent. */ - u32 tx_bytes_hi; /* FCoE TX Bytes sent. */ -}; - -/* Per PCI Function iSCSI Statistics required from the driver*/ -struct iscsi_stats_info { - u8 version[12]; /* Function's Driver Version. */ - u8 mac_local[8]; /* Locally Admin iSCSI MAC Addr. */ - u8 mac_add1[8]; /* Additional Programmed MAC Addr 1. */ - /* QoS Priority (per 802.1p). 0-7255 */ - u32 qos_priority; - u8 initiator_name[64]; /* iSCSI Boot Initiator Node name. */ - u8 ww_port_name[64]; /* iSCSI World wide port name */ - u8 boot_target_name[64];/* iSCSI Boot Target Name. */ - u8 boot_target_ip[16]; /* iSCSI Boot Target IP. */ - u32 boot_target_portal; /* iSCSI Boot Target Portal. */ - u8 boot_init_ip[16]; /* iSCSI Boot Initiator IP Address. */ - u32 max_frame_size; /* Max Frame Size. bytes */ - u32 txq_size; /* PDU TX Descriptors Queue Size. */ - u32 rxq_size; /* PDU RX Descriptors Queue Size. */ - u32 txq_avg_depth; /* PDU TX Descriptor Queue Avg Depth. */ - u32 rxq_avg_depth; /* PDU RX Descriptors Queue Avg Depth. */ - u32 rx_pdus_lo; /* iSCSI PDUs received. */ - u32 rx_pdus_hi; /* iSCSI PDUs received. */ - u32 rx_bytes_lo; /* iSCSI RX Bytes received. */ - u32 rx_bytes_hi; /* iSCSI RX Bytes received. */ - u32 tx_pdus_lo; /* iSCSI PDUs sent. */ - u32 tx_pdus_hi; /* iSCSI PDUs sent. */ - u32 tx_bytes_lo; /* iSCSI PDU TX Bytes sent. */ - u32 tx_bytes_hi; /* iSCSI PDU TX Bytes sent. */ - u32 pcp_prior_map_tbl; /* C-PCP to S-PCP Priority MapTable. - * 9 nibbles, the position of each nibble - * represents the C-PCP value, the value - * of the nibble = S-PCP value. - */ -}; - -union drv_info_to_mcp { - struct eth_stats_info ether_stat; - struct fcoe_stats_info fcoe_stat; - struct iscsi_stats_info iscsi_stat; -}; /* stats collected for afex. * NOTE: structure is exactly as expected to be received by the switch. diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 8ddc78e0d945..5b8b521bdbc3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -74,6 +74,8 @@ #define FW_FILE_NAME_E1H "bnx2x/bnx2x-e1h-" FW_FILE_VERSION ".fw" #define FW_FILE_NAME_E2 "bnx2x/bnx2x-e2-" FW_FILE_VERSION ".fw" +#define MAC_LEADING_ZERO_CNT (ALIGN(ETH_ALEN, sizeof(u32)) - ETH_ALEN) + /* Time in jiffies before concluding the transmitter is hung */ #define TX_TIMEOUT (5*HZ) @@ -3060,7 +3062,8 @@ static void bnx2x_drv_info_fcoe_stat(struct bnx2x *bp) struct fcoe_stats_info *fcoe_stat = &bp->slowpath->drv_info_to_mcp.fcoe_stat; - memcpy(fcoe_stat->mac_local, bp->fip_mac, ETH_ALEN); + memcpy(fcoe_stat->mac_local + MAC_LEADING_ZERO_CNT, + bp->fip_mac, ETH_ALEN); fcoe_stat->qos_priority = app->traffic_type_priority[LLFC_TRAFFIC_TYPE_FCOE]; @@ -3151,7 +3154,8 @@ static void bnx2x_drv_info_iscsi_stat(struct bnx2x *bp) struct iscsi_stats_info *iscsi_stat = &bp->slowpath->drv_info_to_mcp.iscsi_stat; - memcpy(iscsi_stat->mac_local, bp->cnic_eth_dev.iscsi_mac, ETH_ALEN); + memcpy(iscsi_stat->mac_local + MAC_LEADING_ZERO_CNT, + bp->cnic_eth_dev.iscsi_mac, ETH_ALEN); iscsi_stat->qos_priority = app->traffic_type_priority[LLFC_TRAFFIC_TYPE_ISCSI]; @@ -9732,6 +9736,9 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) bp->flags |= (val >= REQ_BC_VER_4_PFC_STATS_SUPPORTED) ? BC_SUPPORTS_PFC_STATS : 0; + bp->flags |= (val >= REQ_BC_VER_4_FCOE_FEATURES) ? + BC_SUPPORTS_FCOE_FEATURES : 0; + bp->flags |= (val >= REQ_BC_VER_4_DCBX_ADMIN_MSG_NON_PMF) ? BC_SUPPORTS_DCBX_MSG_NON_PMF : 0; boot_mode = SHMEM_RD(bp, @@ -12548,21 +12555,45 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl) break; } case DRV_CTL_ULP_REGISTER_CMD: { - int ulp_type = ctl->data.ulp_type; + int ulp_type = ctl->data.register_data.ulp_type; if (CHIP_IS_E3(bp)) { int idx = BP_FW_MB_IDX(bp); - u32 cap; + u32 cap = SHMEM2_RD(bp, drv_capabilities_flag[idx]); + int path = BP_PATH(bp); + int port = BP_PORT(bp); + int i; + u32 scratch_offset; + u32 *host_addr; - cap = SHMEM2_RD(bp, drv_capabilities_flag[idx]); + /* first write capability to shmem2 */ if (ulp_type == CNIC_ULP_ISCSI) cap |= DRV_FLAGS_CAPABILITIES_LOADED_ISCSI; else if (ulp_type == CNIC_ULP_FCOE) cap |= DRV_FLAGS_CAPABILITIES_LOADED_FCOE; SHMEM2_WR(bp, drv_capabilities_flag[idx], cap); + + if ((ulp_type != CNIC_ULP_FCOE) || + (!SHMEM2_HAS(bp, ncsi_oem_data_addr)) || + (!(bp->flags & BC_SUPPORTS_FCOE_FEATURES))) + break; + + /* if reached here - should write fcoe capabilities */ + scratch_offset = SHMEM2_RD(bp, ncsi_oem_data_addr); + if (!scratch_offset) + break; + scratch_offset += offsetof(struct glob_ncsi_oem_data, + fcoe_features[path][port]); + host_addr = (u32 *) &(ctl->data.register_data. + fcoe_features); + for (i = 0; i < sizeof(struct fcoe_capabilities); + i += 4) + REG_WR(bp, scratch_offset + i, + *(host_addr + i/4)); } break; } + case DRV_CTL_ULP_UNREGISTER_CMD: { int ulp_type = ctl->data.ulp_type; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h new file mode 100644 index 000000000000..ddd5106ad2f9 --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h @@ -0,0 +1,168 @@ +/* bnx2x_mfw_req.h: Broadcom Everest network driver. + * + * Copyright (c) 2012 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNX2X_MFW_REQ_H +#define BNX2X_MFW_REQ_H + +#define PORT_0 0 +#define PORT_1 1 +#define PORT_MAX 2 +#define NVM_PATH_MAX 2 + +/* FCoE capabilities required from the driver */ +struct fcoe_capabilities { + u32 capability1; + /* Maximum number of I/Os per connection */ + #define FCOE_IOS_PER_CONNECTION_MASK 0x0000ffff + #define FCOE_IOS_PER_CONNECTION_SHIFT 0 + /* Maximum number of Logins per port */ + #define FCOE_LOGINS_PER_PORT_MASK 0xffff0000 + #define FCOE_LOGINS_PER_PORT_SHIFT 16 + + u32 capability2; + /* Maximum number of exchanges */ + #define FCOE_NUMBER_OF_EXCHANGES_MASK 0x0000ffff + #define FCOE_NUMBER_OF_EXCHANGES_SHIFT 0 + /* Maximum NPIV WWN per port */ + #define FCOE_NPIV_WWN_PER_PORT_MASK 0xffff0000 + #define FCOE_NPIV_WWN_PER_PORT_SHIFT 16 + + u32 capability3; + /* Maximum number of targets supported */ + #define FCOE_TARGETS_SUPPORTED_MASK 0x0000ffff + #define FCOE_TARGETS_SUPPORTED_SHIFT 0 + /* Maximum number of outstanding commands across all connections */ + #define FCOE_OUTSTANDING_COMMANDS_MASK 0xffff0000 + #define FCOE_OUTSTANDING_COMMANDS_SHIFT 16 + + u32 capability4; + #define FCOE_CAPABILITY4_STATEFUL 0x00000001 + #define FCOE_CAPABILITY4_STATELESS 0x00000002 + #define FCOE_CAPABILITY4_CAPABILITIES_REPORTED_VALID 0x00000004 +}; + +struct glob_ncsi_oem_data { + u32 driver_version; + u32 unused[3]; + struct fcoe_capabilities fcoe_features[NVM_PATH_MAX][PORT_MAX]; +}; + +/* current drv_info version */ +#define DRV_INFO_CUR_VER 2 + +/* drv_info op codes supported */ +enum drv_info_opcode { + ETH_STATS_OPCODE, + FCOE_STATS_OPCODE, + ISCSI_STATS_OPCODE +}; + +#define ETH_STAT_INFO_VERSION_LEN 12 +/* Per PCI Function Ethernet Statistics required from the driver */ +struct eth_stats_info { + /* Function's Driver Version. padded to 12 */ + u8 version[ETH_STAT_INFO_VERSION_LEN]; + /* Locally Admin Addr. BigEndian EIU48. Actual size is 6 bytes */ + u8 mac_local[8]; + u8 mac_add1[8]; /* Additional Programmed MAC Addr 1. */ + u8 mac_add2[8]; /* Additional Programmed MAC Addr 2. */ + u32 mtu_size; /* MTU Size. Note : Negotiated MTU */ + u32 feature_flags; /* Feature_Flags. */ +#define FEATURE_ETH_CHKSUM_OFFLOAD_MASK 0x01 +#define FEATURE_ETH_LSO_MASK 0x02 +#define FEATURE_ETH_BOOTMODE_MASK 0x1C +#define FEATURE_ETH_BOOTMODE_SHIFT 2 +#define FEATURE_ETH_BOOTMODE_NONE (0x0 << 2) +#define FEATURE_ETH_BOOTMODE_PXE (0x1 << 2) +#define FEATURE_ETH_BOOTMODE_ISCSI (0x2 << 2) +#define FEATURE_ETH_BOOTMODE_FCOE (0x3 << 2) +#define FEATURE_ETH_TOE_MASK 0x20 + u32 lso_max_size; /* LSO MaxOffloadSize. */ + u32 lso_min_seg_cnt; /* LSO MinSegmentCount. */ + /* Num Offloaded Connections TCP_IPv4. */ + u32 ipv4_ofld_cnt; + /* Num Offloaded Connections TCP_IPv6. */ + u32 ipv6_ofld_cnt; + u32 promiscuous_mode; /* Promiscuous Mode. non-zero true */ + u32 txq_size; /* TX Descriptors Queue Size */ + u32 rxq_size; /* RX Descriptors Queue Size */ + /* TX Descriptor Queue Avg Depth. % Avg Queue Depth since last poll */ + u32 txq_avg_depth; + /* RX Descriptors Queue Avg Depth. % Avg Queue Depth since last poll */ + u32 rxq_avg_depth; + /* IOV_Offload. 0=none; 1=MultiQueue, 2=VEB 3= VEPA*/ + u32 iov_offload; + /* Number of NetQueue/VMQ Config'd. */ + u32 netq_cnt; + u32 vf_cnt; /* Num VF assigned to this PF. */ +}; + +/* Per PCI Function FCOE Statistics required from the driver */ +struct fcoe_stats_info { + u8 version[12]; /* Function's Driver Version. */ + u8 mac_local[8]; /* Locally Admin Addr. */ + u8 mac_add1[8]; /* Additional Programmed MAC Addr 1. */ + u8 mac_add2[8]; /* Additional Programmed MAC Addr 2. */ + /* QoS Priority (per 802.1p). 0-7255 */ + u32 qos_priority; + u32 txq_size; /* FCoE TX Descriptors Queue Size. */ + u32 rxq_size; /* FCoE RX Descriptors Queue Size. */ + /* FCoE TX Descriptor Queue Avg Depth. */ + u32 txq_avg_depth; + /* FCoE RX Descriptors Queue Avg Depth. */ + u32 rxq_avg_depth; + u32 rx_frames_lo; /* FCoE RX Frames received. */ + u32 rx_frames_hi; /* FCoE RX Frames received. */ + u32 rx_bytes_lo; /* FCoE RX Bytes received. */ + u32 rx_bytes_hi; /* FCoE RX Bytes received. */ + u32 tx_frames_lo; /* FCoE TX Frames sent. */ + u32 tx_frames_hi; /* FCoE TX Frames sent. */ + u32 tx_bytes_lo; /* FCoE TX Bytes sent. */ + u32 tx_bytes_hi; /* FCoE TX Bytes sent. */ +}; + +/* Per PCI Function iSCSI Statistics required from the driver*/ +struct iscsi_stats_info { + u8 version[12]; /* Function's Driver Version. */ + u8 mac_local[8]; /* Locally Admin iSCSI MAC Addr. */ + u8 mac_add1[8]; /* Additional Programmed MAC Addr 1. */ + /* QoS Priority (per 802.1p). 0-7255 */ + u32 qos_priority; + u8 initiator_name[64]; /* iSCSI Boot Initiator Node name. */ + u8 ww_port_name[64]; /* iSCSI World wide port name */ + u8 boot_target_name[64];/* iSCSI Boot Target Name. */ + u8 boot_target_ip[16]; /* iSCSI Boot Target IP. */ + u32 boot_target_portal; /* iSCSI Boot Target Portal. */ + u8 boot_init_ip[16]; /* iSCSI Boot Initiator IP Address. */ + u32 max_frame_size; /* Max Frame Size. bytes */ + u32 txq_size; /* PDU TX Descriptors Queue Size. */ + u32 rxq_size; /* PDU RX Descriptors Queue Size. */ + u32 txq_avg_depth; /* PDU TX Descriptor Queue Avg Depth. */ + u32 rxq_avg_depth; /* PDU RX Descriptors Queue Avg Depth. */ + u32 rx_pdus_lo; /* iSCSI PDUs received. */ + u32 rx_pdus_hi; /* iSCSI PDUs received. */ + u32 rx_bytes_lo; /* iSCSI RX Bytes received. */ + u32 rx_bytes_hi; /* iSCSI RX Bytes received. */ + u32 tx_pdus_lo; /* iSCSI PDUs sent. */ + u32 tx_pdus_hi; /* iSCSI PDUs sent. */ + u32 tx_bytes_lo; /* iSCSI PDU TX Bytes sent. */ + u32 tx_bytes_hi; /* iSCSI PDU TX Bytes sent. */ + u32 pcp_prior_map_tbl; /* C-PCP to S-PCP Priority MapTable. + * 9 nibbles, the position of each nibble + * represents the C-PCP value, the value + * of the nibble = S-PCP value. + */ +}; + +union drv_info_to_mcp { + struct eth_stats_info ether_stat; + struct fcoe_stats_info fcoe_stat; + struct iscsi_stats_info iscsi_stat; +}; +#endif /* BNX2X_MFW_REQ_H */ diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 65e66caea50d..0e9be2ba924e 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -256,11 +256,16 @@ static void cnic_ulp_ctl(struct cnic_dev *dev, int ulp_type, bool reg) struct cnic_local *cp = dev->cnic_priv; struct cnic_eth_dev *ethdev = cp->ethdev; struct drv_ctl_info info; + struct fcoe_capabilities *fcoe_cap = + &info.data.register_data.fcoe_features; - if (reg) + if (reg) { info.cmd = DRV_CTL_ULP_REGISTER_CMD; - else + if (ulp_type == CNIC_ULP_FCOE && dev->fcoe_cap) + memcpy(fcoe_cap, dev->fcoe_cap, sizeof(*fcoe_cap)); + } else { info.cmd = DRV_CTL_ULP_UNREGISTER_CMD; + } info.data.ulp_type = ulp_type; ethdev->drv_ctl(dev->netdev, &info); @@ -611,6 +616,8 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) if (ulp_type == CNIC_ULP_ISCSI) cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); + else if (ulp_type == CNIC_ULP_FCOE) + dev->fcoe_cap = NULL; synchronize_rcu(); diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index 289274e546be..d63d45591c1f 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -12,6 +12,8 @@ #ifndef CNIC_IF_H #define CNIC_IF_H +#include "bnx2x/bnx2x_mfw_req.h" + #define CNIC_MODULE_VERSION "2.5.10" #define CNIC_MODULE_RELDATE "March 21, 2012" @@ -131,6 +133,11 @@ struct drv_ctl_l2_ring { u32 cid; }; +struct drv_ctl_register_data { + int ulp_type; + struct fcoe_capabilities fcoe_features; +}; + struct drv_ctl_info { int cmd; union { @@ -138,6 +145,7 @@ struct drv_ctl_info { struct drv_ctl_io io; struct drv_ctl_l2_ring ring; int ulp_type; + struct drv_ctl_register_data register_data; char bytes[MAX_DRV_CTL_DATA]; } data; }; @@ -305,6 +313,7 @@ struct cnic_dev { int max_rdma_conn; union drv_info_to_mcp *stats_addr; + struct fcoe_capabilities *fcoe_cap; void *cnic_priv; }; diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index 0578fa0dc14b..42969e8a45bd 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -59,6 +59,7 @@ #include "57xx_hsi_bnx2fc.h" #include "bnx2fc_debug.h" #include "../../net/ethernet/broadcom/cnic_if.h" +#include "../../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h" #include "bnx2fc_constants.h" #define BNX2FC_NAME "bnx2fc" @@ -84,6 +85,8 @@ #define BNX2FC_NUM_MAX_SESS 1024 #define BNX2FC_NUM_MAX_SESS_LOG (ilog2(BNX2FC_NUM_MAX_SESS)) +#define BNX2FC_MAX_NPIV 256 + #define BNX2FC_MAX_OUTSTANDING_CMNDS 2048 #define BNX2FC_CAN_QUEUE BNX2FC_MAX_OUTSTANDING_CMNDS #define BNX2FC_ELSTM_XIDS BNX2FC_CAN_QUEUE @@ -206,6 +209,7 @@ struct bnx2fc_hba { struct fcoe_statistics_params *stats_buffer; dma_addr_t stats_buf_dma; struct completion stat_req_done; + struct fcoe_capabilities fcoe_cap; /*destroy handling */ struct timer_list destroy_timer; diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index f52f668fd247..05fe6620b3f0 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -1326,6 +1326,7 @@ static void bnx2fc_hba_destroy(struct bnx2fc_hba *hba) static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) { struct bnx2fc_hba *hba; + struct fcoe_capabilities *fcoe_cap; int rc; hba = kzalloc(sizeof(*hba), GFP_KERNEL); @@ -1361,6 +1362,21 @@ static struct bnx2fc_hba *bnx2fc_hba_create(struct cnic_dev *cnic) printk(KERN_ERR PFX "em_config:bnx2fc_cmd_mgr_alloc failed\n"); goto cmgr_err; } + fcoe_cap = &hba->fcoe_cap; + + fcoe_cap->capability1 = BNX2FC_TM_MAX_SQES << + FCOE_IOS_PER_CONNECTION_SHIFT; + fcoe_cap->capability1 |= BNX2FC_NUM_MAX_SESS << + FCOE_LOGINS_PER_PORT_SHIFT; + fcoe_cap->capability2 = BNX2FC_MAX_OUTSTANDING_CMNDS << + FCOE_NUMBER_OF_EXCHANGES_SHIFT; + fcoe_cap->capability2 |= BNX2FC_MAX_NPIV << + FCOE_NPIV_WWN_PER_PORT_SHIFT; + fcoe_cap->capability3 = BNX2FC_NUM_MAX_SESS << + FCOE_TARGETS_SUPPORTED_SHIFT; + fcoe_cap->capability3 |= BNX2FC_MAX_OUTSTANDING_CMNDS << + FCOE_OUTSTANDING_COMMANDS_SHIFT; + fcoe_cap->capability4 = FCOE_CAPABILITY4_STATEFUL; init_waitqueue_head(&hba->shutdown_wait); init_waitqueue_head(&hba->destroy_wait); @@ -1691,6 +1707,32 @@ static void bnx2fc_unbind_pcidev(struct bnx2fc_hba *hba) hba->pcidev = NULL; } +/** + * bnx2fc_ulp_get_stats - cnic callback to populate FCoE stats + * + * @handle: transport handle pointing to adapter struture + */ +static int bnx2fc_ulp_get_stats(void *handle) +{ + struct bnx2fc_hba *hba = handle; + struct cnic_dev *cnic; + struct fcoe_stats_info *stats_addr; + + if (!hba) + return -EINVAL; + + cnic = hba->cnic; + stats_addr = &cnic->stats_addr->fcoe_stat; + if (!stats_addr) + return -EINVAL; + + strncpy(stats_addr->version, BNX2FC_VERSION, + sizeof(stats_addr->version)); + stats_addr->txq_size = BNX2FC_SQ_WQES_MAX; + stats_addr->rxq_size = BNX2FC_CQ_WQES_MAX; + + return 0; +} /** @@ -1944,6 +1986,7 @@ static void bnx2fc_ulp_init(struct cnic_dev *dev) adapter_count++; mutex_unlock(&bnx2fc_dev_lock); + dev->fcoe_cap = &hba->fcoe_cap; clear_bit(BNX2FC_CNIC_REGISTERED, &hba->reg_with_cnic); rc = dev->register_device(dev, CNIC_ULP_FCOE, (void *) hba); @@ -2643,4 +2686,5 @@ static struct cnic_ulp_ops bnx2fc_cnic_cb = { .cnic_stop = bnx2fc_ulp_stop, .indicate_kcqes = bnx2fc_indicate_kcqe, .indicate_netevent = bnx2fc_indicate_netevent, + .cnic_get_stats = bnx2fc_ulp_get_stats, }; diff --git a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h index dc0a08e69c82..f2db5fe7bdc2 100644 --- a/drivers/scsi/bnx2i/57xx_iscsi_hsi.h +++ b/drivers/scsi/bnx2i/57xx_iscsi_hsi.h @@ -267,7 +267,13 @@ struct bnx2i_cmd_request { * task statistics for write response */ struct bnx2i_write_resp_task_stat { - u32 num_data_ins; +#if defined(__BIG_ENDIAN) + u16 num_r2ts; + u16 num_data_outs; +#elif defined(__LITTLE_ENDIAN) + u16 num_data_outs; + u16 num_r2ts; +#endif }; /* @@ -275,11 +281,11 @@ struct bnx2i_write_resp_task_stat { */ struct bnx2i_read_resp_task_stat { #if defined(__BIG_ENDIAN) - u16 num_data_outs; - u16 num_r2ts; + u16 reserved; + u16 num_data_ins; #elif defined(__LITTLE_ENDIAN) - u16 num_r2ts; - u16 num_data_outs; + u16 num_data_ins; + u16 reserved; #endif }; diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index 0c53c28dc3d3..2e326145825c 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -44,6 +44,8 @@ #include "57xx_iscsi_hsi.h" #include "57xx_iscsi_constants.h" +#include "../../net/ethernet/broadcom/bnx2x/bnx2x_mfw_req.h" + #define BNX2_ISCSI_DRIVER_NAME "bnx2i" #define BNX2I_MAX_ADAPTERS 8 @@ -126,6 +128,43 @@ #define REG_WR(__hba, offset, val) \ writel(val, __hba->regview + offset) +#ifdef CONFIG_32BIT +#define GET_STATS_64(__hba, dst, field) \ + do { \ + spin_lock_bh(&__hba->stat_lock); \ + dst->field##_lo = __hba->stats.field##_lo; \ + dst->field##_hi = __hba->stats.field##_hi; \ + spin_unlock_bh(&__hba->stat_lock); \ + } while (0) + +#define ADD_STATS_64(__hba, field, len) \ + do { \ + if (spin_trylock(&__hba->stat_lock)) { \ + if (__hba->stats.field##_lo + len < \ + __hba->stats.field##_lo) \ + __hba->stats.field##_hi++; \ + __hba->stats.field##_lo += len; \ + spin_unlock(&__hba->stat_lock); \ + } \ + } while (0) + +#else +#define GET_STATS_64(__hba, dst, field) \ + do { \ + u64 val, *out; \ + \ + val = __hba->bnx2i_stats.field; \ + out = (u64 *)&__hba->stats.field##_lo; \ + *out = cpu_to_le64(val); \ + out = (u64 *)&dst->field##_lo; \ + *out = cpu_to_le64(val); \ + } while (0) + +#define ADD_STATS_64(__hba, field, len) \ + do { \ + __hba->bnx2i_stats.field += len; \ + } while (0) +#endif /** * struct generic_pdu_resc - login pdu resource structure @@ -288,6 +327,15 @@ struct iscsi_cid_queue { struct bnx2i_conn **conn_cid_tbl; }; + +struct bnx2i_stats_info { + u64 rx_pdus; + u64 rx_bytes; + u64 tx_pdus; + u64 tx_bytes; +}; + + /** * struct bnx2i_hba - bnx2i adapter structure * @@ -341,6 +389,8 @@ struct iscsi_cid_queue { * @ctx_ccell_tasks: captures number of ccells and tasks supported by * currently offloaded connection, used to decode * context memory + * @stat_lock: spin lock used by the statistic collector (32 bit) + * @stats: local iSCSI statistic collection place holder * * Adapter Data Structure */ @@ -426,6 +476,12 @@ struct bnx2i_hba { u32 num_sess_opened; u32 num_conn_opened; unsigned int ctx_ccell_tasks; + +#ifdef CONFIG_32BIT + spinlock_t stat_lock; +#endif + struct bnx2i_stats_info bnx2i_stats; + struct iscsi_stats_info stats; }; @@ -749,6 +805,8 @@ extern void bnx2i_ulp_init(struct cnic_dev *dev); extern void bnx2i_ulp_exit(struct cnic_dev *dev); extern void bnx2i_start(void *handle); extern void bnx2i_stop(void *handle); +extern int bnx2i_get_stats(void *handle); + extern struct bnx2i_hba *get_adapter_list_head(void); struct bnx2i_conn *bnx2i_get_conn_from_id(struct bnx2i_hba *hba, diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index ece47e502282..49e8b056ca10 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -1350,6 +1350,7 @@ int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, struct cqe *cqe) { struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; struct bnx2i_cmd_response *resp_cqe; struct bnx2i_cmd *bnx2i_cmd; struct iscsi_task *task; @@ -1367,16 +1368,26 @@ int bnx2i_process_scsi_cmd_resp(struct iscsi_session *session, if (bnx2i_cmd->req.op_attr & ISCSI_CMD_REQUEST_READ) { conn->datain_pdus_cnt += - resp_cqe->task_stat.read_stat.num_data_outs; + resp_cqe->task_stat.read_stat.num_data_ins; conn->rxdata_octets += bnx2i_cmd->req.total_data_transfer_length; + ADD_STATS_64(hba, rx_pdus, + resp_cqe->task_stat.read_stat.num_data_ins); + ADD_STATS_64(hba, rx_bytes, + bnx2i_cmd->req.total_data_transfer_length); } else { conn->dataout_pdus_cnt += - resp_cqe->task_stat.read_stat.num_data_outs; + resp_cqe->task_stat.write_stat.num_data_outs; conn->r2t_pdus_cnt += - resp_cqe->task_stat.read_stat.num_r2ts; + resp_cqe->task_stat.write_stat.num_r2ts; conn->txdata_octets += bnx2i_cmd->req.total_data_transfer_length; + ADD_STATS_64(hba, tx_pdus, + resp_cqe->task_stat.write_stat.num_data_outs); + ADD_STATS_64(hba, tx_bytes, + bnx2i_cmd->req.total_data_transfer_length); + ADD_STATS_64(hba, rx_pdus, + resp_cqe->task_stat.write_stat.num_r2ts); } bnx2i_iscsi_unmap_sg_list(bnx2i_cmd); @@ -1961,6 +1972,7 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) { struct iscsi_conn *conn = bnx2i_conn->cls_conn->dd_data; struct iscsi_session *session = conn->session; + struct bnx2i_hba *hba = bnx2i_conn->hba; struct qp_info *qp; struct bnx2i_nop_in_msg *nopin; int tgt_async_msg; @@ -1973,7 +1985,7 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) if (!qp->cq_virt) { printk(KERN_ALERT "bnx2i (%s): cq resr freed in bh execution!", - bnx2i_conn->hba->netdev->name); + hba->netdev->name); goto out; } while (1) { @@ -1985,9 +1997,9 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) if (nopin->op_code == ISCSI_OP_NOOP_IN && nopin->itt == (u16) RESERVED_ITT) { printk(KERN_ALERT "bnx2i: Unsolicited " - "NOP-In detected for suspended " - "connection dev=%s!\n", - bnx2i_conn->hba->netdev->name); + "NOP-In detected for suspended " + "connection dev=%s!\n", + hba->netdev->name); bnx2i_unsol_pdu_adjust_rq(bnx2i_conn); goto cqe_out; } @@ -2001,7 +2013,7 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) /* Run the kthread engine only for data cmds All other cmds will be completed in this bh! */ bnx2i_queue_scsi_cmd_resp(session, bnx2i_conn, nopin); - break; + goto done; case ISCSI_OP_LOGIN_RSP: bnx2i_process_login_resp(session, bnx2i_conn, qp->cq_cons_qe); @@ -2044,11 +2056,15 @@ static int bnx2i_process_new_cqes(struct bnx2i_conn *bnx2i_conn) printk(KERN_ALERT "bnx2i: unknown opcode 0x%x\n", nopin->op_code); } + + ADD_STATS_64(hba, rx_pdus, 1); + ADD_STATS_64(hba, rx_bytes, nopin->data_length); +done: if (!tgt_async_msg) { if (!atomic_read(&bnx2i_conn->ep->num_active_cmds)) printk(KERN_ALERT "bnx2i (%s): no active cmd! " "op 0x%x\n", - bnx2i_conn->hba->netdev->name, + hba->netdev->name, nopin->op_code); else atomic_dec(&bnx2i_conn->ep->num_active_cmds); @@ -2692,6 +2708,7 @@ struct cnic_ulp_ops bnx2i_cnic_cb = { .cm_remote_close = bnx2i_cm_remote_close, .cm_remote_abort = bnx2i_cm_remote_abort, .iscsi_nl_send_msg = bnx2i_send_nl_mesg, + .cnic_get_stats = bnx2i_get_stats, .owner = THIS_MODULE }; diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 8b6816706ee5..7729a5223b33 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -380,6 +380,46 @@ void bnx2i_ulp_exit(struct cnic_dev *dev) } +/** + * bnx2i_get_stats - Retrieve various statistic from iSCSI offload + * @handle: bnx2i_hba + * + * function callback exported via bnx2i - cnic driver interface to + * retrieve various iSCSI offload related statistics. + */ +int bnx2i_get_stats(void *handle) +{ + struct bnx2i_hba *hba = handle; + struct iscsi_stats_info *stats; + + if (!hba) + return -EINVAL; + + stats = (struct iscsi_stats_info *)hba->cnic->stats_addr; + + if (!stats) + return -ENOMEM; + + memcpy(stats->version, DRV_MODULE_VERSION, sizeof(stats->version)); + memcpy(stats->mac_add1 + 2, hba->cnic->mac_addr, ETH_ALEN); + + stats->max_frame_size = hba->netdev->mtu; + stats->txq_size = hba->max_sqes; + stats->rxq_size = hba->max_cqes; + + stats->txq_avg_depth = 0; + stats->rxq_avg_depth = 0; + + GET_STATS_64(hba, stats, rx_pdus); + GET_STATS_64(hba, stats, rx_bytes); + + GET_STATS_64(hba, stats, tx_pdus); + GET_STATS_64(hba, stats, tx_bytes); + + return 0; +} + + /** * bnx2i_percpu_thread_create - Create a receive thread for an * online CPU diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index f8d516b53161..b40ac017012c 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -874,6 +874,11 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) hba->conn_ctx_destroy_tmo = 2 * HZ; } +#ifdef CONFIG_32BIT + spin_lock_init(&hba->stat_lock); +#endif + memset(&hba->stats, 0, sizeof(struct iscsi_stats_info)); + if (iscsi_host_add(shost, &hba->pcidev->dev)) goto free_dump_mem; return hba; @@ -1181,12 +1186,18 @@ static int bnx2i_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task) { struct bnx2i_conn *bnx2i_conn = conn->dd_data; + struct bnx2i_hba *hba = bnx2i_conn->hba; struct bnx2i_cmd *cmd = task->dd_data; memset(bnx2i_conn->gen_pdu.req_buf, 0, ISCSI_DEF_MAX_RECV_SEG_LEN); bnx2i_setup_cmd_wqe_template(cmd); bnx2i_conn->gen_pdu.req_buf_size = task->data_count; + + /* Tx PDU/data length count */ + ADD_STATS_64(hba, tx_pdus, 1); + ADD_STATS_64(hba, tx_bytes, task->data_count); + if (task->data_count) { memcpy(bnx2i_conn->gen_pdu.req_buf, task->data, task->data_count); -- cgit v1.2.3 From ebe6e21d233bdc4f6ae5f5f649d13beb4ed7a763 Mon Sep 17 00:00:00 2001 From: Merav Sicron Date: Tue, 26 Jun 2012 06:11:03 +0000 Subject: bnx2x: Change bnx2x_tests_str_arr to static char This patch changes the definition of bnx2x_tests_str_arr from char to static char. This correction will also eliminate the sparse warning created in commit cf2c1df62e065bfc15e38daf2d3479a56b320f29. Signed-off-by: Merav Sicron Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 1f8c1561cdec..bff31290198b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1600,7 +1600,7 @@ static int bnx2x_set_pauseparam(struct net_device *dev, return 0; } -char *bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF] = { +static char *bnx2x_tests_str_arr[BNX2X_NUM_TESTS_SF] = { "register_test (offline) ", "memory_test (offline) ", "int_loopback_test (offline)", -- cgit v1.2.3 From c26502680e59af556211fc3fd3ae0f2aa5d98440 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 26 Jun 2012 19:59:51 +0000 Subject: net: sh_eth: remove unnecessary function The sh_eth_timer() called mod_timer() for itself. So, this patch removes the function. Signed-off-by: Yoshihiro Shimoda Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 22 ---------------------- drivers/net/ethernet/renesas/sh_eth.h | 1 - 2 files changed, 23 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 326cb91abdad..cf0bc316cb60 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -1339,14 +1339,6 @@ other_irq: return ret; } -static void sh_eth_timer(unsigned long data) -{ - struct net_device *ndev = (struct net_device *)data; - struct sh_eth_private *mdp = netdev_priv(ndev); - - mod_timer(&mdp->timer, jiffies + (10 * HZ)); -} - /* PHY state control function */ static void sh_eth_adjust_link(struct net_device *ndev) { @@ -1594,11 +1586,6 @@ static int sh_eth_open(struct net_device *ndev) if (ret) goto out_free_irq; - /* Set the timer to check for link beat. */ - init_timer(&mdp->timer); - mdp->timer.expires = (jiffies + (24 * HZ)) / 10;/* 2.4 sec. */ - setup_timer(&mdp->timer, sh_eth_timer, (unsigned long)ndev); - return ret; out_free_irq: @@ -1623,9 +1610,6 @@ static void sh_eth_tx_timeout(struct net_device *ndev) /* tx_errors count up */ ndev->stats.tx_errors++; - /* timer off */ - del_timer_sync(&mdp->timer); - /* Free all the skbuffs in the Rx queue. */ for (i = 0; i < RX_RING_SIZE; i++) { rxdesc = &mdp->rx_ring[i]; @@ -1643,10 +1627,6 @@ static void sh_eth_tx_timeout(struct net_device *ndev) /* device init */ sh_eth_dev_init(ndev); - - /* timer on */ - mdp->timer.expires = (jiffies + (24 * HZ)) / 10;/* 2.4 sec. */ - add_timer(&mdp->timer); } /* Packet transmit function */ @@ -1719,8 +1699,6 @@ static int sh_eth_close(struct net_device *ndev) free_irq(ndev->irq, ndev); - del_timer_sync(&mdp->timer); - /* Free all the skbuffs in the Rx queue. */ sh_eth_ring_free(ndev); diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index d6763b1392d7..5af3f2afaa28 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -772,7 +772,6 @@ struct sh_eth_private { struct sh_eth_txdesc *tx_ring; struct sk_buff **rx_skbuff; struct sk_buff **tx_skbuff; - struct timer_list timer; spinlock_t lock; u32 cur_rx, dirty_rx; /* Producer/consumer ring indices */ u32 cur_tx, dirty_tx; -- cgit v1.2.3 From 2ecbb783c3bf5a63f555c39deef308dcc1902b7f Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 26 Jun 2012 19:59:58 +0000 Subject: net: sh_eth: remove unnecessary members/definitions This patch removes unnecessary members in sh_th_private. This patch also removes unnecessary definitions in sh_eth.h Signed-off-by: Yoshihiro Shimoda Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 7 +--- drivers/net/ethernet/renesas/sh_eth.h | 69 ----------------------------------- 2 files changed, 1 insertion(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index cf0bc316cb60..43e76d21e57f 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -941,7 +941,6 @@ static int sh_eth_dev_init(struct net_device *ndev) { int ret = 0; struct sh_eth_private *mdp = netdev_priv(ndev); - u_int32_t rx_int_var, tx_int_var; u32 val; /* Soft Reset */ @@ -971,9 +970,7 @@ static int sh_eth_dev_init(struct net_device *ndev) /* Frame recv control */ sh_eth_write(ndev, mdp->cd->rmcr_value, RMCR); - rx_int_var = mdp->rx_int_var = DESC_I_RINT8 | DESC_I_RINT5; - tx_int_var = mdp->tx_int_var = DESC_I_TINT2; - sh_eth_write(ndev, rx_int_var | tx_int_var, TRSCER); + sh_eth_write(ndev, DESC_I_RINT8 | DESC_I_RINT5 | DESC_I_TINT2, TRSCER); if (mdp->cd->bculr) sh_eth_write(ndev, 0x800, BCULR); /* Burst sycle set */ @@ -2336,8 +2333,6 @@ static int sh_eth_drv_probe(struct platform_device *pdev) /* debug message level */ mdp->msg_enable = SH_ETH_DEF_MSG_ENABLE; - mdp->post_rx = POST_RX >> (devno << 1); - mdp->post_fw = POST_FW >> (devno << 1); /* read and set MAC address */ read_mac_address(ndev, pd->mac_addr); diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 5af3f2afaa28..37a0702b7490 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -585,71 +585,6 @@ enum RPADIR_BIT { /* FDR */ #define DEFAULT_FDR_INIT 0x00000707 -enum phy_offsets { - PHY_CTRL = 0, PHY_STAT = 1, PHY_IDT1 = 2, PHY_IDT2 = 3, - PHY_ANA = 4, PHY_ANL = 5, PHY_ANE = 6, - PHY_16 = 16, -}; - -/* PHY_CTRL */ -enum PHY_CTRL_BIT { - PHY_C_RESET = 0x8000, PHY_C_LOOPBK = 0x4000, PHY_C_SPEEDSL = 0x2000, - PHY_C_ANEGEN = 0x1000, PHY_C_PWRDN = 0x0800, PHY_C_ISO = 0x0400, - PHY_C_RANEG = 0x0200, PHY_C_DUPLEX = 0x0100, PHY_C_COLT = 0x0080, -}; -#define DM9161_PHY_C_ANEGEN 0 /* auto nego special */ - -/* PHY_STAT */ -enum PHY_STAT_BIT { - PHY_S_100T4 = 0x8000, PHY_S_100X_F = 0x4000, PHY_S_100X_H = 0x2000, - PHY_S_10T_F = 0x1000, PHY_S_10T_H = 0x0800, PHY_S_ANEGC = 0x0020, - PHY_S_RFAULT = 0x0010, PHY_S_ANEGA = 0x0008, PHY_S_LINK = 0x0004, - PHY_S_JAB = 0x0002, PHY_S_EXTD = 0x0001, -}; - -/* PHY_ANA */ -enum PHY_ANA_BIT { - PHY_A_NP = 0x8000, PHY_A_ACK = 0x4000, PHY_A_RF = 0x2000, - PHY_A_FCS = 0x0400, PHY_A_T4 = 0x0200, PHY_A_FDX = 0x0100, - PHY_A_HDX = 0x0080, PHY_A_10FDX = 0x0040, PHY_A_10HDX = 0x0020, - PHY_A_SEL = 0x001e, -}; -/* PHY_ANL */ -enum PHY_ANL_BIT { - PHY_L_NP = 0x8000, PHY_L_ACK = 0x4000, PHY_L_RF = 0x2000, - PHY_L_FCS = 0x0400, PHY_L_T4 = 0x0200, PHY_L_FDX = 0x0100, - PHY_L_HDX = 0x0080, PHY_L_10FDX = 0x0040, PHY_L_10HDX = 0x0020, - PHY_L_SEL = 0x001f, -}; - -/* PHY_ANE */ -enum PHY_ANE_BIT { - PHY_E_PDF = 0x0010, PHY_E_LPNPA = 0x0008, PHY_E_NPA = 0x0004, - PHY_E_PRX = 0x0002, PHY_E_LPANEGA = 0x0001, -}; - -/* DM9161 */ -enum PHY_16_BIT { - PHY_16_BP4B45 = 0x8000, PHY_16_BPSCR = 0x4000, PHY_16_BPALIGN = 0x2000, - PHY_16_BP_ADPOK = 0x1000, PHY_16_Repeatmode = 0x0800, - PHY_16_TXselect = 0x0400, - PHY_16_Rsvd = 0x0200, PHY_16_RMIIEnable = 0x0100, - PHY_16_Force100LNK = 0x0080, - PHY_16_APDLED_CTL = 0x0040, PHY_16_COLLED_CTL = 0x0020, - PHY_16_RPDCTR_EN = 0x0010, - PHY_16_ResetStMch = 0x0008, PHY_16_PreamSupr = 0x0004, - PHY_16_Sleepmode = 0x0002, - PHY_16_RemoteLoopOut = 0x0001, -}; - -#define POST_RX 0x08 -#define POST_FW 0x04 -#define POST0_RX (POST_RX) -#define POST0_FW (POST_FW) -#define POST1_RX (POST_RX >> 2) -#define POST1_FW (POST_FW >> 2) -#define POST_ALL (POST0_RX | POST0_FW | POST1_RX | POST1_FW) - /* ARSTR */ enum ARSTR_BIT { ARSTR_ARSTR = 0x00000001, }; @@ -786,10 +721,6 @@ struct sh_eth_private { int msg_enable; int speed; int duplex; - u32 rx_int_var, tx_int_var; /* interrupt control variables */ - char post_rx; /* POST receive */ - char post_fw; /* POST forward */ - struct net_device_stats tsu_stats; /* TSU forward status */ int port; /* for TSU */ int vlan_num_ids; /* for VLAN tag filter */ -- cgit v1.2.3 From 91c77550000a7d888aaf9f9ac13e3e3485d18560 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 26 Jun 2012 20:00:01 +0000 Subject: net: sh_eth: fix up the buffer pointers After freeing the buffer, the driver should change the value of the pointer to NULL. Signed-off-by: Yoshihiro Shimoda Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 43e76d21e57f..2dd2ff5ecb5a 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -788,6 +788,7 @@ static void sh_eth_ring_free(struct net_device *ndev) } } kfree(mdp->rx_skbuff); + mdp->rx_skbuff = NULL; /* Free Tx skb ringbuffer */ if (mdp->tx_skbuff) { @@ -797,6 +798,7 @@ static void sh_eth_ring_free(struct net_device *ndev) } } kfree(mdp->tx_skbuff); + mdp->tx_skbuff = NULL; } /* format skb and descriptor buffer */ @@ -933,10 +935,31 @@ desc_ring_free: skb_ring_free: /* Free Rx and Tx skb ring buffer */ sh_eth_ring_free(ndev); + mdp->tx_ring = NULL; + mdp->rx_ring = NULL; return ret; } +static void sh_eth_free_dma_buffer(struct sh_eth_private *mdp) +{ + int ringsize; + + if (mdp->rx_ring) { + ringsize = sizeof(struct sh_eth_rxdesc) * RX_RING_SIZE; + dma_free_coherent(NULL, ringsize, mdp->rx_ring, + mdp->rx_desc_dma); + mdp->rx_ring = NULL; + } + + if (mdp->tx_ring) { + ringsize = sizeof(struct sh_eth_txdesc) * TX_RING_SIZE; + dma_free_coherent(NULL, ringsize, mdp->tx_ring, + mdp->tx_desc_dma); + mdp->tx_ring = NULL; + } +} + static int sh_eth_dev_init(struct net_device *ndev) { int ret = 0; @@ -1677,7 +1700,6 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) static int sh_eth_close(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); - int ringsize; netif_stop_queue(ndev); @@ -1700,12 +1722,7 @@ static int sh_eth_close(struct net_device *ndev) sh_eth_ring_free(ndev); /* free DMA buffer */ - ringsize = sizeof(struct sh_eth_rxdesc) * RX_RING_SIZE; - dma_free_coherent(NULL, ringsize, mdp->rx_ring, mdp->rx_desc_dma); - - /* free DMA buffer */ - ringsize = sizeof(struct sh_eth_txdesc) * TX_RING_SIZE; - dma_free_coherent(NULL, ringsize, mdp->tx_ring, mdp->tx_desc_dma); + sh_eth_free_dma_buffer(mdp); pm_runtime_put_sync(&mdp->pdev->dev); -- cgit v1.2.3 From 525b8075edda9c2ab4b81e210505bd7487ea6e56 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 26 Jun 2012 20:00:03 +0000 Subject: net: sh_eth: add support for set_ringparam/get_ringparam This patch supports the ethtool's set_ringparam() and get_ringparam(). Signed-off-by: Yoshihiro Shimoda Signed-off-by: David S. Miller --- drivers/net/ethernet/renesas/sh_eth.c | 139 ++++++++++++++++++++++++++-------- drivers/net/ethernet/renesas/sh_eth.h | 6 ++ 2 files changed, 112 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index 2dd2ff5ecb5a..af0b867a6cf6 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -782,7 +782,7 @@ static void sh_eth_ring_free(struct net_device *ndev) /* Free Rx skb ringbuffer */ if (mdp->rx_skbuff) { - for (i = 0; i < RX_RING_SIZE; i++) { + for (i = 0; i < mdp->num_rx_ring; i++) { if (mdp->rx_skbuff[i]) dev_kfree_skb(mdp->rx_skbuff[i]); } @@ -792,7 +792,7 @@ static void sh_eth_ring_free(struct net_device *ndev) /* Free Tx skb ringbuffer */ if (mdp->tx_skbuff) { - for (i = 0; i < TX_RING_SIZE; i++) { + for (i = 0; i < mdp->num_tx_ring; i++) { if (mdp->tx_skbuff[i]) dev_kfree_skb(mdp->tx_skbuff[i]); } @@ -809,8 +809,8 @@ static void sh_eth_ring_format(struct net_device *ndev) struct sk_buff *skb; struct sh_eth_rxdesc *rxdesc = NULL; struct sh_eth_txdesc *txdesc = NULL; - int rx_ringsize = sizeof(*rxdesc) * RX_RING_SIZE; - int tx_ringsize = sizeof(*txdesc) * TX_RING_SIZE; + int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring; + int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring; mdp->cur_rx = mdp->cur_tx = 0; mdp->dirty_rx = mdp->dirty_tx = 0; @@ -818,7 +818,7 @@ static void sh_eth_ring_format(struct net_device *ndev) memset(mdp->rx_ring, 0, rx_ringsize); /* build Rx ring buffer */ - for (i = 0; i < RX_RING_SIZE; i++) { + for (i = 0; i < mdp->num_rx_ring; i++) { /* skb */ mdp->rx_skbuff[i] = NULL; skb = netdev_alloc_skb(ndev, mdp->rx_buf_sz); @@ -844,7 +844,7 @@ static void sh_eth_ring_format(struct net_device *ndev) } } - mdp->dirty_rx = (u32) (i - RX_RING_SIZE); + mdp->dirty_rx = (u32) (i - mdp->num_rx_ring); /* Mark the last entry as wrapping the ring. */ rxdesc->status |= cpu_to_edmac(mdp, RD_RDEL); @@ -852,7 +852,7 @@ static void sh_eth_ring_format(struct net_device *ndev) memset(mdp->tx_ring, 0, tx_ringsize); /* build Tx ring buffer */ - for (i = 0; i < TX_RING_SIZE; i++) { + for (i = 0; i < mdp->num_tx_ring; i++) { mdp->tx_skbuff[i] = NULL; txdesc = &mdp->tx_ring[i]; txdesc->status = cpu_to_edmac(mdp, TD_TFP); @@ -886,7 +886,7 @@ static int sh_eth_ring_init(struct net_device *ndev) mdp->rx_buf_sz += NET_IP_ALIGN; /* Allocate RX and TX skb rings */ - mdp->rx_skbuff = kmalloc(sizeof(*mdp->rx_skbuff) * RX_RING_SIZE, + mdp->rx_skbuff = kmalloc(sizeof(*mdp->rx_skbuff) * mdp->num_rx_ring, GFP_KERNEL); if (!mdp->rx_skbuff) { dev_err(&ndev->dev, "Cannot allocate Rx skb\n"); @@ -894,7 +894,7 @@ static int sh_eth_ring_init(struct net_device *ndev) return ret; } - mdp->tx_skbuff = kmalloc(sizeof(*mdp->tx_skbuff) * TX_RING_SIZE, + mdp->tx_skbuff = kmalloc(sizeof(*mdp->tx_skbuff) * mdp->num_tx_ring, GFP_KERNEL); if (!mdp->tx_skbuff) { dev_err(&ndev->dev, "Cannot allocate Tx skb\n"); @@ -903,7 +903,7 @@ static int sh_eth_ring_init(struct net_device *ndev) } /* Allocate all Rx descriptors. */ - rx_ringsize = sizeof(struct sh_eth_rxdesc) * RX_RING_SIZE; + rx_ringsize = sizeof(struct sh_eth_rxdesc) * mdp->num_rx_ring; mdp->rx_ring = dma_alloc_coherent(NULL, rx_ringsize, &mdp->rx_desc_dma, GFP_KERNEL); @@ -917,7 +917,7 @@ static int sh_eth_ring_init(struct net_device *ndev) mdp->dirty_rx = 0; /* Allocate all Tx descriptors. */ - tx_ringsize = sizeof(struct sh_eth_txdesc) * TX_RING_SIZE; + tx_ringsize = sizeof(struct sh_eth_txdesc) * mdp->num_tx_ring; mdp->tx_ring = dma_alloc_coherent(NULL, tx_ringsize, &mdp->tx_desc_dma, GFP_KERNEL); if (!mdp->tx_ring) { @@ -946,21 +946,21 @@ static void sh_eth_free_dma_buffer(struct sh_eth_private *mdp) int ringsize; if (mdp->rx_ring) { - ringsize = sizeof(struct sh_eth_rxdesc) * RX_RING_SIZE; + ringsize = sizeof(struct sh_eth_rxdesc) * mdp->num_rx_ring; dma_free_coherent(NULL, ringsize, mdp->rx_ring, mdp->rx_desc_dma); mdp->rx_ring = NULL; } if (mdp->tx_ring) { - ringsize = sizeof(struct sh_eth_txdesc) * TX_RING_SIZE; + ringsize = sizeof(struct sh_eth_txdesc) * mdp->num_tx_ring; dma_free_coherent(NULL, ringsize, mdp->tx_ring, mdp->tx_desc_dma); mdp->tx_ring = NULL; } } -static int sh_eth_dev_init(struct net_device *ndev) +static int sh_eth_dev_init(struct net_device *ndev, bool start) { int ret = 0; struct sh_eth_private *mdp = netdev_priv(ndev); @@ -1008,7 +1008,8 @@ static int sh_eth_dev_init(struct net_device *ndev) RFLR); sh_eth_write(ndev, sh_eth_read(ndev, EESR), EESR); - sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); + if (start) + sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); /* PAUSE Prohibition */ val = (sh_eth_read(ndev, ECMR) & ECMR_DM) | @@ -1023,7 +1024,8 @@ static int sh_eth_dev_init(struct net_device *ndev) sh_eth_write(ndev, mdp->cd->ecsr_value, ECSR); /* E-MAC Interrupt Enable register */ - sh_eth_write(ndev, mdp->cd->ecsipr_value, ECSIPR); + if (start) + sh_eth_write(ndev, mdp->cd->ecsipr_value, ECSIPR); /* Set MAC address */ update_mac_address(ndev); @@ -1036,10 +1038,12 @@ static int sh_eth_dev_init(struct net_device *ndev) if (mdp->cd->tpauser) sh_eth_write(ndev, TPAUSER_UNLIMITED, TPAUSER); - /* Setting the Rx mode will start the Rx process. */ - sh_eth_write(ndev, EDRRR_R, EDRRR); + if (start) { + /* Setting the Rx mode will start the Rx process. */ + sh_eth_write(ndev, EDRRR_R, EDRRR); - netif_start_queue(ndev); + netif_start_queue(ndev); + } out: return ret; @@ -1054,7 +1058,7 @@ static int sh_eth_txfree(struct net_device *ndev) int entry = 0; for (; mdp->cur_tx - mdp->dirty_tx > 0; mdp->dirty_tx++) { - entry = mdp->dirty_tx % TX_RING_SIZE; + entry = mdp->dirty_tx % mdp->num_tx_ring; txdesc = &mdp->tx_ring[entry]; if (txdesc->status & cpu_to_edmac(mdp, TD_TACT)) break; @@ -1067,7 +1071,7 @@ static int sh_eth_txfree(struct net_device *ndev) freeNum++; } txdesc->status = cpu_to_edmac(mdp, TD_TFP); - if (entry >= TX_RING_SIZE - 1) + if (entry >= mdp->num_tx_ring - 1) txdesc->status |= cpu_to_edmac(mdp, TD_TDLE); ndev->stats.tx_packets++; @@ -1082,8 +1086,8 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) struct sh_eth_private *mdp = netdev_priv(ndev); struct sh_eth_rxdesc *rxdesc; - int entry = mdp->cur_rx % RX_RING_SIZE; - int boguscnt = (mdp->dirty_rx + RX_RING_SIZE) - mdp->cur_rx; + int entry = mdp->cur_rx % mdp->num_rx_ring; + int boguscnt = (mdp->dirty_rx + mdp->num_rx_ring) - mdp->cur_rx; struct sk_buff *skb; u16 pkt_len = 0; u32 desc_status; @@ -1134,13 +1138,13 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) ndev->stats.rx_bytes += pkt_len; } rxdesc->status |= cpu_to_edmac(mdp, RD_RACT); - entry = (++mdp->cur_rx) % RX_RING_SIZE; + entry = (++mdp->cur_rx) % mdp->num_rx_ring; rxdesc = &mdp->rx_ring[entry]; } /* Refill the Rx ring buffers. */ for (; mdp->cur_rx - mdp->dirty_rx > 0; mdp->dirty_rx++) { - entry = mdp->dirty_rx % RX_RING_SIZE; + entry = mdp->dirty_rx % mdp->num_rx_ring; rxdesc = &mdp->rx_ring[entry]; /* The size of the buffer is 16 byte boundary. */ rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); @@ -1157,7 +1161,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status) skb_checksum_none_assert(skb); rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4)); } - if (entry >= RX_RING_SIZE - 1) + if (entry >= mdp->num_rx_ring - 1) rxdesc->status |= cpu_to_edmac(mdp, RD_RACT | RD_RFP | RD_RDEL); else @@ -1557,6 +1561,71 @@ static void sh_eth_get_strings(struct net_device *ndev, u32 stringset, u8 *data) } } +static void sh_eth_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + + ring->rx_max_pending = RX_RING_MAX; + ring->tx_max_pending = TX_RING_MAX; + ring->rx_pending = mdp->num_rx_ring; + ring->tx_pending = mdp->num_tx_ring; +} + +static int sh_eth_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + int ret; + + if (ring->tx_pending > TX_RING_MAX || + ring->rx_pending > RX_RING_MAX || + ring->tx_pending < TX_RING_MIN || + ring->rx_pending < RX_RING_MIN) + return -EINVAL; + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + if (netif_running(ndev)) { + netif_tx_disable(ndev); + /* Disable interrupts by clearing the interrupt mask. */ + sh_eth_write(ndev, 0x0000, EESIPR); + /* Stop the chip's Tx and Rx processes. */ + sh_eth_write(ndev, 0, EDTRR); + sh_eth_write(ndev, 0, EDRRR); + synchronize_irq(ndev->irq); + } + + /* Free all the skbuffs in the Rx queue. */ + sh_eth_ring_free(ndev); + /* Free DMA buffer */ + sh_eth_free_dma_buffer(mdp); + + /* Set new parameters */ + mdp->num_rx_ring = ring->rx_pending; + mdp->num_tx_ring = ring->tx_pending; + + ret = sh_eth_ring_init(ndev); + if (ret < 0) { + dev_err(&ndev->dev, "%s: sh_eth_ring_init failed.\n", __func__); + return ret; + } + ret = sh_eth_dev_init(ndev, false); + if (ret < 0) { + dev_err(&ndev->dev, "%s: sh_eth_dev_init failed.\n", __func__); + return ret; + } + + if (netif_running(ndev)) { + sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); + /* Setting the Rx mode will start the Rx process. */ + sh_eth_write(ndev, EDRRR_R, EDRRR); + netif_wake_queue(ndev); + } + + return 0; +} + static const struct ethtool_ops sh_eth_ethtool_ops = { .get_settings = sh_eth_get_settings, .set_settings = sh_eth_set_settings, @@ -1567,6 +1636,8 @@ static const struct ethtool_ops sh_eth_ethtool_ops = { .get_strings = sh_eth_get_strings, .get_ethtool_stats = sh_eth_get_ethtool_stats, .get_sset_count = sh_eth_get_sset_count, + .get_ringparam = sh_eth_get_ringparam, + .set_ringparam = sh_eth_set_ringparam, }; /* network device open function */ @@ -1597,7 +1668,7 @@ static int sh_eth_open(struct net_device *ndev) goto out_free_irq; /* device init */ - ret = sh_eth_dev_init(ndev); + ret = sh_eth_dev_init(ndev, true); if (ret) goto out_free_irq; @@ -1631,7 +1702,7 @@ static void sh_eth_tx_timeout(struct net_device *ndev) ndev->stats.tx_errors++; /* Free all the skbuffs in the Rx queue. */ - for (i = 0; i < RX_RING_SIZE; i++) { + for (i = 0; i < mdp->num_rx_ring; i++) { rxdesc = &mdp->rx_ring[i]; rxdesc->status = 0; rxdesc->addr = 0xBADF00D0; @@ -1639,14 +1710,14 @@ static void sh_eth_tx_timeout(struct net_device *ndev) dev_kfree_skb(mdp->rx_skbuff[i]); mdp->rx_skbuff[i] = NULL; } - for (i = 0; i < TX_RING_SIZE; i++) { + for (i = 0; i < mdp->num_tx_ring; i++) { if (mdp->tx_skbuff[i]) dev_kfree_skb(mdp->tx_skbuff[i]); mdp->tx_skbuff[i] = NULL; } /* device init */ - sh_eth_dev_init(ndev); + sh_eth_dev_init(ndev, true); } /* Packet transmit function */ @@ -1658,7 +1729,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) unsigned long flags; spin_lock_irqsave(&mdp->lock, flags); - if ((mdp->cur_tx - mdp->dirty_tx) >= (TX_RING_SIZE - 4)) { + if ((mdp->cur_tx - mdp->dirty_tx) >= (mdp->num_tx_ring - 4)) { if (!sh_eth_txfree(ndev)) { if (netif_msg_tx_queued(mdp)) dev_warn(&ndev->dev, "TxFD exhausted.\n"); @@ -1669,7 +1740,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) } spin_unlock_irqrestore(&mdp->lock, flags); - entry = mdp->cur_tx % TX_RING_SIZE; + entry = mdp->cur_tx % mdp->num_tx_ring; mdp->tx_skbuff[entry] = skb; txdesc = &mdp->tx_ring[entry]; /* soft swap. */ @@ -1683,7 +1754,7 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) else txdesc->buffer_length = skb->len; - if (entry >= TX_RING_SIZE - 1) + if (entry >= mdp->num_tx_ring - 1) txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE); else txdesc->status |= cpu_to_edmac(mdp, TD_TACT); @@ -2313,6 +2384,8 @@ static int sh_eth_drv_probe(struct platform_device *pdev) ether_setup(ndev); mdp = netdev_priv(ndev); + mdp->num_tx_ring = TX_RING_SIZE; + mdp->num_rx_ring = RX_RING_SIZE; mdp->addr = ioremap(res->start, resource_size(res)); if (mdp->addr == NULL) { ret = -ENOMEM; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 37a0702b7490..bae84fd2e73a 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -27,6 +27,10 @@ #define TX_TIMEOUT (5*HZ) #define TX_RING_SIZE 64 /* Tx ring size */ #define RX_RING_SIZE 64 /* Rx ring size */ +#define TX_RING_MIN 64 +#define RX_RING_MIN 64 +#define TX_RING_MAX 1024 +#define RX_RING_MAX 1024 #define ETHERSMALL 60 #define PKT_BUF_SZ 1538 #define SH_ETH_TSU_TIMEOUT_MS 500 @@ -701,6 +705,8 @@ struct sh_eth_private { const u16 *reg_offset; void __iomem *addr; void __iomem *tsu_addr; + u32 num_rx_ring; + u32 num_tx_ring; dma_addr_t rx_desc_dma; dma_addr_t tx_desc_dma; struct sh_eth_rxdesc *rx_ring; -- cgit v1.2.3 From 93040ae5cc8dcc893eca4a4366dc8415af278edf Mon Sep 17 00:00:00 2001 From: Somnath Kotur Date: Tue, 26 Jun 2012 22:32:10 +0000 Subject: be2net: Fix to trim skb for padded vlan packets to workaround an ASIC Bug Fixed spelling error in a comment as pointed out by DaveM. Also refactored existing code a bit to provide placeholders for another ASIC Bug workaround that will be checked-in soon after this. Signed-off-by: Somnath Kotur Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be.h | 5 +++ drivers/net/ethernet/emulex/benet/be_main.c | 56 +++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 7b5cc2b212e5..7a71fb66c6dc 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -573,6 +573,11 @@ static inline u8 is_udp_pkt(struct sk_buff *skb) return val; } +static inline bool is_ipv4_pkt(struct sk_buff *skb) +{ + return skb->protocol == ntohs(ETH_P_IP) && ip_hdr(skb)->version == 4; +} + static inline void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) { u32 addr; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index a28896d4411d..edce7af97c87 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -577,6 +577,11 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, return vlan_tag; } +static int be_vlan_tag_chk(struct be_adapter *adapter, struct sk_buff *skb) +{ + return vlan_tx_tag_present(skb) || adapter->pvid; +} + static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, struct sk_buff *skb, u32 wrb_cnt, u32 len) { @@ -704,33 +709,56 @@ dma_err: return 0; } +static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, + struct sk_buff *skb) +{ + u16 vlan_tag = 0; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb)) + return skb; + + if (vlan_tx_tag_present(skb)) { + vlan_tag = be_get_tx_vlan_tag(adapter, skb); + __vlan_put_tag(skb, vlan_tag); + skb->vlan_tci = 0; + } + + return skb; +} + static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; struct be_queue_info *txq = &txo->q; + struct iphdr *ip = NULL; u32 wrb_cnt = 0, copied = 0; - u32 start = txq->head; + u32 start = txq->head, eth_hdr_len; bool dummy_wrb, stopped = false; - /* For vlan tagged pkts, BE - * 1) calculates checksum even when CSO is not requested - * 2) calculates checksum wrongly for padded pkt less than - * 60 bytes long. - * As a workaround disable TX vlan offloading in such cases. + eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? + VLAN_ETH_HLEN : ETH_HLEN; + + /* HW has a bug which considers padding bytes as legal + * and modifies the IPv4 hdr's 'tot_len' field */ - if (vlan_tx_tag_present(skb) && - (skb->ip_summed != CHECKSUM_PARTIAL || skb->len <= 60)) { - skb = skb_share_check(skb, GFP_ATOMIC); - if (unlikely(!skb)) - goto tx_drop; + if (skb->len <= 60 && be_vlan_tag_chk(adapter, skb) && + is_ipv4_pkt(skb)) { + ip = (struct iphdr *)ip_hdr(skb); + pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); + } - skb = __vlan_put_tag(skb, be_get_tx_vlan_tag(adapter, skb)); + /* HW has a bug wherein it will calculate CSUM for VLAN + * pkts even though it is disabled. + * Manually insert VLAN in pkt. + */ + if (skb->ip_summed != CHECKSUM_PARTIAL && + be_vlan_tag_chk(adapter, skb)) { + skb = be_insert_vlan_in_pkt(adapter, skb); if (unlikely(!skb)) goto tx_drop; - - skb->vlan_tci = 0; } wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); -- cgit v1.2.3 From bcf6c3d46fa8624397cc52d9f13f9a71bf3a572f Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 25 May 2012 17:25:36 +0800 Subject: clk: mxs: rename the gpmi clock for imx28 rename the clock name from `8000c000.gpmi` to `8000c000.gpmi-nand`. Signed-off-by: Huang Shijie Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx28.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 2826a2606a29..003b0f31c1ba 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -174,7 +174,7 @@ static struct clk_lookup lcdif_lookups[] __initdata = { static struct clk_lookup gpmi_lookups[] __initdata = { { .dev_id = "imx28-gpmi-nand", }, - { .dev_id = "8000c000.gpmi", }, + { .dev_id = "8000c000.gpmi-nand", }, }; static struct clk_lookup fec_lookups[] __initdata = { -- cgit v1.2.3 From 8c6facab8e99f7f90eaa1edbaf4baf217ac5ea5f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 19 Jun 2012 17:27:32 -0300 Subject: clk: mxs: Fix the GPMI clock name The correct name for the GPMI clock is 'gpmi-nand'. Signed-off-by: Fabio Estevam Acked-by: Marek Vasut Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx23.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index f7be225f544c..40ad638dbe6c 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -106,7 +106,7 @@ static struct clk_lookup lcdif_lookups[] __initdata = { static struct clk_lookup gpmi_lookups[] __initdata = { { .dev_id = "imx23-gpmi-nand", }, - { .dev_id = "8000c000.gpmi", }, + { .dev_id = "8000c000.gpmi-nand", }, }; static const char *sel_pll[] __initconst = { "pll", "ref_xtal", }; -- cgit v1.2.3 From 275d58025f8d59c493384e1e9660c9c5b7b28d2d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 Jun 2012 20:56:03 +0800 Subject: clk: mxs: add clkdev lookup for pwm Add pwm clock lookup for imx23 and imx28 booting from device tree. Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx23.c | 1 + drivers/clk/mxs/clk-imx28.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 40ad638dbe6c..c14b021c4a68 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -189,6 +189,7 @@ int __init mx23_clocks_init(void) } clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_register_clkdev(clks[pwm], NULL, "80064000.pwm"); clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups)); clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups)); clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups)); diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 003b0f31c1ba..365053f052cd 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -314,6 +314,7 @@ int __init mx28_clocks_init(void) clk_register_clkdev(clks[clk32k], NULL, "timrot"); clk_register_clkdev(clks[enet_out], NULL, "enet_out"); + clk_register_clkdev(clks[pwm], NULL, "80064000.pwm"); clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups)); clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups)); clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups)); -- cgit v1.2.3 From 597c854974db20eebe87e91df0d12daaefabf525 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 22 Jun 2012 20:41:10 +0530 Subject: dma: tegra: use sg_dma_address() for getting dma buffer address Use the sg_dma_address() to get the segment buffer address for DMA transfer in place of sg_phys() which returns the physical address of an sg entry. The sg_dma_address() returns the correct buffer memory address for DMA transfer. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 134ea7cfc795..6d7d5d383cb1 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -929,7 +929,7 @@ static struct dma_async_tx_descriptor *tegra_dma_prep_slave_sg( for_each_sg(sgl, sg, sg_len, i) { u32 len, mem; - mem = sg_phys(sg); + mem = sg_dma_address(sg); len = sg_dma_len(sg); if ((len & 3) || (mem & 3) || -- cgit v1.2.3 From 56482ec0b156811eb463576a89a47f89e21f3587 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Mon, 25 Jun 2012 12:01:31 +0530 Subject: dma: tegra: add clk_prepare/clk_unprepare Use clk_prepare/clk_unprepare as required by the generic clk framework. Signed-off-by: Prashant Gaikwad Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 6d7d5d383cb1..78af256cca9e 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1366,7 +1366,7 @@ static int tegra_dma_runtime_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct tegra_dma *tdma = platform_get_drvdata(pdev); - clk_disable(tdma->dma_clk); + clk_disable_unprepare(tdma->dma_clk); return 0; } @@ -1376,7 +1376,7 @@ static int tegra_dma_runtime_resume(struct device *dev) struct tegra_dma *tdma = platform_get_drvdata(pdev); int ret; - ret = clk_enable(tdma->dma_clk); + ret = clk_prepare_enable(tdma->dma_clk); if (ret < 0) { dev_err(dev, "clk_enable failed: %d\n", ret); return ret; -- cgit v1.2.3 From 6660f7af52caf6028b36e6a68a21c461f2fb0440 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 22 Jun 2012 17:12:44 +0530 Subject: dma: tegra: do not set transfer desc flag to DMA_CTRL_ACK in cyclic mode The sound dmaengine pcm driver uses the dma in cyclic mode and it does not ack the transfer descriptor after transfer stops. This may lead to hold that desc in chip's dma driver and does not allow to reuse/free that descriptors. Hence not enabling flag DMA_CTRL_ACK when dma runs in cyclic mode. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index 78af256cca9e..ba4d73c525e6 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1093,7 +1093,7 @@ struct dma_async_tx_descriptor *tegra_dma_prep_dma_cyclic( mem += len; } sg_req->last_sg = true; - dma_desc->txd.flags = DMA_CTRL_ACK; + dma_desc->txd.flags = 0; /* * Make sure that mode should not be conflicting with currently -- cgit v1.2.3 From 46fb3f8ef5bde1325b1e58867a3a98dd746511d7 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 22 Jun 2012 17:12:43 +0530 Subject: dma: tegra: set DMA_CYCLIC capability Tegra's APB DMA driver support the cyclic mode of data transfer and hence setting the DMA_CYCLIC caps for dma channels. This is require when generic sound dmaengine pcm driver request for dma channel with CYCLIC capability. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Vinod Koul --- drivers/dma/tegra20-apb-dma.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index ba4d73c525e6..c0836a7a8631 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -1306,6 +1306,8 @@ static int __devinit tegra_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); + dma_cap_set(DMA_CYCLIC, tdma->dma_dev.cap_mask); + tdma->dma_dev.dev = &pdev->dev; tdma->dma_dev.device_alloc_chan_resources = tegra_dma_alloc_chan_resources; -- cgit v1.2.3 From bf2516cd020dbf80ed2f1750ac1f4b0da3549c91 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 27 Jun 2012 10:40:28 -0300 Subject: regulator: core: Remove unused get_device_regulator commit 222cc7b1 (regulator: core: Allow multiple requests of a single supply mapping) removed the usage of get_device_regulator(). Remove the function definition too amd get rid of the following warning: drivers/regulator/core.c:112:26: warning: 'get_device_regulator' defined but not used [-Wunused-function] Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/regulator/core.c | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 00c787c01f3b..d886ee265e47 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -108,28 +108,6 @@ static const char *rdev_get_name(struct regulator_dev *rdev) return ""; } -/* gets the regulator for a given consumer device */ -static struct regulator *get_device_regulator(struct device *dev) -{ - struct regulator *regulator = NULL; - struct regulator_dev *rdev; - - mutex_lock(®ulator_list_mutex); - list_for_each_entry(rdev, ®ulator_list, list) { - mutex_lock(&rdev->mutex); - list_for_each_entry(regulator, &rdev->consumer_list, list) { - if (regulator->dev == dev) { - mutex_unlock(&rdev->mutex); - mutex_unlock(®ulator_list_mutex); - return regulator; - } - } - mutex_unlock(&rdev->mutex); - } - mutex_unlock(®ulator_list_mutex); - return NULL; -} - /** * of_get_regulator - get a regulator device node based on supply name * @dev: Device pointer for the consumer (of regulator) device -- cgit v1.2.3 From 954bd6d1c9f6a545a594517b1a4aa9262b613f14 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 27 Jun 2012 12:02:07 +0100 Subject: lm8333: Fix check ordering Fix harmless reference off end of array Reported-by: Resolves-bug: https://bugzilla.kernel.org/show_bug.cgi?43861 Signed-off-by: Alan Cox Signed-off-by: Jiri Kosina --- drivers/input/keyboard/lm8333.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index ca168a6679de..081fd9effa8c 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -91,7 +91,7 @@ static void lm8333_key_handler(struct lm8333 *lm8333) return; } - for (i = 0; keys[i] && i < LM8333_FIFO_TRANSFER_SIZE; i++) { + for (i = 0; i < LM8333_FIFO_TRANSFER_SIZE && keys[i]; i++) { pressed = keys[i] & 0x80; code = keys[i] & 0x7f; -- cgit v1.2.3 From dfb89c56add259b72a9c68d6b2846c1cd8c4e4b6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Jun 2012 09:23:48 +0200 Subject: cfg80211: don't allow WoWLAN support without CONFIG_PM When CONFIG_PM is disabled, no device can possibly support WoWLAN since it can't go to sleep to start with. Due to this, mac80211 had even rejected the hardware registration. By making all the code and data for WoWLAN depend on CONFIG_PM we can promote this runtime error to a compile-time error. Add #ifdef around all WoWLAN code to remove it in systems that don't need it as they never suspend. Cc: Kalle Valo Acked-by: Luciano Coelho Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 2 ++ drivers/net/wireless/ti/wlcore/main.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index f27e9732951d..b8fce0d4d72e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3487,6 +3487,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->cipher_suites = cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); +#ifdef CONFIG_PM wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_GTK_REKEY_FAILURE | @@ -3496,6 +3497,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->wowlan.n_patterns = WOW_MAX_FILTERS_PER_LIST; wiphy->wowlan.pattern_min_len = 1; wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; +#endif wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1156e3f578c1..747a997bc608 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5321,6 +5321,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) goto out_free_hw; } +#ifdef CONFIG_PM ret = enable_irq_wake(wl->irq); if (!ret) { wl->irq_wake_enabled = true; @@ -5334,6 +5335,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev) WL1271_RX_FILTER_MAX_PATTERN_SIZE; } } +#endif disable_irq(wl->irq); ret = wl12xx_get_hw_info(wl); -- cgit v1.2.3 From 75cc52358799bd6001e7d1a47847f997f5ae99f0 Mon Sep 17 00:00:00 2001 From: Deepthi Dharwar Date: Mon, 25 Jun 2012 23:59:54 +0200 Subject: PM / ACPI: Fix suspend/resume regression caused by cpuidle cleanup. Commit e978aa7d7d57d04eb5f88a7507c4fb98577def77 ( cpuidle: Move dev->last_residency update to driver enter routine; remove dev->last_state) was breaking suspend on laptops, as reported in the below link - https://lkml.org/lkml/2011/11/11/164 This was fixed in commit 3439a8da16bcad6b0982ece938c9f8299bb53584 (ACPI / cpuidle: Remove acpi_idle_suspend (to fix suspend regression) by removing acpi_idle_suspend flag. - https://lkml.org/lkml/2011/11/14/74 But this did fix did not work on all systems as Suspend/resume regression was reported on Lenovo S10-3 recently by Dave. - https://lkml.org/lkml/2012/5/27/115 It looked like with commit e978aa7d broke suspend and with commit 3439a8da resume was not working with acpi_idle driver. This patch fixes the regression that caused this issue in the first place. acpi_idle_suspend flag is essential on some x86 systems to prevent the cpus from going to deeper C-states when suspend is triggered ( commit b04e7bdb984 ) So reverting the commit 3439a8da is essential. By default, irqs are disabled in cpu_idle arch specific call and re-enabled in idle state return path . During suspend, the acpi_idle_suspend flag is set, which prevents the cpus from going to deeper idle states, it is essential to enabling the irqs in this return path too. To address the suspend issue, we were not re-enabling the interrupts while returning from acpi_idle_enter_bm() routine if acpi_idle_suspend flag is set. and this caused suspend failure. In addition to the above, to improve the readability of the code, return of -ENIVAL is replaced with -EBUSY in acpi_idle_suspend return path. Implying that the system is currently busy when suspend is in progress, which prevents the cpus from entering deeper C-states. Reported-and-Tested-by: Dav Hansen Tested-by: Preeti Murthy Signed-off-by: Deepthi Dharwar Reviewed-by: Srivatsa S Bhat Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_idle.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index f3decb30223f..47a8caa89dbe 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -224,6 +224,7 @@ static void lapic_timer_state_broadcast(struct acpi_processor *pr, /* * Suspend / resume control */ +static int acpi_idle_suspend; static u32 saved_bm_rld; static void acpi_idle_bm_rld_save(void) @@ -242,13 +243,21 @@ static void acpi_idle_bm_rld_restore(void) int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) { + if (acpi_idle_suspend == 1) + return 0; + acpi_idle_bm_rld_save(); + acpi_idle_suspend = 1; return 0; } int acpi_processor_resume(struct acpi_device * device) { + if (acpi_idle_suspend == 0) + return 0; + acpi_idle_bm_rld_restore(); + acpi_idle_suspend = 0; return 0; } @@ -754,6 +763,12 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, local_irq_disable(); + if (acpi_idle_suspend) { + local_irq_enable(); + cpu_relax(); + return -EBUSY; + } + lapic_timer_state_broadcast(pr, cx, 1); kt1 = ktime_get_real(); acpi_idle_do_entry(cx); @@ -823,6 +838,12 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, local_irq_disable(); + if (acpi_idle_suspend) { + local_irq_enable(); + cpu_relax(); + return -EBUSY; + } + if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; /* @@ -907,14 +928,21 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, drv, drv->safe_state_index); } else { local_irq_disable(); - acpi_safe_halt(); + if (!acpi_idle_suspend) + acpi_safe_halt(); local_irq_enable(); - return -EINVAL; + return -EBUSY; } } local_irq_disable(); + if (acpi_idle_suspend) { + local_irq_enable(); + cpu_relax(); + return -EBUSY; + } + if (cx->entry_method != ACPI_CSTATE_FFH) { current_thread_info()->status &= ~TS_POLLING; /* -- cgit v1.2.3 From f63d7dabd5da9ef41f28f6d69b29bc084db0ca5a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 25 Jun 2012 18:01:12 -0500 Subject: rtlwifi: rtl8192cu: New USB IDs The latest Realtek driver for the RTL8188CU and RTL8192CU chips adds three new USB IDs. Reported-by: Xose Vazquez Perez Signed-off-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/rtlwifi/rtl8192cu/sw.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index d228358e6a40..9970c2b1b199 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -301,9 +301,11 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/ {RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/ {RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/ + {RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/ {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/ + {RTL_USB_DEVICE(0x4856, 0x0091, rtl92cu_hal_cfg)}, /*NetweeN - Feixun*/ /* HP - Lite-On ,8188CUS Slim Combo */ {RTL_USB_DEVICE(0x103c, 0x1629, rtl92cu_hal_cfg)}, {RTL_USB_DEVICE(0x13d3, 0x3357, rtl92cu_hal_cfg)}, /* AzureWave */ @@ -346,6 +348,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Funai -Abocom*/ {RTL_USB_DEVICE(0x0846, 0x9021, rtl92cu_hal_cfg)}, /*Netgear-Sercomm*/ {RTL_USB_DEVICE(0x0b05, 0x17ab, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/ + {RTL_USB_DEVICE(0x0bda, 0x8186, rtl92cu_hal_cfg)}, /*Realtek 92CE-VAU*/ {RTL_USB_DEVICE(0x0df6, 0x0061, rtl92cu_hal_cfg)}, /*Sitecom-Edimax*/ {RTL_USB_DEVICE(0x0e66, 0x0019, rtl92cu_hal_cfg)}, /*Hawking-Edimax*/ {RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/ -- cgit v1.2.3 From 7508b657967cf664b5aa0f6367d05016e7e3bc2a Mon Sep 17 00:00:00 2001 From: Panayiotis Karabassis Date: Tue, 26 Jun 2012 23:37:17 +0300 Subject: ath9k: enable serialize_regmode for non-PCIE AR9287 https://bugzilla.kernel.org/show_bug.cgi?id=42903 Based on the work of Signed-off-by: Panayiotis Karabassis Cc: stable@vger.kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/hw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1c68e564f503..995ca8e1302e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -622,7 +622,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) { if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || - ((AR_SREV_9160(ah) || AR_SREV_9280(ah)) && + ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) && !ah->is_pciexpress)) { ah->config.serialize_regmode = SER_REG_MODE_ON; -- cgit v1.2.3 From 6bb51c70cabaadddc54a6454844eceba91a56083 Mon Sep 17 00:00:00 2001 From: Tom Hughes Date: Wed, 27 Jun 2012 18:21:15 +0100 Subject: ath9k: fix panic caused by returning a descriptor we have queued for reuse Commit 3a2923e83c introduced a bug when a corrupt descriptor is encountered - although the following descriptor is discarded and returned to the queue for reuse the associated frame is also returned for processing. This leads to a panic: BUG: unable to handle kernel NULL pointer dereference at 000000000000003a IP: [] ath_rx_tasklet+0x165/0x1b00 [ath9k] Call Trace: [] ? map_single+0x60/0x60 [] ? ath9k_ioread32+0x34/0x90 [ath9k] [] athk9k_tasklet+0xdc/0x160 [ath9k] [] tasklet_action+0x63/0xd0 [] __do_softirq+0xc0/0x1e0 [] ? native_sched_clock+0x13/0x80 [] call_softirq+0x1c/0x30 [] do_softirq+0x75/0xb0 [] irq_exit+0xb5/0xc0 [] do_IRQ+0x63/0xe0 [] common_interrupt+0x6a/0x6a [] ? intel_idle+0xea/0x150 [] ? intel_idle+0xcb/0x150 [] cpuidle_enter+0x19/0x20 [] cpuidle_idle_call+0xa9/0x240 [] cpu_idle+0xaf/0x120 [] rest_init+0x72/0x74 [] start_kernel+0x3b7/0x3c4 [] ? repair_env_string+0x5e/0x5e [] x86_64_start_reservations+0x131/0x135 [] x86_64_start_kernel+0x100/0x10f Making sure bf is cleared to NULL in this case restores the old behaviour. Signed-off-by: Tom Hughes Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/recv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 599667ababee..0735aeb3b26c 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -695,9 +695,9 @@ static bool ath_edma_get_buffers(struct ath_softc *sc, __skb_unlink(skb, &rx_edma->rx_fifo); list_add_tail(&bf->list, &sc->rx.rxbuf); ath_rx_edma_buf_link(sc, qtype); - } else { - bf = NULL; } + + bf = NULL; } *dest = bf; -- cgit v1.2.3 From d2bb2b9e9161d221badafae063d0c62b41bf3078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Mon, 25 Jun 2012 22:12:20 +0200 Subject: bcma: define some additional cores IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of them are BCM4706 specific AFAWK. Most of them was confirmed on Netgear WNDR450. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/scan.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 5ed0718fc660..a3420585d942 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -28,6 +28,12 @@ static const struct bcma_device_id_name bcma_arm_device_names[] = { static const struct bcma_device_id_name bcma_bcm_device_names[] = { { BCMA_CORE_OOB_ROUTER, "OOB Router" }, + { BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" }, + { BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" }, + { BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" }, + { BCMA_CORE_AMEMC, "AMEMC (DDR)" }, + { BCMA_CORE_ALTA, "ALTA (I2S)" }, + { BCMA_CORE_4706_MAC_GBIT_COMMON, "BCM4706 GBit MAC Common" }, { BCMA_CORE_INVALID, "Invalid" }, { BCMA_CORE_CHIPCOMMON, "ChipCommon" }, { BCMA_CORE_ILINE20, "ILine 20" }, -- cgit v1.2.3 From 135e4c6184d29f0834078bf150009a77b9dae717 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 26 Jun 2012 21:26:34 +0200 Subject: brcmfmac: add support for bus specific data command brcmfmac need to support data command setting for dongle's bus core. A list must be placed at brcmf_bus structure before calling brcmf_bus_start in order to be sent by brcmf_c_preinit_dcmds. Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 8 ++++++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 16 ++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 ++ 3 files changed, 26 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 366916494be4..537f499cc5d2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -36,6 +36,13 @@ struct dngl_stats { unsigned long multicast; /* multicast packets received */ }; +struct brcmf_bus_dcmd { + char *name; + char *param; + int param_len; + struct list_head list; +}; + /* interface structure between common and bus layer */ struct brcmf_bus { u8 type; /* bus type */ @@ -50,6 +57,7 @@ struct brcmf_bus { unsigned long tx_realloc; /* Tx packets realloced for headroom */ struct dngl_stats dstats; /* Stats for dongle-based data */ u8 align; /* bus alignment requirement */ + struct list_head dcmd_list; /* interface functions pointers */ /* Stop bus module: clear pending frames, disable data flow */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 236cb9fa460c..1c9eb1893d29 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -807,6 +807,8 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) int scan_assoc_time = 40; int scan_unassoc_time = 40; int i; + struct brcmf_bus_dcmd *cmdlst; + struct list_head *cur, *q; mutex_lock(&drvr->proto_block); @@ -874,6 +876,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) 0, true); } + /* set bus specific command if there is any */ + list_for_each_safe(cur, q, &drvr->bus_if->dcmd_list) { + cmdlst = list_entry(cur, struct brcmf_bus_dcmd, list); + if (cmdlst->name && cmdlst->param && cmdlst->param_len) { + brcmf_c_mkiovar(cmdlst->name, cmdlst->param, + cmdlst->param_len, iovbuf, + sizeof(iovbuf)); + brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, + iovbuf, sizeof(iovbuf)); + } + list_del(cur); + kfree(cmdlst); + } + mutex_unlock(&drvr->proto_block); return 0; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 01cf6c03390b..57bf1d7ee80f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -1020,6 +1020,8 @@ int brcmf_attach(uint bus_hdrlen, struct device *dev) INIT_WORK(&drvr->setmacaddr_work, _brcmf_set_mac_address); INIT_WORK(&drvr->multicast_work, _brcmf_set_multicast_list); + INIT_LIST_HEAD(&drvr->bus_if->dcmd_list); + return ret; fail: -- cgit v1.2.3 From bbfd6a66ff69e3e1850bead3e30031e303581cb1 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 26 Jun 2012 21:26:35 +0200 Subject: brcmfmac: restrict dongle txglom disable to old SDIO core txglomming is a firmware feature for sdio bus interface. For SDIO device cores newer than revision 11, the default setting of firmware should be used instead of disabling it from the host side. Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 6 ------ drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 1c9eb1893d29..40fa4726b4a8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -801,7 +801,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) "event_msgs" + '\0' + bitvec */ char buf[128], *ptr; u32 dongle_align = drvr->bus_if->align; - u32 glom = 0; u32 roaming = 1; uint bcn_timeout = 3; int scan_assoc_time = 40; @@ -835,11 +834,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf)); - /* disable glom option per default */ - brcmf_c_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, - sizeof(iovbuf)); - /* Setup timeout if Beacons are lost and roam is off to report link down */ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 076b7720ded9..1616c14d5f0c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4169,6 +4169,9 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; + struct brcmf_bus_dcmd *dlst; + u32 dngl_txglom; + u8 idx; brcmf_dbg(TRACE, "Enter\n"); @@ -4254,6 +4257,18 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); + /* sdio bus core specific dcmd */ + idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + dlst = kzalloc(sizeof(struct brcmf_bus_dcmd), GFP_KERNEL); + if (bus->ci->c_inf[idx].rev < 12 && dlst) { + /* for sdio core rev < 12, disable txgloming */ + dngl_txglom = 0; + dlst->name = "bus:txglom"; + dlst->param = (char *)&dngl_txglom; + dlst->param_len = sizeof(u32); + list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list); + } + /* if firmware path present try to download and bring up bus */ ret = brcmf_bus_start(bus->sdiodev->dev); if (ret != 0) { -- cgit v1.2.3 From c3d2bc35e53dc0dcb85f4f4f06486ecd99143615 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 26 Jun 2012 21:26:36 +0200 Subject: brcmfmac: move glom alignment setting to SDIO bus layer txglomming alignment is a SDIO bus specific feature. It is more appropriate to place it in SDIO bus layer instead of common layer. Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 7 ------- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 21 +++++++++++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 40fa4726b4a8..2621dd3d7dcd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -800,7 +800,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ char buf[128], *ptr; - u32 dongle_align = drvr->bus_if->align; u32 roaming = 1; uint bcn_timeout = 3; int scan_assoc_time = 40; @@ -828,12 +827,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr) /* Print fw version info */ brcmf_dbg(ERROR, "Firmware version = %s\n", buf); - /* Match Host and Dongle rx alignment */ - brcmf_c_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, - sizeof(iovbuf)); - brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf, - sizeof(iovbuf)); - /* Setup timeout if Beacons are lost and roam is off to report link down */ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1616c14d5f0c..581e7d999737 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -4171,6 +4171,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) struct brcmf_sdio *bus; struct brcmf_bus_dcmd *dlst; u32 dngl_txglom; + u32 dngl_txglomalign; u8 idx; brcmf_dbg(TRACE, "Enter\n"); @@ -4260,12 +4261,20 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) /* sdio bus core specific dcmd */ idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); dlst = kzalloc(sizeof(struct brcmf_bus_dcmd), GFP_KERNEL); - if (bus->ci->c_inf[idx].rev < 12 && dlst) { - /* for sdio core rev < 12, disable txgloming */ - dngl_txglom = 0; - dlst->name = "bus:txglom"; - dlst->param = (char *)&dngl_txglom; - dlst->param_len = sizeof(u32); + if (dlst) { + if (bus->ci->c_inf[idx].rev < 12) { + /* for sdio core rev < 12, disable txgloming */ + dngl_txglom = 0; + dlst->name = "bus:txglom"; + dlst->param = (char *)&dngl_txglom; + dlst->param_len = sizeof(u32); + } else { + /* otherwise, set txglomalign */ + dngl_txglomalign = bus->sdiodev->bus_if->align; + dlst->name = "bus:txglomalign"; + dlst->param = (char *)&dngl_txglomalign; + dlst->param_len = sizeof(u32); + } list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list); } -- cgit v1.2.3 From d610cde30b009ac3dbcd1ae5d7d55fa4c7d066fa Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 26 Jun 2012 21:26:37 +0200 Subject: brcmfmac: use firmware data buffer directly for nvram The nvram file could be parsed directly in the data buffer in the firmware structure passed by request_firmware function. This patch gets rid of the redundant memcpy. Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 97 +++++++--------------- 1 file changed, 30 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 581e7d999737..a50796c9bd89 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3336,39 +3336,6 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) return rxlen ? (int)rxlen : -ETIMEDOUT; } -static int brcmf_sdbrcm_downloadvars(struct brcmf_sdio *bus, void *arg, int len) -{ - int bcmerror = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Basic sanity checks */ - if (bus->sdiodev->bus_if->drvr_up) { - bcmerror = -EISCONN; - goto err; - } - if (!len) { - bcmerror = -EOVERFLOW; - goto err; - } - - /* Free the old ones and replace with passed variables */ - kfree(bus->vars); - - bus->vars = kmalloc(len, GFP_ATOMIC); - bus->varsz = bus->vars ? len : 0; - if (bus->vars == NULL) { - bcmerror = -ENOMEM; - goto err; - } - - /* Copy the passed variables, which should include the - terminating double-null */ - memcpy(bus->vars, arg, bus->varsz); -err: - return bcmerror; -} - static int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus) { int bcmerror = 0; @@ -3573,13 +3540,21 @@ err: * by two NULs. */ -static uint brcmf_process_nvram_vars(char *varbuf, uint len) +static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) { + char *varbuf; char *dp; bool findNewline; int column; - uint buf_len, n; + int ret = 0; + uint buf_len, n, len; + + len = bus->firmware->size; + varbuf = vmalloc(len); + if (!varbuf) + return -ENOMEM; + memcpy(varbuf, bus->firmware->data, len); dp = varbuf; findNewline = false; @@ -3608,56 +3583,44 @@ static uint brcmf_process_nvram_vars(char *varbuf, uint len) column++; } buf_len = dp - varbuf; - while (dp < varbuf + n) *dp++ = 0; - return buf_len; + kfree(bus->vars); + + bus->varsz = buf_len + 1; + bus->vars = kmalloc(bus->varsz, GFP_KERNEL); + if (bus->vars == NULL) { + bus->varsz = 0; + ret = -ENOMEM; + goto err; + } + + /* copy the processed variables and add null termination */ + memcpy(bus->vars, varbuf, buf_len); + bus->vars[buf_len] = 0; +err: + vfree(varbuf); + return ret; } static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) { - uint len; - char *memblock = NULL; - char *bufp; int ret; + if (bus->sdiodev->bus_if->drvr_up) + return -EISCONN; + ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME, &bus->sdiodev->func[2]->dev); if (ret) { brcmf_dbg(ERROR, "Fail to request nvram %d\n", ret); return ret; } - bus->fw_ptr = 0; - - memblock = kmalloc(MEMBLOCK, GFP_ATOMIC); - if (memblock == NULL) { - ret = -ENOMEM; - goto err; - } - - len = brcmf_sdbrcm_get_image(memblock, MEMBLOCK, bus); - - if (len > 0 && len < MEMBLOCK) { - bufp = (char *)memblock; - bufp[len] = 0; - len = brcmf_process_nvram_vars(bufp, len); - bufp += len; - *bufp++ = 0; - if (len) - ret = brcmf_sdbrcm_downloadvars(bus, memblock, len + 1); - if (ret) - brcmf_dbg(ERROR, "error downloading vars: %d\n", ret); - } else { - brcmf_dbg(ERROR, "error reading nvram file: %d\n", len); - ret = -EIO; - } -err: - kfree(memblock); + ret = brcmf_process_nvram_vars(bus); release_firmware(bus->firmware); - bus->fw_ptr = 0; return ret; } -- cgit v1.2.3 From 6d4ef68086f54713b6c9dda478b8e3f749acb3ba Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 26 Jun 2012 21:26:38 +0200 Subject: brcmfmac: reduce allocations needed during nvram data download The nvram data is preprocessed before being sent to the device and just before sending an additional allocation was done that assured word alignment of the data. This has moved to the preprocessing step to reduce allocations and subsequent copying of the nvram data. Reviewed-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 48 ++++++++-------------- 1 file changed, 17 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index a50796c9bd89..2ee19c1a0d51 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3339,9 +3339,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) static int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus) { int bcmerror = 0; - u32 varsize; u32 varaddr; - u8 *vbuffer; u32 varsizew; __le32 varsizew_le; #ifdef DEBUG @@ -3350,56 +3348,44 @@ static int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus) /* Even if there are no vars are to be written, we still need to set the ramsize. */ - varsize = bus->varsz ? roundup(bus->varsz, 4) : 0; - varaddr = (bus->ramsize - 4) - varsize; + varaddr = (bus->ramsize - 4) - bus->varsz; if (bus->vars) { - vbuffer = kzalloc(varsize, GFP_ATOMIC); - if (!vbuffer) - return -ENOMEM; - - memcpy(vbuffer, bus->vars, bus->varsz); - /* Write the vars list */ - bcmerror = - brcmf_sdbrcm_membytes(bus, true, varaddr, vbuffer, varsize); + bcmerror = brcmf_sdbrcm_membytes(bus, true, varaddr, + bus->vars, bus->varsz); #ifdef DEBUG /* Verify NVRAM bytes */ - brcmf_dbg(INFO, "Compare NVRAM dl & ul; varsize=%d\n", varsize); - nvram_ularray = kmalloc(varsize, GFP_ATOMIC); - if (!nvram_ularray) { - kfree(vbuffer); + brcmf_dbg(INFO, "Compare NVRAM dl & ul; varsize=%d\n", + bus->varsz); + nvram_ularray = kmalloc(bus->varsz, GFP_ATOMIC); + if (!nvram_ularray) return -ENOMEM; - } /* Upload image to verify downloaded contents. */ - memset(nvram_ularray, 0xaa, varsize); + memset(nvram_ularray, 0xaa, bus->varsz); /* Read the vars list to temp buffer for comparison */ - bcmerror = - brcmf_sdbrcm_membytes(bus, false, varaddr, nvram_ularray, - varsize); + bcmerror = brcmf_sdbrcm_membytes(bus, false, varaddr, + nvram_ularray, bus->varsz); if (bcmerror) { brcmf_dbg(ERROR, "error %d on reading %d nvram bytes at 0x%08x\n", - bcmerror, varsize, varaddr); + bcmerror, bus->varsz, varaddr); } /* Compare the org NVRAM with the one read from RAM */ - if (memcmp(vbuffer, nvram_ularray, varsize)) + if (memcmp(bus->vars, nvram_ularray, bus->varsz)) brcmf_dbg(ERROR, "Downloaded NVRAM image is corrupted\n"); else brcmf_dbg(ERROR, "Download/Upload/Compare of NVRAM ok\n"); kfree(nvram_ularray); #endif /* DEBUG */ - - kfree(vbuffer); } /* adjust to the user specified RAM */ brcmf_dbg(INFO, "Physical memory size: %d\n", bus->ramsize); brcmf_dbg(INFO, "Vars are at %d, orig varsize is %d\n", - varaddr, varsize); - varsize = ((bus->ramsize - 4) - varaddr); + varaddr, bus->varsz); /* * Determine the length token: @@ -3410,13 +3396,13 @@ static int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus) varsizew = 0; varsizew_le = cpu_to_le32(0); } else { - varsizew = varsize / 4; + varsizew = bus->varsz / 4; varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF); varsizew_le = cpu_to_le32(varsizew); } brcmf_dbg(INFO, "New varsize is %d, length token=0x%08x\n", - varsize, varsizew); + bus->varsz, varsizew); /* Write the length token to the last word */ bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->ramsize - 4), @@ -3587,8 +3573,8 @@ static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) *dp++ = 0; kfree(bus->vars); - - bus->varsz = buf_len + 1; + /* roundup needed for download to device */ + bus->varsz = roundup(buf_len + 1, 4); bus->vars = kmalloc(bus->varsz, GFP_KERNEL); if (bus->vars == NULL) { bus->varsz = 0; -- cgit v1.2.3 From 85a4a1c3bc42e7ab7027f94984be04e41a7cc496 Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Tue, 26 Jun 2012 21:26:39 +0200 Subject: brcmfmac: add BCM4334 support BCM4334 is a dualband a/b/g/n WiFi chip support 20MHz/40MHz channels. This patch adds support for its SDIO interface. Reviewed-by: Arend van Spriel Signed-off-by: Franky Lin Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c | 2 ++ drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 ++ drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c | 17 +++++++++++++++++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 1 + 4 files changed, 22 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 82f51dbd0d66..49765d34b4e0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -44,6 +44,7 @@ #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 +#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 @@ -52,6 +53,7 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 2ee19c1a0d51..5c5f1cd8ff22 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3885,6 +3885,8 @@ static bool brcmf_sdbrcm_chipmatch(u16 chipid) return true; if (chipid == BCM4330_CHIP_ID) return true; + if (chipid == BCM4334_CHIP_ID) + return true; return false; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index f8e1f1c84d08..58155e23d220 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -403,6 +403,23 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->c_inf[3].cib = 0x03004211; ci->ramsize = 0x48000; break; + case BCM4334_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x29004211; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18002000; + ci->c_inf[1].wrapbase = 0x18102000; + ci->c_inf[1].cib = 0x0d004211; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = 0x18004000; + ci->c_inf[2].wrapbase = 0x18104000; + ci->c_inf[2].cib = 0x13080401; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = 0x18003000; + ci->c_inf[3].wrapbase = 0x18103000; + ci->c_inf[3].cib = 0x07004211; + ci->ramsize = 0x80000; + break; default: brcmf_dbg(ERROR, "chipid 0x%x is not supported\n", ci->chip); return -ENODEV; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 333193f20e1c..bcc79b4e3267 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -37,5 +37,6 @@ #define BCM4329_CHIP_ID 0x4329 #define BCM4330_CHIP_ID 0x4330 #define BCM4331_CHIP_ID 0x4331 +#define BCM4334_CHIP_ID 0x4334 #endif /* _BRCM_HW_IDS_H_ */ -- cgit v1.2.3 From 9bd02c6b9e6219829929111c46ef007c7a76865f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 26 Jun 2012 21:26:40 +0200 Subject: brcmfmac: fix sparse warning introduced with checkdied patch The commit "brcmfmac: introduce checkdied debugfs functionality" also introduced a sparse warning: ..../brcmfmac/dhd_sdio.c:3147:45: sparse: cast to restricted __le32 This patch fixes this sparse warning. Reported-by: Fengguang Wu Reviewed-by: Franky (Zhenhui) Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 5c5f1cd8ff22..5c868bac1c1c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3145,7 +3145,7 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, le32_to_cpu(tr.type), le32_to_cpu(tr.epc), le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), - le32_to_cpu(tr.pc), le32_to_cpu(sh->trap_addr), + le32_to_cpu(tr.pc), sh->trap_addr, le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), -- cgit v1.2.3 From bf52592fe4901f486a5266fd73e4ee80205b18b4 Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Wed, 27 Jun 2012 14:15:59 +0530 Subject: ath9k: Fix compilation breakage Wrap the MCI-work canceling with CONFIG_ATH9K_BTCOEX_SUPPORT. Reported-by: Emmanuel Benisty Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c14cf5aa403b..e4e73f061a22 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -151,8 +151,10 @@ static void __ath_cancel_work(struct ath_softc *sc) cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); +#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT if (ath9k_hw_mci_is_enabled(sc->sc_ah)) cancel_work_sync(&sc->mci_work); +#endif } static void ath_cancel_work(struct ath_softc *sc) -- cgit v1.2.3 From c9015b24b262bc7ea56cfd5d78983a73fb5ebd7d Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Wed, 27 Jun 2012 12:46:24 -0700 Subject: mwifiex: fix memory leak associated with IE manamgement Free ap_custom_ie before return from function. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/ie.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index ceb82cd749cc..383820a52beb 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -213,6 +213,7 @@ mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, /* save assoc resp ie index after auto-indexing */ *assoc_idx = *((u16 *)pos); + kfree(ap_custom_ie); return ret; } -- cgit v1.2.3 From a2028b2376a858cb68a7fcc129f6508f59b45381 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 27 Jun 2012 15:08:19 +0000 Subject: cnic: Fix occasional NULL pointer dereference during reboot. We register with bnx2x before we allocate ctx_tbl structure, so it is possible for bnx2x to call cnic_ctl before the structure is allocated. This can sometimes cause NULL pointer dereference of cp->ctx_tbl. We fix this by adding simple checking for valid state before proceeding. The cnic_ctl call is RCU protected so we don't have to deal with race conditions. Because of the additional checking, we need to finish the shutdown before clearing the CNIC_UP flag. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/cnic.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 0e9be2ba924e..31b05ad325d0 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -291,6 +291,9 @@ static int cnic_get_l5_cid(struct cnic_local *cp, u32 cid, u32 *l5_cid) { u32 i; + if (!cp->ctx_tbl) + return -EINVAL; + for (i = 0; i < cp->max_cid_space; i++) { if (cp->ctx_tbl[i].cid == cid) { *l5_cid = i; @@ -3220,6 +3223,9 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info) u32 l5_cid; struct cnic_local *cp = dev->cnic_priv; + if (!test_bit(CNIC_F_CNIC_UP, &dev->flags)) + break; + if (cnic_get_l5_cid(cp, cid, &l5_cid) == 0) { struct cnic_context *ctx = &cp->ctx_tbl[l5_cid]; @@ -4253,8 +4259,6 @@ static int cnic_cm_shutdown(struct cnic_dev *dev) struct cnic_local *cp = dev->cnic_priv; int i; - cp->stop_cm(dev); - if (!cp->csk_tbl) return 0; @@ -5290,6 +5294,7 @@ static void cnic_stop_hw(struct cnic_dev *dev) i++; } cnic_shutdown_rings(dev); + cp->stop_cm(dev); clear_bit(CNIC_F_CNIC_UP, &dev->flags); RCU_INIT_POINTER(cp->ulp_ops[CNIC_ULP_L4], NULL); synchronize_rcu(); -- cgit v1.2.3 From 78ea22e8042b8a55c5a047869ed1fd9b3711340c Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Wed, 27 Jun 2012 15:08:20 +0000 Subject: cnic: Read bnx2x function number from internal register so that it will work on any hypervisor. Signed-off-by: Eddie Wai Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/cnic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 31b05ad325d0..5980443cb895 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -4988,8 +4988,14 @@ static int cnic_start_bnx2x_hw(struct cnic_dev *dev) cp->port_mode = CHIP_PORT_MODE_NONE; if (BNX2X_CHIP_IS_E2_PLUS(cp->chip_id)) { - u32 val = CNIC_RD(dev, MISC_REG_PORT4MODE_EN_OVWR); + u32 val; + + pci_read_config_dword(dev->pcidev, PCICFG_ME_REGISTER, &val); + cp->func = (u8) ((val & ME_REG_ABS_PF_NUM) >> + ME_REG_ABS_PF_NUM_SHIFT); + func = CNIC_FUNC(cp); + val = CNIC_RD(dev, MISC_REG_PORT4MODE_EN_OVWR); if (!(val & 1)) val = CNIC_RD(dev, MISC_REG_PORT4MODE_EN); else -- cgit v1.2.3 From 1f85d58cdf15354a7120fc9ccc9bb9c45b53af88 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 27 Jun 2012 15:08:21 +0000 Subject: cnic: Remove uio mem[0]. This memory region is no longer used. Userspace gets the BAR address directly from sysfs. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/cnic.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 5980443cb895..ec43df1668c1 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -1063,10 +1063,7 @@ static int cnic_init_uio(struct cnic_dev *dev) uinfo = &udev->cnic_uinfo; - uinfo->mem[0].addr = dev->netdev->base_addr; - uinfo->mem[0].internal_addr = dev->regview; - uinfo->mem[0].size = dev->netdev->mem_end - dev->netdev->mem_start; - uinfo->mem[0].memtype = UIO_MEM_PHYS; + uinfo->mem[0].memtype = UIO_MEM_NONE; if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) { uinfo->mem[1].addr = (unsigned long) cp->status_blk.gen & -- cgit v1.2.3 From 7bc910fd8f953c131de055d9871679a598db8dc6 Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Wed, 27 Jun 2012 15:08:22 +0000 Subject: cnic: Handle RAMROD_CMD_ID_CLOSE error. If firmware returns error status, proceed to close the iSCSI connection. Update version to 2.5.11. Signed-off-by: Eddie Wai Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/cnic.c | 9 +++++++++ drivers/net/ethernet/broadcom/cnic_if.h | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index ec43df1668c1..f897306b0eb7 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -3953,6 +3953,15 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe) cnic_cm_upcall(cp, csk, opcode); break; + case L5CM_RAMROD_CMD_ID_CLOSE: + if (l4kcqe->status != 0) { + netdev_warn(dev->netdev, "RAMROD CLOSE compl with " + "status 0x%x\n", l4kcqe->status); + opcode = L4_KCQE_OPCODE_VALUE_CLOSE_COMP; + /* Fall through */ + } else { + break; + } case L4_KCQE_OPCODE_VALUE_RESET_RECEIVED: case L4_KCQE_OPCODE_VALUE_CLOSE_COMP: case L4_KCQE_OPCODE_VALUE_RESET_COMP: diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index d63d45591c1f..54f68f07692e 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -14,8 +14,8 @@ #include "bnx2x/bnx2x_mfw_req.h" -#define CNIC_MODULE_VERSION "2.5.10" -#define CNIC_MODULE_RELDATE "March 21, 2012" +#define CNIC_MODULE_VERSION "2.5.11" +#define CNIC_MODULE_RELDATE "June 27, 2012" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 -- cgit v1.2.3 From 7947c9ce17c4b51122cfcef3c2e9e2632eb96a67 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 27 Jun 2012 15:08:23 +0000 Subject: bnx2: Add "fall through" comments to indicate that the mising break statements are intended. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 9b69a628641c..e6116ecddd16 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -1972,22 +1972,26 @@ bnx2_remote_phy_event(struct bnx2 *bp) switch (speed) { case BNX2_LINK_STATUS_10HALF: bp->duplex = DUPLEX_HALF; + /* fall through */ case BNX2_LINK_STATUS_10FULL: bp->line_speed = SPEED_10; break; case BNX2_LINK_STATUS_100HALF: bp->duplex = DUPLEX_HALF; + /* fall through */ case BNX2_LINK_STATUS_100BASE_T4: case BNX2_LINK_STATUS_100FULL: bp->line_speed = SPEED_100; break; case BNX2_LINK_STATUS_1000HALF: bp->duplex = DUPLEX_HALF; + /* fall through */ case BNX2_LINK_STATUS_1000FULL: bp->line_speed = SPEED_1000; break; case BNX2_LINK_STATUS_2500HALF: bp->duplex = DUPLEX_HALF; + /* fall through */ case BNX2_LINK_STATUS_2500FULL: bp->line_speed = SPEED_2500; break; -- cgit v1.2.3 From d2e553bca89596b7ac35f8abcec9c9b93f772fc2 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Wed, 27 Jun 2012 15:08:24 +0000 Subject: bnx2: Add missing netif_tx_disable() in bnx2_close() to stop all tx queues. Update version to 2.2.3. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index e6116ecddd16..9eb7624639b9 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -58,8 +58,8 @@ #include "bnx2_fw.h" #define DRV_MODULE_NAME "bnx2" -#define DRV_MODULE_VERSION "2.2.2" -#define DRV_MODULE_RELDATE "June 16, 2012" +#define DRV_MODULE_VERSION "2.2.3" +#define DRV_MODULE_RELDATE "June 27, 2012" #define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-6.2.3.fw" #define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-6.0.15.fw" #define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-6.2.1b.fw" @@ -6703,6 +6703,7 @@ bnx2_close(struct net_device *dev) bnx2_disable_int_sync(bp); bnx2_napi_disable(bp); + netif_tx_disable(dev); del_timer_sync(&bp->timer); bnx2_shutdown_chip(bp); bnx2_free_irq(bp); -- cgit v1.2.3 From 2ca9b2aa0d5a43e954789e0da675a826b83932b7 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 27 Jun 2012 03:45:20 +0000 Subject: net: fec: reset phy after pinctrl setup In case that bootloader or platform initialization does not set up fec pins, the fec_reset_phy will not be able to succeed, because fec_reset_phy is currently called before devm_pinctrl_get_select_default. Move fec_reset_phy call to the place between devm_pinctrl_get_select_default and fec_enet_init to have above case be taken care. Signed-off-by: Shawn Guo Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index ff7f4c5115a1..e868a377c898 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1593,8 +1593,6 @@ fec_probe(struct platform_device *pdev) fep->phy_interface = ret; } - fec_reset_phy(pdev); - for (i = 0; i < FEC_IRQ_NUM; i++) { irq = platform_get_irq(pdev, i); if (irq < 0) { @@ -1634,6 +1632,8 @@ fec_probe(struct platform_device *pdev) clk_prepare_enable(fep->clk_ahb); clk_prepare_enable(fep->clk_ipg); + fec_reset_phy(pdev); + ret = fec_enet_init(ndev); if (ret) goto failed_init; -- cgit v1.2.3 From 5fa9c0fe3ec0a008352d4a4fb8d972a437183404 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 27 Jun 2012 03:45:21 +0000 Subject: net: fec: enable regulator for fec phy If bootloader or platform initialization code does not enable the power supply to fec phy, we need to do it in fec driver before calling fec_reset_phy to have the phy powered on. Signed-off-by: Shawn Guo Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index e868a377c898..4dce9e3ac83a 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -1546,6 +1547,7 @@ fec_probe(struct platform_device *pdev) const struct of_device_id *of_id; static int dev_id; struct pinctrl *pinctrl; + struct regulator *reg_phy; of_id = of_match_device(fec_dt_ids, &pdev->dev); if (of_id) @@ -1632,6 +1634,16 @@ fec_probe(struct platform_device *pdev) clk_prepare_enable(fep->clk_ahb); clk_prepare_enable(fep->clk_ipg); + reg_phy = devm_regulator_get(&pdev->dev, "phy"); + if (!IS_ERR(reg_phy)) { + ret = regulator_enable(reg_phy); + if (ret) { + dev_err(&pdev->dev, + "Failed to enable phy regulator: %d\n", ret); + goto failed_regulator; + } + } + fec_reset_phy(pdev); ret = fec_enet_init(ndev); @@ -1655,6 +1667,7 @@ failed_register: fec_enet_mii_remove(fep); failed_mii_init: failed_init: +failed_regulator: clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); failed_pin: -- cgit v1.2.3 From 119fc007766fc6ec6cff0f696d73cebb6f2cbe58 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 27 Jun 2012 03:45:22 +0000 Subject: net: fec: use managed function devm_gpio_request_one Using gpio_request_one will require the probe fail-out call gpio_free, which is missing currently. Change to use devm_gpio_request_one to fix the problem. Signed-off-by: Shawn Guo Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index 4dce9e3ac83a..f174070646f5 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1513,7 +1513,8 @@ static void __devinit fec_reset_phy(struct platform_device *pdev) return; phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); - err = gpio_request_one(phy_reset, GPIOF_OUT_INIT_LOW, "phy-reset"); + err = devm_gpio_request_one(&pdev->dev, phy_reset, + GPIOF_OUT_INIT_LOW, "phy-reset"); if (err) { pr_debug("FEC: failed to get gpio phy-reset: %d\n", err); return; -- cgit v1.2.3 From a3caad0a160c03b7238a2518fa89abda78adef1e Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 27 Jun 2012 03:45:24 +0000 Subject: net: fec: add phy-reset-duration for device tree probe Different boards may require different phy reset duration. Add property phy-reset-duration for device tree probe, so that the boards that need a longer reset duration can specify it in their device tree. Signed-off-by: Shawn Guo Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/fec.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c index f174070646f5..dafd797a6069 100644 --- a/drivers/net/ethernet/freescale/fec.c +++ b/drivers/net/ethernet/freescale/fec.c @@ -1507,11 +1507,17 @@ static int __devinit fec_get_phy_mode_dt(struct platform_device *pdev) static void __devinit fec_reset_phy(struct platform_device *pdev) { int err, phy_reset; + int msec = 1; struct device_node *np = pdev->dev.of_node; if (!np) return; + of_property_read_u32(np, "phy-reset-duration", &msec); + /* A sane reset duration should not be longer than 1s */ + if (msec > 1000) + msec = 1; + phy_reset = of_get_named_gpio(np, "phy-reset-gpios", 0); err = devm_gpio_request_one(&pdev->dev, phy_reset, GPIOF_OUT_INIT_LOW, "phy-reset"); @@ -1519,7 +1525,7 @@ static void __devinit fec_reset_phy(struct platform_device *pdev) pr_debug("FEC: failed to get gpio phy-reset: %d\n", err); return; } - msleep(1); + msleep(msec); gpio_set_value(phy_reset, 1); } #else /* CONFIG_OF */ -- cgit v1.2.3 From ac28b9f8cd66d6bc54f8063df59e99abd62173a4 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jun 2012 07:33:35 +0000 Subject: netdev/phy: Handle IEEE802.3 clause 45 Ethernet PHYs The IEEE802.3 clause 45 MDIO bus protocol allows for directly addressing PHY registers using a 21 bit address, and is used by many 10G Ethernet PHYS. Already existing is the ability of MDIO bus drivers to use clause 45, with the MII_ADDR_C45 flag. Here we add struct phy_c45_device_ids to hold the device identifier registers present in clause 45. struct phy_device gets a couple of new fields: c45_ids to hold the identifiers and is_c45 to signal that it is clause 45. get_phy_device() gets a new parameter is_c45 to indicate that the PHY device should use the clause 45 protocol, and its callers are adjusted to pass false. The follow-on patch to of_mdio.c will pass true where appropriate. EXPORT phy_device_create() so that the follow-on patch to of_mdio.c can use it to create phy devices for PHYs, that have non-standard device identifier registers, based on the device tree bindings. Signed-off-by: David Daney Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 2 +- drivers/net/phy/phy_device.c | 105 +++++++++++++++++++++++++++++++++++++++---- drivers/of/of_mdio.c | 2 +- 3 files changed, 99 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 31470b0d0c32..2cee6d218d21 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -232,7 +232,7 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) struct phy_device *phydev; int err; - phydev = get_phy_device(bus, addr); + phydev = get_phy_device(bus, addr, false); if (IS_ERR(phydev) || phydev == NULL) return phydev; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 18ab0daf4490..ef4cdeebedd6 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -152,8 +152,8 @@ int phy_scan_fixups(struct phy_device *phydev) } EXPORT_SYMBOL(phy_scan_fixups); -static struct phy_device* phy_device_create(struct mii_bus *bus, - int addr, int phy_id) +struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, + bool is_c45, struct phy_c45_device_ids *c45_ids) { struct phy_device *dev; @@ -174,8 +174,11 @@ static struct phy_device* phy_device_create(struct mii_bus *bus, dev->autoneg = AUTONEG_ENABLE; + dev->is_c45 = is_c45; dev->addr = addr; dev->phy_id = phy_id; + if (c45_ids) + dev->c45_ids = *c45_ids; dev->bus = bus; dev->dev.parent = bus->parent; dev->dev.bus = &mdio_bus_type; @@ -200,20 +203,99 @@ static struct phy_device* phy_device_create(struct mii_bus *bus, return dev; } +EXPORT_SYMBOL(phy_device_create); + +/** + * get_phy_c45_ids - reads the specified addr for its 802.3-c45 IDs. + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * @phy_id: where to store the ID retrieved. + * @c45_ids: where to store the c45 ID information. + * + * If the PHY devices-in-package appears to be valid, it and the + * corresponding identifiers are stored in @c45_ids, zero is stored + * in @phy_id. Otherwise 0xffffffff is stored in @phy_id. Returns + * zero on success. + * + */ +static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, + struct phy_c45_device_ids *c45_ids) { + int phy_reg; + int i, reg_addr; + const int num_ids = ARRAY_SIZE(c45_ids->device_ids); + + /* Find first non-zero Devices In package. Device + * zero is reserved, so don't probe it. + */ + for (i = 1; + i < num_ids && c45_ids->devices_in_package == 0; + i++) { + reg_addr = MII_ADDR_C45 | i << 16 | 6; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->devices_in_package = (phy_reg & 0xffff) << 16; + + reg_addr = MII_ADDR_C45 | i << 16 | 5; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->devices_in_package |= (phy_reg & 0xffff); + + /* If mostly Fs, there is no device there, + * let's get out of here. + */ + if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { + *phy_id = 0xffffffff; + return 0; + } + } + + /* Now probe Device Identifiers for each device present. */ + for (i = 1; i < num_ids; i++) { + if (!(c45_ids->devices_in_package & (1 << i))) + continue; + + reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID1; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->device_ids[i] = (phy_reg & 0xffff) << 16; + + reg_addr = MII_ADDR_C45 | i << 16 | MII_PHYSID2; + phy_reg = mdiobus_read(bus, addr, reg_addr); + if (phy_reg < 0) + return -EIO; + c45_ids->device_ids[i] |= (phy_reg & 0xffff); + } + *phy_id = 0; + return 0; +} /** * get_phy_id - reads the specified addr for its ID. * @bus: the target MII bus * @addr: PHY address on the MII bus * @phy_id: where to store the ID retrieved. + * @is_c45: If true the PHY uses the 802.3 clause 45 protocol + * @c45_ids: where to store the c45 ID information. + * + * Description: In the case of a 802.3-c22 PHY, reads the ID registers + * of the PHY at @addr on the @bus, stores it in @phy_id and returns + * zero on success. + * + * In the case of a 802.3-c45 PHY, get_phy_c45_ids() is invoked, and + * its return value is in turn returned. * - * Description: Reads the ID registers of the PHY at @addr on the - * @bus, stores it in @phy_id and returns zero on success. */ -static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) +static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, + bool is_c45, struct phy_c45_device_ids *c45_ids) { int phy_reg; + if (is_c45) + return get_phy_c45_ids(bus, addr, phy_id, c45_ids); + /* Grab the bits from PHYIR1, and put them * in the upper half */ phy_reg = mdiobus_read(bus, addr, MII_PHYSID1); @@ -238,17 +320,19 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id) * get_phy_device - reads the specified PHY device and returns its @phy_device struct * @bus: the target MII bus * @addr: PHY address on the MII bus + * @is_c45: If true the PHY uses the 802.3 clause 45 protocol * * Description: Reads the ID registers of the PHY at @addr on the * @bus, then allocates and returns the phy_device to represent it. */ -struct phy_device * get_phy_device(struct mii_bus *bus, int addr) +struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) { struct phy_device *dev = NULL; u32 phy_id; + struct phy_c45_device_ids c45_ids = {0}; int r; - r = get_phy_id(bus, addr, &phy_id); + r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); if (r) return ERR_PTR(r); @@ -256,7 +340,7 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) if ((phy_id & 0x1fffffff) == 0x1fffffff) return NULL; - dev = phy_device_create(bus, addr, phy_id); + dev = phy_device_create(bus, addr, phy_id, is_c45, &c45_ids); return dev; } @@ -449,6 +533,11 @@ static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev, /* Assume that if there is no driver, that it doesn't * exist, and we should use the genphy driver. */ if (NULL == d->driver) { + if (phydev->is_c45) { + pr_err("No driver for phy %x\n", phydev->phy_id); + return -ENODEV; + } + d->driver = &genphy_driver.driver; err = d->driver->probe(d); diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 2574abde8d99..6c24cad322df 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -79,7 +79,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) mdio->irq[addr] = PHY_POLL; } - phy = get_phy_device(mdio, addr); + phy = get_phy_device(mdio, addr, false); if (!phy || IS_ERR(phy)) { dev_err(&mdio->dev, "error probing PHY at address %i\n", addr); -- cgit v1.2.3 From 6bd47ac2e434611e52027155438d7b4ad3c76bdb Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jun 2012 07:33:36 +0000 Subject: netdev/phy/of: Handle IEEE802.3 clause 45 Ethernet PHYs in of_mdiobus_register() Define two new "compatible" values for Ethernet PHYs. "ethernet-phy-ieee802.3-c22" and "ethernet-phy-ieee802.3-c45" are used to indicate a PHY uses the corresponding protocol. If a PHY is "compatible" with "ethernet-phy-ieee802.3-c45", we indicate this so that get_phy_device() can properly probe the device. If get_phy_device() fails, it was probably due to failing the probe of the PHY identifier registers. Since we have the device tree telling us the PHY exists, go ahead and add it anyhow with a phy_id of zero. There may be a driver match based on the "compatible" property. Signed-off-by: David Daney Signed-off-by: David S. Miller --- drivers/of/of_mdio.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c index 6c24cad322df..8e6c25f35040 100644 --- a/drivers/of/of_mdio.c +++ b/drivers/of/of_mdio.c @@ -57,6 +57,7 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) const __be32 *paddr; u32 addr; int len; + bool is_c45; /* A PHY must have a reg property in the range [0-31] */ paddr = of_get_property(child, "reg", &len); @@ -79,11 +80,18 @@ int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) mdio->irq[addr] = PHY_POLL; } - phy = get_phy_device(mdio, addr, false); + is_c45 = of_device_is_compatible(child, + "ethernet-phy-ieee802.3-c45"); + phy = get_phy_device(mdio, addr, is_c45); + if (!phy || IS_ERR(phy)) { - dev_err(&mdio->dev, "error probing PHY at address %i\n", - addr); - continue; + phy = phy_device_create(mdio, addr, 0, false, NULL); + if (!phy || IS_ERR(phy)) { + dev_err(&mdio->dev, + "error creating PHY at address %i\n", + addr); + continue; + } } /* Associate the OF node with the device structure so it -- cgit v1.2.3 From a30e2c1891296b5ee8de48430a07fdf8b818c661 Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jun 2012 07:33:37 +0000 Subject: netdev/phy/of: Add more methods for binding PHY devices to drivers. Allow PHY drivers to supply their own device matching function (match_phy_device()), or to be matched OF compatible properties. PHYs following IEEE802.3 clause 45 have more than one device identifier constants, which breaks the default device matching code. Other 10G PHYs don't follow the standard manufacturer/device identifier register layout standards, but they do use the standard MDIO bus protocols for register access. Both of these require adjustments to the PHY driver to device matching code. If the there is an of_node associated with such a PHY, we can match it to its driver using the "compatible" properties, just as we do with certain platform devices. If the "compatible" property match fails, first check if there is a driver supplied matching function, and if not fall back to the existing identifier matching rules. Signed-off-by: David Daney Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 2cee6d218d21..170eb411ab5d 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -308,6 +309,12 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); + if (of_driver_match_device(dev, drv)) + return 1; + + if (phydrv->match_phy_device) + return phydrv->match_phy_device(phydev); + return ((phydrv->phy_id & phydrv->phy_id_mask) == (phydev->phy_id & phydrv->phy_id_mask)); } -- cgit v1.2.3 From e9976d7c96423ac1991396aa82335206ded55bcf Mon Sep 17 00:00:00 2001 From: David Daney Date: Wed, 27 Jun 2012 07:33:38 +0000 Subject: netdev/phy: Add driver for Broadcom BCM87XX 10G Ethernet PHYs Add a driver for BCM8706 and BCM8727 devices. These are a 10Gig PHYs which use MII_ADDR_C45 addressing. They are always 10G full duplex, so there is no autonegotiation. All we do is report link state and send interrupts when it changes. If the PHY has a device tree of_node associated with it, the "broadcom,c45-reg-init" property is used to supply register initialization values when config_init() is called. Signed-off-by: David Daney Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/bcm87xx.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 drivers/net/phy/bcm87xx.c (limited to 'drivers') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 944cdfb80fe4..3090dc65a6f1 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -67,6 +67,11 @@ config BCM63XX_PHY ---help--- Currently supports the 6348 and 6358 PHYs. +config BCM87XX_PHY + tristate "Driver for Broadcom BCM8706 and BCM8727 PHYs" + help + Currently supports the BCM8706 and BCM8727 10G Ethernet PHYs. + config ICPLUS_PHY tristate "Drivers for ICPlus PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index f51af688ef8b..6d2dc6c94f2e 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_VITESSE_PHY) += vitesse.o obj-$(CONFIG_BROADCOM_PHY) += broadcom.o obj-$(CONFIG_BCM63XX_PHY) += bcm63xx.o +obj-$(CONFIG_BCM87XX_PHY) += bcm87xx.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c new file mode 100644 index 000000000000..f5f0562934db --- /dev/null +++ b/drivers/net/phy/bcm87xx.c @@ -0,0 +1,238 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011 - 2012 Cavium, Inc. + */ + +#include +#include +#include + +#define PHY_ID_BCM8706 0x0143bdc1 +#define PHY_ID_BCM8727 0x0143bff0 + +#define BCM87XX_PMD_RX_SIGNAL_DETECT (MII_ADDR_C45 | 0x1000a) +#define BCM87XX_10GBASER_PCS_STATUS (MII_ADDR_C45 | 0x30020) +#define BCM87XX_XGXS_LANE_STATUS (MII_ADDR_C45 | 0x40018) + +#define BCM87XX_LASI_CONTROL (MII_ADDR_C45 | 0x39002) +#define BCM87XX_LASI_STATUS (MII_ADDR_C45 | 0x39005) + +#if IS_ENABLED(CONFIG_OF_MDIO) +/* Set and/or override some configuration registers based on the + * marvell,reg-init property stored in the of_node for the phydev. + * + * broadcom,c45-reg-init = ,...; + * + * There may be one or more sets of : + * + * devid: which sub-device to use. + * reg: the register. + * mask: if non-zero, ANDed with existing register value. + * value: ORed with the masked value and written to the regiser. + * + */ +static int bcm87xx_of_reg_init(struct phy_device *phydev) +{ + const __be32 *paddr; + const __be32 *paddr_end; + int len, ret; + + if (!phydev->dev.of_node) + return 0; + + paddr = of_get_property(phydev->dev.of_node, + "broadcom,c45-reg-init", &len); + if (!paddr) + return 0; + + paddr_end = paddr + (len /= sizeof(*paddr)); + + ret = 0; + + while (paddr + 3 < paddr_end) { + u16 devid = be32_to_cpup(paddr++); + u16 reg = be32_to_cpup(paddr++); + u16 mask = be32_to_cpup(paddr++); + u16 val_bits = be32_to_cpup(paddr++); + int val; + u32 regnum = MII_ADDR_C45 | (devid << 16) | reg; + val = 0; + if (mask) { + val = phy_read(phydev, regnum); + if (val < 0) { + ret = val; + goto err; + } + val &= mask; + } + val |= val_bits; + + ret = phy_write(phydev, regnum, val); + if (ret < 0) + goto err; + } +err: + return ret; +} +#else +static int bcm87xx_of_reg_init(struct phy_device *phydev) +{ + return 0; +} +#endif /* CONFIG_OF_MDIO */ + +static int bcm87xx_config_init(struct phy_device *phydev) +{ + phydev->supported = SUPPORTED_10000baseR_FEC; + phydev->advertising = ADVERTISED_10000baseR_FEC; + phydev->state = PHY_NOLINK; + + bcm87xx_of_reg_init(phydev); + + return 0; +} + +static int bcm87xx_config_aneg(struct phy_device *phydev) +{ + return -EINVAL; +} + +static int bcm87xx_read_status(struct phy_device *phydev) +{ + int rx_signal_detect; + int pcs_status; + int xgxs_lane_status; + + rx_signal_detect = phy_read(phydev, BCM87XX_PMD_RX_SIGNAL_DETECT); + if (rx_signal_detect < 0) + return rx_signal_detect; + + if ((rx_signal_detect & 1) == 0) + goto no_link; + + pcs_status = phy_read(phydev, BCM87XX_10GBASER_PCS_STATUS); + if (pcs_status < 0) + return pcs_status; + + if ((pcs_status & 1) == 0) + goto no_link; + + xgxs_lane_status = phy_read(phydev, BCM87XX_XGXS_LANE_STATUS); + if (xgxs_lane_status < 0) + return xgxs_lane_status; + + if ((xgxs_lane_status & 0x1000) == 0) + goto no_link; + + phydev->speed = 10000; + phydev->link = 1; + phydev->duplex = 1; + return 0; + +no_link: + phydev->link = 0; + return 0; +} + +static int bcm87xx_config_intr(struct phy_device *phydev) +{ + int reg, err; + + reg = phy_read(phydev, BCM87XX_LASI_CONTROL); + + if (reg < 0) + return reg; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + reg |= 1; + else + reg &= ~1; + + err = phy_write(phydev, BCM87XX_LASI_CONTROL, reg); + return err; +} + +static int bcm87xx_did_interrupt(struct phy_device *phydev) +{ + int reg; + + reg = phy_read(phydev, BCM87XX_LASI_STATUS); + + if (reg < 0) { + dev_err(&phydev->dev, + "Error: Read of BCM87XX_LASI_STATUS failed: %d\n", reg); + return 0; + } + return (reg & 1) != 0; +} + +static int bcm87xx_ack_interrupt(struct phy_device *phydev) +{ + /* Reading the LASI status clears it. */ + bcm87xx_did_interrupt(phydev); + return 0; +} + +static int bcm8706_match_phy_device(struct phy_device *phydev) +{ + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8706; +} + +static int bcm8727_match_phy_device(struct phy_device *phydev) +{ + return phydev->c45_ids.device_ids[4] == PHY_ID_BCM8727; +} + +static struct phy_driver bcm8706_driver = { + .phy_id = PHY_ID_BCM8706, + .phy_id_mask = 0xffffffff, + .name = "Broadcom BCM8706", + .flags = PHY_HAS_INTERRUPT, + .config_init = bcm87xx_config_init, + .config_aneg = bcm87xx_config_aneg, + .read_status = bcm87xx_read_status, + .ack_interrupt = bcm87xx_ack_interrupt, + .config_intr = bcm87xx_config_intr, + .did_interrupt = bcm87xx_did_interrupt, + .match_phy_device = bcm8706_match_phy_device, + .driver = { .owner = THIS_MODULE }, +}; + +static struct phy_driver bcm8727_driver = { + .phy_id = PHY_ID_BCM8727, + .phy_id_mask = 0xffffffff, + .name = "Broadcom BCM8727", + .flags = PHY_HAS_INTERRUPT, + .config_init = bcm87xx_config_init, + .config_aneg = bcm87xx_config_aneg, + .read_status = bcm87xx_read_status, + .ack_interrupt = bcm87xx_ack_interrupt, + .config_intr = bcm87xx_config_intr, + .did_interrupt = bcm87xx_did_interrupt, + .match_phy_device = bcm8727_match_phy_device, + .driver = { .owner = THIS_MODULE }, +}; + +static int __init bcm87xx_init(void) +{ + int ret; + + ret = phy_driver_register(&bcm8706_driver); + if (ret) + goto err; + + ret = phy_driver_register(&bcm8727_driver); +err: + return ret; +} +module_init(bcm87xx_init); + +static void __exit bcm87xx_exit(void) +{ + phy_driver_unregister(&bcm8706_driver); + phy_driver_unregister(&bcm8727_driver); +} +module_exit(bcm87xx_exit); -- cgit v1.2.3 From d4fc6918f4b3cc844185f59fc518351525950449 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 27 Jun 2012 05:27:46 +0000 Subject: virtio_net: allow to change mac when iface is running Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index f18149ae2588..36a16d581f71 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -679,11 +679,12 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p) { struct virtnet_info *vi = netdev_priv(dev); struct virtio_device *vdev = vi->vdev; - int ret; + struct sockaddr *addr = p; - ret = eth_mac_addr(dev, p); - if (ret) - return ret; + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + dev->addr_assign_type &= ~NET_ADDR_RANDOM; if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) vdev->config->set(vdev, offsetof(struct virtio_net_config, mac), -- cgit v1.2.3 From 160c85f0e050d92c0e42321aab2ffc343594e1c0 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 27 Jun 2012 21:28:14 -0700 Subject: phy: Fix warning in get_phy_device(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/net/phy/phy_device.c: In function ‘get_phy_device’: drivers/net/phy/phy_device.c:340:14: warning: ‘phy_id’ may be used uninitialized in this function [-Wmaybe-uninitialized] GCC can't see that when we return zero we always initialize phy_id and that's the only path where we use it. Initialize phy_id to zero to shut it up. Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ef4cdeebedd6..47e02e7dc737 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -327,9 +327,9 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id, */ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45) { - struct phy_device *dev = NULL; - u32 phy_id; struct phy_c45_device_ids c45_ids = {0}; + struct phy_device *dev = NULL; + u32 phy_id = 0; int r; r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids); -- cgit v1.2.3 From 4aceed37e315e8eaa26cb4c8dfd619a32fa24669 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 19 Jun 2012 14:39:52 +0200 Subject: HID: hid-multitouch: fix input mode feature command Zytronic panels shows a new way of setting the Input Mode feature. This feature is put in the second usage in the HID feature, instead of the first, as the majority of the multitouch devices. This patch adds a detection step when the feature is presented to know where the feature is located in the report. We can then trigger the right command to the device. This removes the magic number "0" in the function mt_set_input_mode. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 61cc4cbe0f3e..9a3891e00cdd 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -83,6 +83,7 @@ struct mt_device { unsigned last_field_index; /* last field index of the report */ unsigned last_slot_field; /* the last field of a slot */ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ + __s8 inputmode_index; /* InputMode HID feature index in the report */ __s8 maxcontact_report_id; /* Maximum Contact Number HID feature, -1 if non-existent */ __u8 num_received; /* how many contacts we received */ @@ -260,10 +261,20 @@ static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { struct mt_device *td = hid_get_drvdata(hdev); + int i; switch (usage->hid) { case HID_DG_INPUTMODE: td->inputmode = field->report->id; + td->inputmode_index = 0; /* has to be updated below */ + + for (i=0; i < field->maxusage; i++) { + if (field->usage[i].hid == usage->hid) { + td->inputmode_index = i; + break; + } + } + break; case HID_DG_CONTACTMAX: td->maxcontact_report_id = field->report->id; @@ -618,7 +629,7 @@ static void mt_set_input_mode(struct hid_device *hdev) re = &(hdev->report_enum[HID_FEATURE_REPORT]); r = re->report_id_hash[td->inputmode]; if (r) { - r->field[0]->value[0] = 0x02; + r->field[0]->value[td->inputmode_index] = 0x02; usbhid_submit_report(hdev, r, USB_DIR_OUT); } } -- cgit v1.2.3 From 82d069822feaf9bf7eb85d5c9ba9a123ecc8f15f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 19 Jun 2012 14:39:54 +0200 Subject: HID: hid-multitouch: add support for Zytronic panels Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-multitouch.c | 5 +++++ 3 files changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e9c68fedfcff..bcaf3fa79e93 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -393,6 +393,7 @@ config HID_MULTITOUCH - Unitec Panels - XAT optical touch panels - Xiroku optical touch panels + - Zytronic touch panels If unsure, say N. diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 734a2b986502..c77bfdde94ed 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -802,6 +802,9 @@ #define USB_VENDOR_ID_ZYDACRON 0x13EC #define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL 0x0006 +#define USB_VENDOR_ID_ZYTRONIC 0x14c8 +#define USB_DEVICE_ID_ZYTRONIC_ZXY100 0x0005 + #define USB_VENDOR_ID_PRIMAX 0x0461 #define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 9a3891e00cdd..59c8b5c1d2de 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1064,6 +1064,11 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, + /* Zytronic panels */ + { .driver_data = MT_CLS_SERIAL, + MT_USB_DEVICE(USB_VENDOR_ID_ZYTRONIC, + USB_DEVICE_ID_ZYTRONIC_ZXY100) }, + /* Generic MT device */ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, { } -- cgit v1.2.3 From 6a2a6390cf098b899a30146ef5c1fb85c9fefb3c Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 20 May 2012 22:44:54 +0200 Subject: HID: roccat: add support for Roccat Savu This patch adds rupport for Roccat Savu gaming mouse. In comparison to the other Roccat modules I tried to move even more functionality to userland. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/Makefile | 3 +- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-roccat-savu.c | 357 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-roccat-savu.h | 103 ++++++++++++ 5 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 drivers/hid/hid-roccat-savu.c create mode 100644 drivers/hid/hid-roccat-savu.h (limited to 'drivers') diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ca6cc9f0485c..348b90404598 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -69,7 +69,8 @@ obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o obj-$(CONFIG_HID_PRIMAX) += hid-primax.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ - hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o + hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ + hid-roccat-savu.o obj-$(CONFIG_HID_SAITEK) += hid-saitek.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6ac0286b5375..fd95df8d1e07 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1617,6 +1617,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index d1cdd2d28409..ddc293d827f2 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -644,6 +644,7 @@ #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 +#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c new file mode 100644 index 000000000000..d6c82d57408a --- /dev/null +++ b/drivers/hid/hid-roccat-savu.c @@ -0,0 +1,357 @@ +/* + * Roccat Savu driver for Linux + * + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* Roccat Savu is a gamer mouse with macro keys that can be configured in + * 5 profiles. + */ + +#include +#include +#include +#include +#include +#include +#include "hid-ids.h" +#include "hid-roccat-common.h" +#include "hid-roccat-savu.h" + +static struct class *savu_class; + +static int savu_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct savu_control control; + + do { + msleep(50); + retval = roccat_common_receive(usb_dev, SAVU_COMMAND_CONTROL, + &control, sizeof(struct savu_control)); + + if (retval) + return retval; + + switch (control.value) { + case SAVU_CONTROL_REQUEST_WRITE_CHECK_OK: + return 0; + case SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT: + continue; + case SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID: + /* seems to be critical - replug necessary */ + case SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD: + return -EINVAL; + default: + hid_err(usb_dev, "savu_receive_control_status: " + "unknown response value 0x%x\n", + control.value); + return -EINVAL; + } + + } while (1); +} + +static int savu_send(struct usb_device *usb_dev, uint command, + void const *buf, uint size) +{ + int retval; + + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + + return savu_receive_control_status(usb_dev); +} + +static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, + char *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off >= real_size) + return 0; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&savu->savu_lock); + retval = roccat_common_receive(usb_dev, command, buf, real_size); + mutex_unlock(&savu->savu_lock); + + return retval ? retval : real_size; +} + +static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, + void const *buf, loff_t off, size_t count, + size_t real_size, uint command) +{ + struct device *dev = + container_of(kobj, struct device, kobj)->parent->parent; + struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval; + + if (off != 0 || count != real_size) + return -EINVAL; + + mutex_lock(&savu->savu_lock); + retval = savu_send(usb_dev, command, (void *)buf, real_size); + mutex_unlock(&savu->savu_lock); + + return retval ? retval : real_size; +} + +#define SAVU_SYSFS_W(thingy, THINGY) \ +static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return savu_sysfs_write(fp, kobj, buf, off, count, \ + SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ +} + +#define SAVU_SYSFS_R(thingy, THINGY) \ +static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \ + struct kobject *kobj, struct bin_attribute *attr, char *buf, \ + loff_t off, size_t count) \ +{ \ + return savu_sysfs_read(fp, kobj, buf, off, count, \ + SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ +} + +#define SAVU_SYSFS_RW(thingy, THINGY) \ +SAVU_SYSFS_W(thingy, THINGY) \ +SAVU_SYSFS_R(thingy, THINGY) + +#define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0660 }, \ + .size = SAVU_SIZE_ ## THINGY, \ + .read = savu_sysfs_read_ ## thingy, \ + .write = savu_sysfs_write_ ## thingy \ +} + +#define SAVU_BIN_ATTRIBUTE_R(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0440 }, \ + .size = SAVU_SIZE_ ## THINGY, \ + .read = savu_sysfs_read_ ## thingy, \ +} + +#define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \ +{ \ + .attr = { .name = #thingy, .mode = 0220 }, \ + .size = SAVU_SIZE_ ## THINGY, \ + .write = savu_sysfs_write_ ## thingy \ +} + +SAVU_SYSFS_W(control, CONTROL) +SAVU_SYSFS_RW(profile, PROFILE) +SAVU_SYSFS_RW(general, GENERAL) +SAVU_SYSFS_RW(buttons, BUTTONS) +SAVU_SYSFS_RW(macro, MACRO) +SAVU_SYSFS_R(info, INFO) + +static struct bin_attribute savu_bin_attributes[] = { + SAVU_BIN_ATTRIBUTE_W(control, CONTROL), + SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE), + SAVU_BIN_ATTRIBUTE_RW(general, GENERAL), + SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS), + SAVU_BIN_ATTRIBUTE_RW(macro, MACRO), + SAVU_BIN_ATTRIBUTE_R(info, INFO), + __ATTR_NULL +}; + +static int savu_init_savu_device_struct(struct usb_device *usb_dev, + struct savu_device *savu) +{ + mutex_init(&savu->savu_lock); + + return 0; +} + +static int savu_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct savu_device *savu; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) { + hid_set_drvdata(hdev, NULL); + return 0; + } + + savu = kzalloc(sizeof(*savu), GFP_KERNEL); + if (!savu) { + hid_err(hdev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, savu); + + retval = savu_init_savu_device_struct(usb_dev, savu); + if (retval) { + hid_err(hdev, "couldn't init struct savu_device\n"); + goto exit_free; + } + + retval = roccat_connect(savu_class, hdev, + sizeof(struct savu_roccat_report)); + if (retval < 0) { + hid_err(hdev, "couldn't init char dev\n"); + } else { + savu->chrdev_minor = retval; + savu->roccat_claimed = 1; + } + + return 0; +exit_free: + kfree(savu); + return retval; +} + +static void savu_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct savu_device *savu; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return; + + savu = hid_get_drvdata(hdev); + if (savu->roccat_claimed) + roccat_disconnect(savu->chrdev_minor); + kfree(savu); +} + +static int savu_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + hid_err(hdev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + hid_err(hdev, "hw start failed\n"); + goto exit; + } + + retval = savu_init_specials(hdev); + if (retval) { + hid_err(hdev, "couldn't install mouse\n"); + goto exit_stop; + } + + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void savu_remove(struct hid_device *hdev) +{ + savu_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void savu_report_to_chrdev(struct savu_device const *savu, + u8 const *data) +{ + struct savu_roccat_report roccat_report; + struct savu_mouse_report_special const *special_report; + + if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL) + return; + + special_report = (struct savu_mouse_report_special const *)data; + + roccat_report.type = special_report->type; + roccat_report.data[0] = special_report->data[0]; + roccat_report.data[1] = special_report->data[1]; + roccat_report_event(savu->chrdev_minor, + (uint8_t const *)&roccat_report); +} + +static int savu_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct savu_device *savu = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + if (savu == NULL) + return 0; + + if (savu->roccat_claimed) + savu_report_to_chrdev(savu, data); + + return 0; +} + +static const struct hid_device_id savu_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, + { } +}; + +MODULE_DEVICE_TABLE(hid, savu_devices); + +static struct hid_driver savu_driver = { + .name = "savu", + .id_table = savu_devices, + .probe = savu_probe, + .remove = savu_remove, + .raw_event = savu_raw_event +}; + +static int __init savu_init(void) +{ + int retval; + + savu_class = class_create(THIS_MODULE, "savu"); + if (IS_ERR(savu_class)) + return PTR_ERR(savu_class); + savu_class->dev_bin_attrs = savu_bin_attributes; + + retval = hid_register_driver(&savu_driver); + if (retval) + class_destroy(savu_class); + return retval; +} + +static void __exit savu_exit(void) +{ + hid_unregister_driver(&savu_driver); + class_destroy(savu_class); +} + +module_init(savu_init); +module_exit(savu_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Savu driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-savu.h b/drivers/hid/hid-roccat-savu.h new file mode 100644 index 000000000000..97b43d5b0477 --- /dev/null +++ b/drivers/hid/hid-roccat-savu.h @@ -0,0 +1,103 @@ +#ifndef __HID_ROCCAT_SAVU_H +#define __HID_ROCCAT_SAVU_H + +/* + * Copyright (c) 2012 Stefan Achatz + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include + +enum { + SAVU_SIZE_CONTROL = 0x03, + SAVU_SIZE_PROFILE = 0x03, + SAVU_SIZE_GENERAL = 0x10, + SAVU_SIZE_BUTTONS = 0x2f, + SAVU_SIZE_MACRO = 0x0823, + SAVU_SIZE_INFO = 0x08, +}; + +struct savu_control { + uint8_t command; /* SAVU_COMMAND_CONTROL */ + /* + * value is profile number in range 0-4 for requesting settings and buttons + * 1 if status ok for requesting status + */ + uint8_t value; + uint8_t request; +} __packed; + +enum savu_control_requests { + SAVU_CONTROL_REQUEST_WRITE_CHECK = 0x00, + SAVU_CONTROL_REQUEST_GENERAL = 0x80, + SAVU_CONTROL_REQUEST_BUTTONS = 0x90, +}; + +enum savu_control_values { + SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD = 0, + SAVU_CONTROL_REQUEST_WRITE_CHECK_OK = 1, + SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID = 2, + SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT = 3, +}; + +enum savu_commands { + SAVU_COMMAND_CONTROL = 0x4, + SAVU_COMMAND_PROFILE = 0x5, + SAVU_COMMAND_GENERAL = 0x6, + SAVU_COMMAND_BUTTONS = 0x7, + SAVU_COMMAND_MACRO = 0x8, + SAVU_COMMAND_INFO = 0x9, +}; + +struct savu_mouse_report_special { + uint8_t report_number; /* always 3 */ + uint8_t zero; + uint8_t type; + uint8_t data[2]; +} __packed; + +enum { + SAVU_MOUSE_REPORT_NUMBER_SPECIAL = 3, +}; + +enum savu_mouse_report_button_types { + /* data1 = new profile range 1-5 */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_PROFILE = 0x20, + + /* data1 = button number range 1-24; data2 = action */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60, + + /* data1 = button number range 1-24; data2 = action */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80, + + /* data1 = setting number range 1-5 */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0, + + /* data1 and data2 = range 0x1-0xb */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0, + + /* data1 = 22 = next track... + * data2 = action + */ + SAVU_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0, +}; + +struct savu_roccat_report { + uint8_t type; + uint8_t data[2]; +} __packed; + +struct savu_device { + int roccat_claimed; + int chrdev_minor; + + struct mutex savu_lock; +}; + +#endif -- cgit v1.2.3 From 4728f2dc9f8e32ce898223fb863316ed7fa2d224 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 20 May 2012 22:44:59 +0200 Subject: HID: roccat: move functionality to roccat-common Reduced code duplication by moving functions from individual drivers to roccat-common module. Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-common.c | 58 ++++++++++++++++++++++++++ drivers/hid/hid-roccat-common.h | 12 ++++++ drivers/hid/hid-roccat-isku.c | 50 ++-------------------- drivers/hid/hid-roccat-isku.h | 7 ---- drivers/hid/hid-roccat-koneplus.c | 88 +++++++-------------------------------- drivers/hid/hid-roccat-koneplus.h | 22 ---------- drivers/hid/hid-roccat-kovaplus.c | 63 +++++----------------------- drivers/hid/hid-roccat-kovaplus.h | 15 ------- drivers/hid/hid-roccat-pyra.c | 51 +++++------------------ drivers/hid/hid-roccat-pyra.h | 12 ------ drivers/hid/hid-roccat-savu.c | 47 +-------------------- drivers/hid/hid-roccat-savu.h | 18 -------- 12 files changed, 111 insertions(+), 332 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c index a6d93992c75a..291414eac279 100644 --- a/drivers/hid/hid-roccat-common.c +++ b/drivers/hid/hid-roccat-common.c @@ -64,6 +64,64 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id, } EXPORT_SYMBOL_GPL(roccat_common_send); +enum roccat_common_control_states { + ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0, + ROCCAT_COMMON_CONTROL_STATUS_OK = 1, + ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, + ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3, +}; + +static int roccat_common_receive_control_status(struct usb_device *usb_dev) +{ + int retval; + struct roccat_common_control control; + + do { + msleep(50); + retval = roccat_common_receive(usb_dev, + ROCCAT_COMMON_COMMAND_CONTROL, + &control, sizeof(struct roccat_common_control)); + + if (retval) + return retval; + + switch (control.value) { + case ROCCAT_COMMON_CONTROL_STATUS_OK: + return 0; + case ROCCAT_COMMON_CONTROL_STATUS_WAIT: + msleep(500); + continue; + case ROCCAT_COMMON_CONTROL_STATUS_INVALID: + + case ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD: + /* seems to be critical - replug necessary */ + return -EINVAL; + default: + dev_err(&usb_dev->dev, + "roccat_common_receive_control_status: " + "unknown response value 0x%x\n", + control.value); + return -EINVAL; + } + + } while (1); +} + +int roccat_common_send_with_status(struct usb_device *usb_dev, + uint command, void const *buf, uint size) +{ + int retval; + + retval = roccat_common_send(usb_dev, command, buf, size); + if (retval) + return retval; + + msleep(100); + + return roccat_common_receive_control_status(usb_dev); +} +EXPORT_SYMBOL_GPL(roccat_common_send_with_status); + MODULE_AUTHOR("Stefan Achatz"); MODULE_DESCRIPTION("USB Roccat common driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h index 9a5bc61f9699..86bce05da013 100644 --- a/drivers/hid/hid-roccat-common.h +++ b/drivers/hid/hid-roccat-common.h @@ -15,9 +15,21 @@ #include #include +enum roccat_common_commands { + ROCCAT_COMMON_COMMAND_CONTROL = 0x4, +}; + +struct roccat_common_control { + uint8_t command; + uint8_t value; + uint8_t request; /* always 0 on requesting write check */ +} __packed; + int roccat_common_receive(struct usb_device *usb_dev, uint report_id, void *data, uint size); int roccat_common_send(struct usb_device *usb_dev, uint report_id, void const *data, uint size); +int roccat_common_send_with_status(struct usb_device *usb_dev, + uint command, void const *buf, uint size); #endif diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 0e4a0ab47142..20e7f84ee832 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -39,50 +39,6 @@ static int isku_receive(struct usb_device *usb_dev, uint command, return roccat_common_receive(usb_dev, command, buf, size); } -static int isku_receive_control_status(struct usb_device *usb_dev) -{ - int retval; - struct isku_control control; - - do { - msleep(50); - retval = isku_receive(usb_dev, ISKU_COMMAND_CONTROL, - &control, sizeof(struct isku_control)); - - if (retval) - return retval; - - switch (control.value) { - case ISKU_CONTROL_VALUE_STATUS_OK: - return 0; - case ISKU_CONTROL_VALUE_STATUS_WAIT: - continue; - case ISKU_CONTROL_VALUE_STATUS_INVALID: - /* seems to be critical - replug necessary */ - case ISKU_CONTROL_VALUE_STATUS_OVERLOAD: - return -EINVAL; - default: - hid_err(usb_dev, "isku_receive_control_status: " - "unknown response value 0x%x\n", - control.value); - return -EINVAL; - } - - } while (1); -} - -static int isku_send(struct usb_device *usb_dev, uint command, - void const *buf, uint size) -{ - int retval; - - retval = roccat_common_send(usb_dev, command, buf, size); - if (retval) - return retval; - - return isku_receive_control_status(usb_dev); -} - static int isku_get_actual_profile(struct usb_device *usb_dev) { struct isku_actual_profile buf; @@ -100,7 +56,8 @@ static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile) buf.command = ISKU_COMMAND_ACTUAL_PROFILE; buf.size = sizeof(struct isku_actual_profile); buf.actual_profile = new_profile; - return isku_send(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, &buf, + return roccat_common_send_with_status(usb_dev, + ISKU_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct isku_actual_profile)); } @@ -197,7 +154,8 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&isku->isku_lock); - retval = isku_send(usb_dev, command, (void *)buf, real_size); + retval = roccat_common_send_with_status(usb_dev, command, + (void *)buf, real_size); mutex_unlock(&isku->isku_lock); return retval ? retval : real_size; diff --git a/drivers/hid/hid-roccat-isku.h b/drivers/hid/hid-roccat-isku.h index 075f6efaec58..605b3ce21638 100644 --- a/drivers/hid/hid-roccat-isku.h +++ b/drivers/hid/hid-roccat-isku.h @@ -25,13 +25,6 @@ struct isku_control { uint8_t request; } __packed; -enum isku_control_values { - ISKU_CONTROL_VALUE_STATUS_OVERLOAD = 0, - ISKU_CONTROL_VALUE_STATUS_OK = 1, - ISKU_CONTROL_VALUE_STATUS_INVALID = 2, - ISKU_CONTROL_VALUE_STATUS_WAIT = 3, -}; - struct isku_actual_profile { uint8_t command; /* ISKU_COMMAND_ACTUAL_PROFILE */ uint8_t size; /* always 3 */ diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 59e47770fa10..01167a831258 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -39,82 +39,20 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus, static int koneplus_send_control(struct usb_device *usb_dev, uint value, enum koneplus_control_requests request) { - struct koneplus_control control; + struct roccat_common_control control; if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && value > 4) return -EINVAL; - control.command = KONEPLUS_COMMAND_CONTROL; + control.command = ROCCAT_COMMON_COMMAND_CONTROL; control.value = value; control.request = request; - return roccat_common_send(usb_dev, KONEPLUS_COMMAND_CONTROL, - &control, sizeof(struct koneplus_control)); -} - -static int koneplus_receive_control_status(struct usb_device *usb_dev) -{ - int retval; - struct koneplus_control control; - - do { - retval = roccat_common_receive(usb_dev, KONEPLUS_COMMAND_CONTROL, - &control, sizeof(struct koneplus_control)); - - /* check if we get a completely wrong answer */ - if (retval) - return retval; - - if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OK) - return 0; - - /* indicates that hardware needs some more time to complete action */ - if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_WAIT) { - msleep(500); /* windows driver uses 1000 */ - continue; - } - - /* seems to be critical - replug necessary */ - if (control.value == KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) - return -EINVAL; - - hid_err(usb_dev, "koneplus_receive_control_status: " - "unknown response value 0x%x\n", control.value); - return -EINVAL; - } while (1); -} - -static int koneplus_send(struct usb_device *usb_dev, uint command, - void const *buf, uint size) -{ - int retval; - - retval = roccat_common_send(usb_dev, command, buf, size); - if (retval) - return retval; - - return koneplus_receive_control_status(usb_dev); -} - -static int koneplus_select_profile(struct usb_device *usb_dev, uint number, - enum koneplus_control_requests request) -{ - int retval; - - retval = koneplus_send_control(usb_dev, number, request); - if (retval) - return retval; - - /* allow time to settle things - windows driver uses 500 */ - msleep(100); - - retval = koneplus_receive_control_status(usb_dev); - if (retval) - return retval; - - return 0; + return roccat_common_send_with_status(usb_dev, + ROCCAT_COMMON_COMMAND_CONTROL, + &control, sizeof(struct roccat_common_control)); } static int koneplus_get_info(struct usb_device *usb_dev, @@ -129,7 +67,7 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev, { int retval; - retval = koneplus_select_profile(usb_dev, number, + retval = koneplus_send_control(usb_dev, number, KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); if (retval) return retval; @@ -141,7 +79,8 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev, static int koneplus_set_profile_settings(struct usb_device *usb_dev, struct koneplus_profile_settings const *settings) { - return koneplus_send(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS, + return roccat_common_send_with_status(usb_dev, + KONEPLUS_COMMAND_PROFILE_SETTINGS, settings, sizeof(struct koneplus_profile_settings)); } @@ -150,7 +89,7 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev, { int retval; - retval = koneplus_select_profile(usb_dev, number, + retval = koneplus_send_control(usb_dev, number, KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); if (retval) return retval; @@ -162,7 +101,8 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev, static int koneplus_set_profile_buttons(struct usb_device *usb_dev, struct koneplus_profile_buttons const *buttons) { - return koneplus_send(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS, + return roccat_common_send_with_status(usb_dev, + KONEPLUS_COMMAND_PROFILE_BUTTONS, buttons, sizeof(struct koneplus_profile_buttons)); } @@ -187,7 +127,8 @@ static int koneplus_set_actual_profile(struct usb_device *usb_dev, buf.size = sizeof(struct koneplus_actual_profile); buf.actual_profile = new_profile; - return koneplus_send(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE, + return roccat_common_send_with_status(usb_dev, + KONEPLUS_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct koneplus_actual_profile)); } @@ -231,7 +172,8 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = koneplus_send(usb_dev, command, buf, real_size); + retval = roccat_common_send_with_status(usb_dev, command, + buf, real_size); mutex_unlock(&koneplus->koneplus_lock); if (retval) diff --git a/drivers/hid/hid-roccat-koneplus.h b/drivers/hid/hid-roccat-koneplus.h index c03332a4fa9a..7074b2a4b94b 100644 --- a/drivers/hid/hid-roccat-koneplus.h +++ b/drivers/hid/hid-roccat-koneplus.h @@ -20,32 +20,11 @@ struct koneplus_talk { uint8_t data[14]; } __packed; -/* - * case 1: writes request 80 and reads value 1 - * - */ -struct koneplus_control { - uint8_t command; /* KONEPLUS_COMMAND_CONTROL */ - /* - * value is profile number in range 0-4 for requesting settings and buttons - * 1 if status ok for requesting status - */ - uint8_t value; - uint8_t request; -} __attribute__ ((__packed__)); - enum koneplus_control_requests { - KONEPLUS_CONTROL_REQUEST_STATUS = 0x00, KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x80, KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x90, }; -enum koneplus_control_values { - KONEPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, - KONEPLUS_CONTROL_REQUEST_STATUS_OK = 1, - KONEPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, -}; - struct koneplus_actual_profile { uint8_t command; /* KONEPLUS_COMMAND_ACTUAL_PROFILE */ uint8_t size; /* always 3 */ @@ -137,7 +116,6 @@ struct koneplus_tcu_image { } __attribute__ ((__packed__)); enum koneplus_commands { - KONEPLUS_COMMAND_CONTROL = 0x4, KONEPLUS_COMMAND_ACTUAL_PROFILE = 0x5, KONEPLUS_COMMAND_PROFILE_SETTINGS = 0x6, KONEPLUS_COMMAND_PROFILE_BUTTONS = 0x7, diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 112d934132c8..c219cff91555 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -47,69 +47,23 @@ static int kovaplus_send_control(struct usb_device *usb_dev, uint value, enum kovaplus_control_requests request) { int retval; - struct kovaplus_control control; + struct roccat_common_control control; if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && value > 4) return -EINVAL; - control.command = KOVAPLUS_COMMAND_CONTROL; + control.command = ROCCAT_COMMON_COMMAND_CONTROL; control.value = value; control.request = request; - retval = roccat_common_send(usb_dev, KOVAPLUS_COMMAND_CONTROL, - &control, sizeof(struct kovaplus_control)); + retval = roccat_common_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, + &control, sizeof(struct roccat_common_control)); return retval; } -static int kovaplus_receive_control_status(struct usb_device *usb_dev) -{ - int retval; - struct kovaplus_control control; - - do { - retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_CONTROL, - &control, sizeof(struct kovaplus_control)); - - /* check if we get a completely wrong answer */ - if (retval) - return retval; - - if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK) - return 0; - - /* indicates that hardware needs some more time to complete action */ - if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT) { - msleep(500); /* windows driver uses 1000 */ - continue; - } - - /* seems to be critical - replug necessary */ - if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD) - return -EINVAL; - - hid_err(usb_dev, "roccat_common_receive_control_status: " - "unknown response value 0x%x\n", control.value); - return -EINVAL; - } while (1); -} - -static int kovaplus_send(struct usb_device *usb_dev, uint command, - void const *buf, uint size) -{ - int retval; - - retval = roccat_common_send(usb_dev, command, buf, size); - if (retval) - return retval; - - msleep(100); - - return kovaplus_receive_control_status(usb_dev); -} - static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, enum kovaplus_control_requests request) { @@ -140,7 +94,8 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev, static int kovaplus_set_profile_settings(struct usb_device *usb_dev, struct kovaplus_profile_settings const *settings) { - return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS, + return roccat_common_send_with_status(usb_dev, + KOVAPLUS_COMMAND_PROFILE_SETTINGS, settings, sizeof(struct kovaplus_profile_settings)); } @@ -161,7 +116,8 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, static int kovaplus_set_profile_buttons(struct usb_device *usb_dev, struct kovaplus_profile_buttons const *buttons) { - return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS, + return roccat_common_send_with_status(usb_dev, + KOVAPLUS_COMMAND_PROFILE_BUTTONS, buttons, sizeof(struct kovaplus_profile_buttons)); } @@ -186,7 +142,8 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev, buf.size = sizeof(struct kovaplus_actual_profile); buf.actual_profile = new_profile; - return kovaplus_send(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE, + return roccat_common_send_with_status(usb_dev, + KOVAPLUS_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct kovaplus_actual_profile)); } diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h index fb2aed44a8e0..f82daa1cdcb9 100644 --- a/drivers/hid/hid-roccat-kovaplus.h +++ b/drivers/hid/hid-roccat-kovaplus.h @@ -14,27 +14,13 @@ #include -struct kovaplus_control { - uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */ - uint8_t value; - uint8_t request; -} __packed; - enum kovaplus_control_requests { - /* read after write; value = 1 */ - KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0, /* write; value = profile number range 0-4 */ KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, /* write; value = profile number range 0-4 */ KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20, }; -enum kovaplus_control_values { - KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */ - KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1, - KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */ -}; - struct kovaplus_actual_profile { uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */ uint8_t size; /* always 3 */ @@ -75,7 +61,6 @@ struct kovaplus_a { } __packed; enum kovaplus_commands { - KOVAPLUS_COMMAND_CONTROL = 0x4, KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5, KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6, KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7, diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index df05c1b1064f..440cb1bd70d4 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -42,43 +42,19 @@ static void profile_activated(struct pyra_device *pyra, static int pyra_send_control(struct usb_device *usb_dev, int value, enum pyra_control_requests request) { - struct pyra_control control; + struct roccat_common_control control; if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && (value < 0 || value > 4)) return -EINVAL; - control.command = PYRA_COMMAND_CONTROL; + control.command = ROCCAT_COMMON_COMMAND_CONTROL; control.value = value; control.request = request; - return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL, - &control, sizeof(struct pyra_control)); -} - -static int pyra_receive_control_status(struct usb_device *usb_dev) -{ - int retval; - struct pyra_control control; - - do { - msleep(10); - retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL, - &control, sizeof(struct pyra_control)); - - /* requested too early, try again */ - } while (retval == -EPROTO); - - if (!retval && control.command == PYRA_COMMAND_CONTROL && - control.request == PYRA_CONTROL_REQUEST_STATUS && - control.value == 1) - return 0; - else { - hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n", - control.request, control.value); - return retval ? retval : -EINVAL; - } + return roccat_common_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, + &control, sizeof(struct roccat_common_control)); } static int pyra_get_profile_settings(struct usb_device *usb_dev, @@ -118,34 +94,27 @@ static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) buf, sizeof(struct pyra_info)); } -static int pyra_send(struct usb_device *usb_dev, uint command, - void const *buf, uint size) -{ - int retval; - retval = roccat_common_send(usb_dev, command, buf, size); - if (retval) - return retval; - return pyra_receive_control_status(usb_dev); -} - static int pyra_set_profile_settings(struct usb_device *usb_dev, struct pyra_profile_settings const *settings) { - return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings, + return roccat_common_send_with_status(usb_dev, + PYRA_COMMAND_PROFILE_SETTINGS, settings, sizeof(struct pyra_profile_settings)); } static int pyra_set_profile_buttons(struct usb_device *usb_dev, struct pyra_profile_buttons const *buttons) { - return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons, + return roccat_common_send_with_status(usb_dev, + PYRA_COMMAND_PROFILE_BUTTONS, buttons, sizeof(struct pyra_profile_buttons)); } static int pyra_set_settings(struct usb_device *usb_dev, struct pyra_settings const *settings) { - return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings, + return roccat_common_send_with_status(usb_dev, + PYRA_COMMAND_SETTINGS, settings, sizeof(struct pyra_settings)); } diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h index 0442d7fa2dcf..eada7830fa99 100644 --- a/drivers/hid/hid-roccat-pyra.h +++ b/drivers/hid/hid-roccat-pyra.h @@ -20,18 +20,7 @@ struct pyra_b { uint8_t unknown; /* 1 */ } __attribute__ ((__packed__)); -struct pyra_control { - uint8_t command; /* PYRA_COMMAND_CONTROL */ - /* - * value is profile number for request_settings and request_buttons - * 1 if status ok for request_status - */ - uint8_t value; /* Range 0-4 */ - uint8_t request; -} __attribute__ ((__packed__)); - enum pyra_control_requests { - PYRA_CONTROL_REQUEST_STATUS = 0x00, PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20 }; @@ -75,7 +64,6 @@ struct pyra_info { } __attribute__ ((__packed__)); enum pyra_commands { - PYRA_COMMAND_CONTROL = 0x4, PYRA_COMMAND_SETTINGS = 0x5, PYRA_COMMAND_PROFILE_SETTINGS = 0x6, PYRA_COMMAND_PROFILE_BUTTONS = 0x7, diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index d6c82d57408a..19f9c47fc020 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -27,50 +27,6 @@ static struct class *savu_class; -static int savu_receive_control_status(struct usb_device *usb_dev) -{ - int retval; - struct savu_control control; - - do { - msleep(50); - retval = roccat_common_receive(usb_dev, SAVU_COMMAND_CONTROL, - &control, sizeof(struct savu_control)); - - if (retval) - return retval; - - switch (control.value) { - case SAVU_CONTROL_REQUEST_WRITE_CHECK_OK: - return 0; - case SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT: - continue; - case SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID: - /* seems to be critical - replug necessary */ - case SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD: - return -EINVAL; - default: - hid_err(usb_dev, "savu_receive_control_status: " - "unknown response value 0x%x\n", - control.value); - return -EINVAL; - } - - } while (1); -} - -static int savu_send(struct usb_device *usb_dev, uint command, - void const *buf, uint size) -{ - int retval; - - retval = roccat_common_send(usb_dev, command, buf, size); - if (retval) - return retval; - - return savu_receive_control_status(usb_dev); -} - static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, char *buf, loff_t off, size_t count, size_t real_size, uint command) @@ -108,7 +64,8 @@ static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&savu->savu_lock); - retval = savu_send(usb_dev, command, (void *)buf, real_size); + retval = roccat_common_send_with_status(usb_dev, command, + (void *)buf, real_size); mutex_unlock(&savu->savu_lock); return retval ? retval : real_size; diff --git a/drivers/hid/hid-roccat-savu.h b/drivers/hid/hid-roccat-savu.h index 97b43d5b0477..b15a1bbb66fb 100644 --- a/drivers/hid/hid-roccat-savu.h +++ b/drivers/hid/hid-roccat-savu.h @@ -23,29 +23,11 @@ enum { SAVU_SIZE_INFO = 0x08, }; -struct savu_control { - uint8_t command; /* SAVU_COMMAND_CONTROL */ - /* - * value is profile number in range 0-4 for requesting settings and buttons - * 1 if status ok for requesting status - */ - uint8_t value; - uint8_t request; -} __packed; - enum savu_control_requests { - SAVU_CONTROL_REQUEST_WRITE_CHECK = 0x00, SAVU_CONTROL_REQUEST_GENERAL = 0x80, SAVU_CONTROL_REQUEST_BUTTONS = 0x90, }; -enum savu_control_values { - SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD = 0, - SAVU_CONTROL_REQUEST_WRITE_CHECK_OK = 1, - SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID = 2, - SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT = 3, -}; - enum savu_commands { SAVU_COMMAND_CONTROL = 0x4, SAVU_COMMAND_PROFILE = 0x5, -- cgit v1.2.3 From 4ec141ad4e470485803a98ddb250aa7df030e8df Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 20 May 2012 22:45:08 +0200 Subject: HID: roccat: fix wrong hid_err usage on struct usb_device Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-kone.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 40090d602158..9ce2d0b615a4 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -138,7 +138,7 @@ static int kone_check_write(struct usb_device *usb_dev) return 0; /* unknown answer */ - hid_err(usb_dev, "got retval %d when checking write\n", data); + dev_err(&usb_dev->dev, "got retval %d when checking write\n", data); return -EIO; } @@ -503,7 +503,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev, retval = kone_set_settings(usb_dev, &kone->settings); if (retval) { - hid_err(usb_dev, "couldn't set tcu state\n"); + dev_err(&usb_dev->dev, "couldn't set tcu state\n"); /* * try to reread valid settings into buffer overwriting * first error code @@ -519,7 +519,7 @@ static ssize_t kone_sysfs_set_tcu(struct device *dev, retval = size; exit_no_settings: - hid_err(usb_dev, "couldn't read settings\n"); + dev_err(&usb_dev->dev, "couldn't read settings\n"); exit_unlock: mutex_unlock(&kone->kone_lock); return retval; -- cgit v1.2.3 From 7392d73be2b3c907d65126f072c313215e1907b3 Mon Sep 17 00:00:00 2001 From: Stefan Achatz Date: Sun, 20 May 2012 22:45:04 +0200 Subject: HID: roccat: rename roccat_common functions to roccat_common2 Did this to illustrate my understanding of the firmware generations: Valo and Kone were 1st generation Arvo was externaly developed and lies in the middle All others until now are considered 2nd generation Signed-off-by: Stefan Achatz Signed-off-by: Jiri Kosina --- drivers/hid/hid-roccat-arvo.c | 16 ++++++++-------- drivers/hid/hid-roccat-common.c | 34 +++++++++++++++++----------------- drivers/hid/hid-roccat-common.h | 10 +++++----- drivers/hid/hid-roccat-isku.c | 6 +++--- drivers/hid/hid-roccat-koneplus.c | 24 ++++++++++++------------ drivers/hid/hid-roccat-kovaplus.c | 20 ++++++++++---------- drivers/hid/hid-roccat-pyra.c | 20 ++++++++++---------- drivers/hid/hid-roccat-savu.c | 4 ++-- 8 files changed, 67 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index 093bfad00b02..327f9b8ed1f4 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -39,7 +39,7 @@ static ssize_t arvo_sysfs_show_mode_key(struct device *dev, int retval; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_receive(usb_dev, ARVO_COMMAND_MODE_KEY, + retval = roccat_common2_receive(usb_dev, ARVO_COMMAND_MODE_KEY, &temp_buf, sizeof(struct arvo_mode_key)); mutex_unlock(&arvo->arvo_lock); if (retval) @@ -67,7 +67,7 @@ static ssize_t arvo_sysfs_set_mode_key(struct device *dev, temp_buf.state = state; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_send(usb_dev, ARVO_COMMAND_MODE_KEY, + retval = roccat_common2_send(usb_dev, ARVO_COMMAND_MODE_KEY, &temp_buf, sizeof(struct arvo_mode_key)); mutex_unlock(&arvo->arvo_lock); if (retval) @@ -87,7 +87,7 @@ static ssize_t arvo_sysfs_show_key_mask(struct device *dev, int retval; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_receive(usb_dev, ARVO_COMMAND_KEY_MASK, + retval = roccat_common2_receive(usb_dev, ARVO_COMMAND_KEY_MASK, &temp_buf, sizeof(struct arvo_key_mask)); mutex_unlock(&arvo->arvo_lock); if (retval) @@ -115,7 +115,7 @@ static ssize_t arvo_sysfs_set_key_mask(struct device *dev, temp_buf.key_mask = key_mask; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_send(usb_dev, ARVO_COMMAND_KEY_MASK, + retval = roccat_common2_send(usb_dev, ARVO_COMMAND_KEY_MASK, &temp_buf, sizeof(struct arvo_key_mask)); mutex_unlock(&arvo->arvo_lock); if (retval) @@ -130,7 +130,7 @@ static int arvo_get_actual_profile(struct usb_device *usb_dev) struct arvo_actual_profile temp_buf; int retval; - retval = roccat_common_receive(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE, + retval = roccat_common2_receive(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE, &temp_buf, sizeof(struct arvo_actual_profile)); if (retval) @@ -170,7 +170,7 @@ static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, temp_buf.actual_profile = profile; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_send(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE, + retval = roccat_common2_send(usb_dev, ARVO_COMMAND_ACTUAL_PROFILE, &temp_buf, sizeof(struct arvo_actual_profile)); if (!retval) { arvo->actual_profile = profile; @@ -194,7 +194,7 @@ static ssize_t arvo_sysfs_write(struct file *fp, return -EINVAL; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_send(usb_dev, command, buf, real_size); + retval = roccat_common2_send(usb_dev, command, buf, real_size); mutex_unlock(&arvo->arvo_lock); return (retval ? retval : real_size); @@ -217,7 +217,7 @@ static ssize_t arvo_sysfs_read(struct file *fp, return -EINVAL; mutex_lock(&arvo->arvo_lock); - retval = roccat_common_receive(usb_dev, command, buf, real_size); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&arvo->arvo_lock); return (retval ? retval : real_size); diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c index 291414eac279..74f704032627 100644 --- a/drivers/hid/hid-roccat-common.c +++ b/drivers/hid/hid-roccat-common.c @@ -16,12 +16,12 @@ #include #include "hid-roccat-common.h" -static inline uint16_t roccat_common_feature_report(uint8_t report_id) +static inline uint16_t roccat_common2_feature_report(uint8_t report_id) { return 0x300 | report_id; } -int roccat_common_receive(struct usb_device *usb_dev, uint report_id, +int roccat_common2_receive(struct usb_device *usb_dev, uint report_id, void *data, uint size) { char *buf; @@ -34,16 +34,16 @@ int roccat_common_receive(struct usb_device *usb_dev, uint report_id, len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), HID_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, - roccat_common_feature_report(report_id), + roccat_common2_feature_report(report_id), 0, buf, size, USB_CTRL_SET_TIMEOUT); memcpy(data, buf, size); kfree(buf); return ((len < 0) ? len : ((len != size) ? -EIO : 0)); } -EXPORT_SYMBOL_GPL(roccat_common_receive); +EXPORT_SYMBOL_GPL(roccat_common2_receive); -int roccat_common_send(struct usb_device *usb_dev, uint report_id, +int roccat_common2_send(struct usb_device *usb_dev, uint report_id, void const *data, uint size) { char *buf; @@ -56,31 +56,31 @@ int roccat_common_send(struct usb_device *usb_dev, uint report_id, len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), HID_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, - roccat_common_feature_report(report_id), + roccat_common2_feature_report(report_id), 0, buf, size, USB_CTRL_SET_TIMEOUT); kfree(buf); return ((len < 0) ? len : ((len != size) ? -EIO : 0)); } -EXPORT_SYMBOL_GPL(roccat_common_send); +EXPORT_SYMBOL_GPL(roccat_common2_send); -enum roccat_common_control_states { +enum roccat_common2_control_states { ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0, ROCCAT_COMMON_CONTROL_STATUS_OK = 1, ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3, }; -static int roccat_common_receive_control_status(struct usb_device *usb_dev) +static int roccat_common2_receive_control_status(struct usb_device *usb_dev) { int retval; - struct roccat_common_control control; + struct roccat_common2_control control; do { msleep(50); - retval = roccat_common_receive(usb_dev, + retval = roccat_common2_receive(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, - &control, sizeof(struct roccat_common_control)); + &control, sizeof(struct roccat_common2_control)); if (retval) return retval; @@ -98,7 +98,7 @@ static int roccat_common_receive_control_status(struct usb_device *usb_dev) return -EINVAL; default: dev_err(&usb_dev->dev, - "roccat_common_receive_control_status: " + "roccat_common2_receive_control_status: " "unknown response value 0x%x\n", control.value); return -EINVAL; @@ -107,20 +107,20 @@ static int roccat_common_receive_control_status(struct usb_device *usb_dev) } while (1); } -int roccat_common_send_with_status(struct usb_device *usb_dev, +int roccat_common2_send_with_status(struct usb_device *usb_dev, uint command, void const *buf, uint size) { int retval; - retval = roccat_common_send(usb_dev, command, buf, size); + retval = roccat_common2_send(usb_dev, command, buf, size); if (retval) return retval; msleep(100); - return roccat_common_receive_control_status(usb_dev); + return roccat_common2_receive_control_status(usb_dev); } -EXPORT_SYMBOL_GPL(roccat_common_send_with_status); +EXPORT_SYMBOL_GPL(roccat_common2_send_with_status); MODULE_AUTHOR("Stefan Achatz"); MODULE_DESCRIPTION("USB Roccat common driver"); diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h index 86bce05da013..a97746a63b70 100644 --- a/drivers/hid/hid-roccat-common.h +++ b/drivers/hid/hid-roccat-common.h @@ -15,21 +15,21 @@ #include #include -enum roccat_common_commands { +enum roccat_common2_commands { ROCCAT_COMMON_COMMAND_CONTROL = 0x4, }; -struct roccat_common_control { +struct roccat_common2_control { uint8_t command; uint8_t value; uint8_t request; /* always 0 on requesting write check */ } __packed; -int roccat_common_receive(struct usb_device *usb_dev, uint report_id, +int roccat_common2_receive(struct usb_device *usb_dev, uint report_id, void *data, uint size); -int roccat_common_send(struct usb_device *usb_dev, uint report_id, +int roccat_common2_send(struct usb_device *usb_dev, uint report_id, void const *data, uint size); -int roccat_common_send_with_status(struct usb_device *usb_dev, +int roccat_common2_send_with_status(struct usb_device *usb_dev, uint command, void const *buf, uint size); #endif diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 20e7f84ee832..5669916c2943 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -36,7 +36,7 @@ static void isku_profile_activated(struct isku_device *isku, uint new_profile) static int isku_receive(struct usb_device *usb_dev, uint command, void *buf, uint size) { - return roccat_common_receive(usb_dev, command, buf, size); + return roccat_common2_receive(usb_dev, command, buf, size); } static int isku_get_actual_profile(struct usb_device *usb_dev) @@ -56,7 +56,7 @@ static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile) buf.command = ISKU_COMMAND_ACTUAL_PROFILE; buf.size = sizeof(struct isku_actual_profile); buf.actual_profile = new_profile; - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct isku_actual_profile)); } @@ -154,7 +154,7 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&isku->isku_lock); - retval = roccat_common_send_with_status(usb_dev, command, + retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); mutex_unlock(&isku->isku_lock); diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 01167a831258..f5602fec4865 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -39,7 +39,7 @@ static void koneplus_profile_activated(struct koneplus_device *koneplus, static int koneplus_send_control(struct usb_device *usb_dev, uint value, enum koneplus_control_requests request) { - struct roccat_common_control control; + struct roccat_common2_control control; if ((request == KONEPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || request == KONEPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && @@ -50,15 +50,15 @@ static int koneplus_send_control(struct usb_device *usb_dev, uint value, control.value = value; control.request = request; - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, - &control, sizeof(struct roccat_common_control)); + &control, sizeof(struct roccat_common2_control)); } static int koneplus_get_info(struct usb_device *usb_dev, struct koneplus_info *buf) { - return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_INFO, + return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_INFO, buf, sizeof(struct koneplus_info)); } @@ -72,14 +72,14 @@ static int koneplus_get_profile_settings(struct usb_device *usb_dev, if (retval) return retval; - return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS, + return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS, buf, sizeof(struct koneplus_profile_settings)); } static int koneplus_set_profile_settings(struct usb_device *usb_dev, struct koneplus_profile_settings const *settings) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, KONEPLUS_COMMAND_PROFILE_SETTINGS, settings, sizeof(struct koneplus_profile_settings)); } @@ -94,14 +94,14 @@ static int koneplus_get_profile_buttons(struct usb_device *usb_dev, if (retval) return retval; - return roccat_common_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS, + return roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS, buf, sizeof(struct koneplus_profile_buttons)); } static int koneplus_set_profile_buttons(struct usb_device *usb_dev, struct koneplus_profile_buttons const *buttons) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, KONEPLUS_COMMAND_PROFILE_BUTTONS, buttons, sizeof(struct koneplus_profile_buttons)); } @@ -112,7 +112,7 @@ static int koneplus_get_actual_profile(struct usb_device *usb_dev) struct koneplus_actual_profile buf; int retval; - retval = roccat_common_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE, + retval = roccat_common2_receive(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct koneplus_actual_profile)); return retval ? retval : buf.actual_profile; @@ -127,7 +127,7 @@ static int koneplus_set_actual_profile(struct usb_device *usb_dev, buf.size = sizeof(struct koneplus_actual_profile); buf.actual_profile = new_profile; - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, KONEPLUS_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct koneplus_actual_profile)); } @@ -149,7 +149,7 @@ static ssize_t koneplus_sysfs_read(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = roccat_common_receive(usb_dev, command, buf, real_size); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&koneplus->koneplus_lock); if (retval) @@ -172,7 +172,7 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&koneplus->koneplus_lock); - retval = roccat_common_send_with_status(usb_dev, command, + retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size); mutex_unlock(&koneplus->koneplus_lock); diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index c219cff91555..ca6527ac655d 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -47,7 +47,7 @@ static int kovaplus_send_control(struct usb_device *usb_dev, uint value, enum kovaplus_control_requests request) { int retval; - struct roccat_common_control control; + struct roccat_common2_control control; if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && @@ -58,8 +58,8 @@ static int kovaplus_send_control(struct usb_device *usb_dev, uint value, control.value = value; control.request = request; - retval = roccat_common_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, - &control, sizeof(struct roccat_common_control)); + retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, + &control, sizeof(struct roccat_common2_control)); return retval; } @@ -73,7 +73,7 @@ static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, static int kovaplus_get_info(struct usb_device *usb_dev, struct kovaplus_info *buf) { - return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_INFO, + return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO, buf, sizeof(struct kovaplus_info)); } @@ -87,14 +87,14 @@ static int kovaplus_get_profile_settings(struct usb_device *usb_dev, if (retval) return retval; - return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS, + return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS, buf, sizeof(struct kovaplus_profile_settings)); } static int kovaplus_set_profile_settings(struct usb_device *usb_dev, struct kovaplus_profile_settings const *settings) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS, settings, sizeof(struct kovaplus_profile_settings)); } @@ -109,14 +109,14 @@ static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, if (retval) return retval; - return roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS, + return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS, buf, sizeof(struct kovaplus_profile_buttons)); } static int kovaplus_set_profile_buttons(struct usb_device *usb_dev, struct kovaplus_profile_buttons const *buttons) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS, buttons, sizeof(struct kovaplus_profile_buttons)); } @@ -127,7 +127,7 @@ static int kovaplus_get_actual_profile(struct usb_device *usb_dev) struct kovaplus_actual_profile buf; int retval; - retval = roccat_common_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE, + retval = roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct kovaplus_actual_profile)); return retval ? retval : buf.actual_profile; @@ -142,7 +142,7 @@ static int kovaplus_set_actual_profile(struct usb_device *usb_dev, buf.size = sizeof(struct kovaplus_actual_profile); buf.actual_profile = new_profile; - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE, &buf, sizeof(struct kovaplus_actual_profile)); } diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 440cb1bd70d4..1317c177a3e2 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -42,7 +42,7 @@ static void profile_activated(struct pyra_device *pyra, static int pyra_send_control(struct usb_device *usb_dev, int value, enum pyra_control_requests request) { - struct roccat_common_control control; + struct roccat_common2_control control; if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && @@ -53,8 +53,8 @@ static int pyra_send_control(struct usb_device *usb_dev, int value, control.value = value; control.request = request; - return roccat_common_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, - &control, sizeof(struct roccat_common_control)); + return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, + &control, sizeof(struct roccat_common2_control)); } static int pyra_get_profile_settings(struct usb_device *usb_dev, @@ -65,7 +65,7 @@ static int pyra_get_profile_settings(struct usb_device *usb_dev, PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); if (retval) return retval; - return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, + return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, buf, sizeof(struct pyra_profile_settings)); } @@ -77,27 +77,27 @@ static int pyra_get_profile_buttons(struct usb_device *usb_dev, PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); if (retval) return retval; - return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, + return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buf, sizeof(struct pyra_profile_buttons)); } static int pyra_get_settings(struct usb_device *usb_dev, struct pyra_settings *buf) { - return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS, + return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS, buf, sizeof(struct pyra_settings)); } static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) { - return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO, + return roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO, buf, sizeof(struct pyra_info)); } static int pyra_set_profile_settings(struct usb_device *usb_dev, struct pyra_profile_settings const *settings) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings, sizeof(struct pyra_profile_settings)); } @@ -105,7 +105,7 @@ static int pyra_set_profile_settings(struct usb_device *usb_dev, static int pyra_set_profile_buttons(struct usb_device *usb_dev, struct pyra_profile_buttons const *buttons) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons, sizeof(struct pyra_profile_buttons)); } @@ -113,7 +113,7 @@ static int pyra_set_profile_buttons(struct usb_device *usb_dev, static int pyra_set_settings(struct usb_device *usb_dev, struct pyra_settings const *settings) { - return roccat_common_send_with_status(usb_dev, + return roccat_common2_send_with_status(usb_dev, PYRA_COMMAND_SETTINGS, settings, sizeof(struct pyra_settings)); } diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 19f9c47fc020..29e87d712de0 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -44,7 +44,7 @@ static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&savu->savu_lock); - retval = roccat_common_receive(usb_dev, command, buf, real_size); + retval = roccat_common2_receive(usb_dev, command, buf, real_size); mutex_unlock(&savu->savu_lock); return retval ? retval : real_size; @@ -64,7 +64,7 @@ static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&savu->savu_lock); - retval = roccat_common_send_with_status(usb_dev, command, + retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size); mutex_unlock(&savu->savu_lock); -- cgit v1.2.3 From 6ac7d11527a31d01b566a5c45369180e326ff4f1 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 6 Jun 2012 14:17:46 +0200 Subject: treewide: Put a space between #include and FILE Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/media/video/zoran/zr36016.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-io.c | 2 +- drivers/scsi/aha1542.c | 2 +- drivers/staging/comedi/drivers/s626.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/zoran/zr36016.c b/drivers/media/video/zoran/zr36016.c index 21c088ea9046..b87ddba8608f 100644 --- a/drivers/media/video/zoran/zr36016.c +++ b/drivers/media/video/zoran/zr36016.c @@ -40,10 +40,10 @@ /* v4l API */ /* headerfile of this module */ -#include"zr36016.h" +#include "zr36016.h" /* codec io API */ -#include"videocodec.h" +#include "videocodec.h" /* it doesn't make sense to have more than 20 or so, just to prevent some unwanted loops */ diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 081dd34d2387..0f8b8aa9e8d7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -29,7 +29,7 @@ #include #include "iwl-io.h" -#include"iwl-csr.h" +#include "iwl-csr.h" #include "iwl-debug.h" #define IWL_POLL_INTERVAL 10 /* microseconds */ diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c index f79c8f9e33a4..770c48ddbe5e 100644 --- a/drivers/scsi/aha1542.c +++ b/drivers/scsi/aha1542.c @@ -49,7 +49,7 @@ #define SCSI_BUF_PA(address) isa_virt_to_bus(address) #define SCSI_SG_PA(sgent) (isa_page_to_bus(sg_page((sgent))) + (sgent)->offset) -#include +#include #ifdef DEBUG #define DEB(x) x diff --git a/drivers/staging/comedi/drivers/s626.h b/drivers/staging/comedi/drivers/s626.h index 2d1afecbbb60..92d3ea5eb44d 100644 --- a/drivers/staging/comedi/drivers/s626.h +++ b/drivers/staging/comedi/drivers/s626.h @@ -80,7 +80,7 @@ #define INLINE static __inline #endif -#include +#include #define S626_SIZE 0x0200 #define SIZEOF_ADDRESS_SPACE 0x0200 -- cgit v1.2.3 From 8b7d133cc2dc5295bd720acaccf616584604369d Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 6 Jun 2012 23:28:23 +0200 Subject: ab8500-btemp: Fix typo 'AB5500' Signed-off-by: Paul Bolle Signed-off-by: Jiri Kosina --- drivers/power/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 99dc29f2f2f2..f2525af2e39b 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -299,7 +299,7 @@ config AB8500_BM bool "AB8500 Battery Management Driver" depends on AB8500_CORE && AB8500_GPADC help - Say Y to include support for AB5500 battery management. + Say Y to include support for AB8500 battery management. config AB8500_BATTERY_THERM_ON_BATCTRL bool "Thermistor connected on BATCTRL ADC" -- cgit v1.2.3 From 26b6da6b7829dc7cf5d21e854564be90c7928323 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 27 Jun 2012 10:36:27 +0200 Subject: iwlegacy: print how long queue was actually stuck Every now and then, after resuming from suspend, the iwlegacy driver prints iwl4965 0000:03:00.0: Queue 2 stuck for 2000 ms. iwl4965 0000:03:00.0: On demand firmware reload I have no idea what causes these errors. But the code currently uses wd_timeout in the first error. wd_timeout will generally be set at IL_DEF_WD_TIMEOUT (ie, 2000). Perhaps printing for how long the queue was actually stuck can clarify the cause of these errors. Signed-off-by: Paul Bolle Signed-off-by: John W. Linville --- drivers/net/wireless/iwlegacy/common.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index cbf2dc18341f..763c7529921b 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -4717,10 +4717,11 @@ il_check_stuck_queue(struct il_priv *il, int cnt) struct il_tx_queue *txq = &il->txq[cnt]; struct il_queue *q = &txq->q; unsigned long timeout; + unsigned long now = jiffies; int ret; if (q->read_ptr == q->write_ptr) { - txq->time_stamp = jiffies; + txq->time_stamp = now; return 0; } @@ -4728,9 +4729,9 @@ il_check_stuck_queue(struct il_priv *il, int cnt) txq->time_stamp + msecs_to_jiffies(il->cfg->wd_timeout); - if (time_after(jiffies, timeout)) { + if (time_after(now, timeout)) { IL_ERR("Queue %d stuck for %u ms.\n", q->id, - il->cfg->wd_timeout); + jiffies_to_msecs(now - txq->time_stamp)); ret = il_force_reset(il, false); return (ret == -EAGAIN) ? 0 : 1; } -- cgit v1.2.3 From 73dc3eb8b93eda8101dcd6c0b2a0db00f4bc74fd Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 27 Jun 2012 14:58:19 +0200 Subject: ath9k: fix ANI operation in AP mode ath9k_ani_reset (which is called at reset time) uses a state variable ani->update_ani to prevent the ANI noise immunity state on the operating channel from being overwritten by background scans. Unfortunately this is also being set for AP mode, since it's mixed with code that is only supposed to change the default settings after a reset. In AP mode this has the side effect of having ANI run, but being unable to change its runtime noise immunity level, making it effectively useless. Fix this by getting rid of ani->update_ani and passing a parameter to ath9k_hw_set_ofdm_nil and ath9k_hw_set_cck_nil instead. Signed-off-by: Felix Fietkau Cc: Rajkumar Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ani.c | 29 +++++++++++++++-------------- drivers/net/wireless/ath/ath9k/ani.h | 1 - 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 7ebc3465f22d..ff007f500feb 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -140,7 +140,8 @@ static void ath9k_ani_restart(struct ath_hw *ah) } /* Adjust the OFDM Noise Immunity Level */ -static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) +static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, + bool scan) { struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); @@ -153,7 +154,7 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) immunityLevel, BEACON_RSSI(ah), aniState->rssiThrLow, aniState->rssiThrHigh); - if (aniState->update_ani) + if (!scan) aniState->ofdmNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; @@ -199,13 +200,14 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah) aniState = &ah->curchan->ani; if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL) - ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1); + ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false); } /* * Set the ANI settings to match an CCK level. */ -static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) +static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, + bool scan) { struct ar5416AniState *aniState = &ah->curchan->ani; struct ath_common *common = ath9k_hw_common(ah); @@ -222,7 +224,7 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; - if (aniState->update_ani) + if (!scan) aniState->cckNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; @@ -254,7 +256,8 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah) aniState = &ah->curchan->ani; if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL) - ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1); + ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1, + false); } /* @@ -270,13 +273,15 @@ static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah) /* lower OFDM noise immunity */ if (aniState->ofdmNoiseImmunityLevel > 0 && (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) { - ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1); + ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1, + false); return; } /* lower CCK noise immunity */ if (aniState->cckNoiseImmunityLevel > 0) - ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1); + ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1, + false); } /* @@ -338,7 +343,6 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); - aniState->update_ani = false; ofdm_nil = ATH9K_ANI_OFDM_DEF_LEVEL; cck_nil = ATH9K_ANI_CCK_DEF_LEVEL; } @@ -354,11 +358,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); - - aniState->update_ani = true; } - ath9k_hw_set_ofdm_nil(ah, ofdm_nil); - ath9k_hw_set_cck_nil(ah, cck_nil); + ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning); + ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning); /* * enable phy counters if hw supports or if not, enable phy @@ -534,7 +536,6 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; - ani->update_ani = false; } /* diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index e9d841bbe86f..1485bf5e3518 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -114,7 +114,6 @@ struct ar5416AniState { u8 firstepLevel; u8 ofdmWeakSigDetect; u8 cckWeakSigThreshold; - bool update_ani; u32 listenTime; int32_t rssiThrLow; int32_t rssiThrHigh; -- cgit v1.2.3 From 812944891c598300afcc5628905d775019f25310 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 27 Jun 2012 20:00:26 +0530 Subject: ath9k_hw: make use of the wrapper to check for MCI init ath9k_hw_mci_is_enabled wrapper also takes care of ATH9K_HW_CAP_MCI being set for the AR9462 under test. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 26032cb59b8a..9ae6a4d97691 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -416,7 +416,7 @@ int ath9k_init_btcoex(struct ath_softc *sc) txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - if (AR_SREV_9462(ah)) { + if (ath9k_hw_mci_is_enabled(ah)) { sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; INIT_LIST_HEAD(&sc->btcoex.mci.info); -- cgit v1.2.3 From d8fffb4a9e13d1130b4fd0e577973704cad79f40 Mon Sep 17 00:00:00 2001 From: Mohammed Shafi Shajakhan Date: Wed, 27 Jun 2012 20:00:27 +0530 Subject: ath9k: Fix signedness in a MCI debug message seems i got a message like this ath: phy0: BT_Status_Update: is_link=0, linkId=2, state=1, SEQ=-2085766476 initially. Signed-off-by: Mohammed Shafi Shajakhan Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index c40e568b5c2b..64cc782587d8 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -348,7 +348,7 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) seq_num = *((u32 *)(rx_payload + 12)); ath_dbg(common, MCI, - "BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%d\n", + "BT_Status_Update: is_link=%d, linkId=%d, state=%d, SEQ=%u\n", profile_status.is_link, profile_status.conn_handle, profile_status.is_critical, seq_num); -- cgit v1.2.3 From 589ebd85268293e64ff6b7e0dd4ed8fb26811aa7 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 28 Jun 2012 02:33:40 +0200 Subject: ath9k: update AR934x initvals to latest version Generated using the initvals tool from the qca-swiss-army-knife repository from https://github.com/mcgrof/qca-swiss-army-knife Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9340_initvals.h | 475 ++++++++++++++--------- 1 file changed, 290 insertions(+), 185 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h index 815a8af1beef..210fd79c6a72 100644 --- a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,16 +19,16 @@ #define INITVALS_9340_H static const u32 ar9340_1p0_radio_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646800, 0xa4646800, 0xa4646800, 0xa4646800}, - {0x0001610c, 0x08000000, 0x08000000, 0x00000000, 0x00000000}, + {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016140, 0x10804000, 0x10804000, 0x50804000, 0x50804000}, - {0x0001650c, 0x08000000, 0x08000000, 0x00000000, 0x00000000}, + {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, {0x00016540, 0x10804000, 0x10804000, 0x50804000, 0x50804000}, }; static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, @@ -100,20 +101,20 @@ static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { }; static const u32 ar9340Modes_fast_clock_1p0[][3] = { - /* Addr 5G_HT20 5G_HT40 */ + /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, {0x000010b0, 0x00000fd0, 0x00001fa0}, {0x00008014, 0x044c044c, 0x08980898}, {0x0000801c, 0x148ec02b, 0x148ec057}, {0x00008318, 0x000044c0, 0x00008980}, - {0x00009e00, 0x03721821, 0x03721821}, + {0x00009e00, 0x0372131c, 0x0372131c}, {0x0000a230, 0x0000000b, 0x00000016}, {0x0000a254, 0x00000898, 0x00001130}, }; static const u32 ar9340_1p0_radio_core[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x00016000, 0x36db6db6}, {0x00016004, 0x6db6db40}, {0x00016008, 0x73f00000}, @@ -146,15 +147,13 @@ static const u32 ar9340_1p0_radio_core[][2] = { {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80000}, {0x00016108, 0x00080010}, - {0x0001610c, 0x00000000}, {0x00016140, 0x50804008}, {0x00016144, 0x01884080}, {0x00016148, 0x000080c0}, {0x00016280, 0x01000015}, - {0x00016284, 0x05530000}, + {0x00016284, 0x15530000}, {0x00016288, 0x00318000}, {0x0001628c, 0x50000000}, - {0x00016290, 0x4080294f}, {0x00016380, 0x00000000}, {0x00016384, 0x00000000}, {0x00016388, 0x00800700}, @@ -219,13 +218,14 @@ static const u32 ar9340_1p0_radio_core[][2] = { }; static const u32 ar9340_1p0_radio_core_40M[][2] = { + /* Addr allmodes */ {0x0001609c, 0x02566f3a}, {0x000160ac, 0xa4647c00}, {0x000160b0, 0x01885f5a}, }; static const u32 ar9340_1p0_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, @@ -237,34 +237,37 @@ static const u32 ar9340_1p0_mac_postamble[][5] = { }; static const u32 ar9340_1p0_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, }; static const u32 ar9340_1p0_baseband_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a022e, 0x206a022e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, - {0x00009c00, 0x00000044, 0x000000c4, 0x000000c4, 0x00000044}, - {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, - {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec88d2e, 0x7ec88d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e44, 0x02321e27, 0x02321e27, 0x02291e27, 0x02291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, - {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0}, + {0x0000a204, 0x00003ec0, 0x00003ec4, 0x00003ec4, 0x00003ec0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, @@ -277,11 +280,11 @@ static const u32 ar9340_1p0_baseband_postamble[][5] = { {0x0000a288, 0x00000220, 0x00000220, 0x00000110, 0x00000110}, {0x0000a28c, 0x00011111, 0x00011111, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071982}, - {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, + {0x0000a2d0, 0x00041983, 0x00041983, 0x00041982, 0x00041982}, + {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000ae04, 0x00180000, 0x00180000, 0x00180000, 0x00180000}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, @@ -289,21 +292,21 @@ static const u32 ar9340_1p0_baseband_postamble[][5] = { }; static const u32 ar9340_1p0_baseband_core[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x00009800, 0xafe68e30}, {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, - {0x00009814, 0xb280c00a}, + {0x00009814, 0x3280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, - {0x00009834, 0x5f3ca3de}, + {0x00009834, 0x6400a190}, {0x00009838, 0x0108ecff}, - {0x0000983c, 0x14750600}, + {0x0000983c, 0x14000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, - {0x000098b0, 0x52440bbe}, + {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, @@ -329,7 +332,6 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, - {0x00009e3c, 0xcf946222}, {0x00009e40, 0x0d261820}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, @@ -342,8 +344,6 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000a220, 0x00000000}, {0x0000a224, 0x00000000}, {0x0000a228, 0x10002310}, - {0x0000a22c, 0x01036a1e}, - {0x0000a234, 0x10000fff}, {0x0000a23c, 0x00000000}, {0x0000a244, 0x0c000000}, {0x0000a2a0, 0x00000001}, @@ -351,10 +351,6 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000a2c8, 0x00000000}, {0x0000a2cc, 0x18c43433}, {0x0000a2d4, 0x00000000}, - {0x0000a2dc, 0x00000000}, - {0x0000a2e0, 0x00000000}, - {0x0000a2e4, 0x00000000}, - {0x0000a2e8, 0x00000000}, {0x0000a2ec, 0x00000000}, {0x0000a2f0, 0x00000000}, {0x0000a2f4, 0x00000000}, @@ -385,7 +381,7 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, - {0x0000a3f4, 0x00000246}, + {0x0000a3f4, 0x00000000}, {0x0000a3f8, 0x0cdbd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, @@ -402,33 +398,17 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000a430, 0x1ce739ce}, {0x0000a434, 0x00000000}, {0x0000a438, 0x00001801}, - {0x0000a43c, 0x00000000}, + {0x0000a43c, 0x00100000}, {0x0000a440, 0x00000000}, {0x0000a444, 0x00000000}, - {0x0000a448, 0x04000080}, + {0x0000a448, 0x05000080}, {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a458, 0x00000000}, - {0x0000a600, 0x00000000}, - {0x0000a604, 0x00000000}, - {0x0000a608, 0x00000000}, - {0x0000a60c, 0x00000000}, - {0x0000a610, 0x00000000}, - {0x0000a614, 0x00000000}, - {0x0000a618, 0x00000000}, - {0x0000a61c, 0x00000000}, - {0x0000a620, 0x00000000}, - {0x0000a624, 0x00000000}, - {0x0000a628, 0x00000000}, - {0x0000a62c, 0x00000000}, - {0x0000a630, 0x00000000}, - {0x0000a634, 0x00000000}, - {0x0000a638, 0x00000000}, - {0x0000a63c, 0x00000000}, {0x0000a640, 0x00000000}, {0x0000a644, 0x3fad9d74}, {0x0000a648, 0x0048060a}, - {0x0000a64c, 0x00000637}, + {0x0000a64c, 0x00003c37}, {0x0000a670, 0x03020100}, {0x0000a674, 0x09080504}, {0x0000a678, 0x0d0c0b0a}, @@ -451,10 +431,6 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x0000a8f4, 0x00000000}, {0x0000b2d0, 0x00000080}, {0x0000b2d4, 0x00000000}, - {0x0000b2dc, 0x00000000}, - {0x0000b2e0, 0x00000000}, - {0x0000b2e4, 0x00000000}, - {0x0000b2e8, 0x00000000}, {0x0000b2ec, 0x00000000}, {0x0000b2f0, 0x00000000}, {0x0000b2f4, 0x00000000}, @@ -465,80 +441,108 @@ static const u32 ar9340_1p0_baseband_core[][2] = { }; static const u32 ar9340Modes_high_power_tx_gain_table_1p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, - {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, - {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, - {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, - {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, - {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, - {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, - {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, - {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, - {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, - {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, - {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, - {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, - {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, - {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, - {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a504, 0x04002222, 0x04002222, 0x02000001, 0x02000001}, + {0x0000a508, 0x09002421, 0x09002421, 0x05000003, 0x05000003}, + {0x0000a50c, 0x0d002621, 0x0d002621, 0x0a000005, 0x0a000005}, + {0x0000a510, 0x13004620, 0x13004620, 0x0e000201, 0x0e000201}, + {0x0000a514, 0x19004a20, 0x19004a20, 0x11000203, 0x11000203}, + {0x0000a518, 0x1d004e20, 0x1d004e20, 0x14000401, 0x14000401}, + {0x0000a51c, 0x21005420, 0x21005420, 0x18000403, 0x18000403}, + {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000602, 0x1b000602}, + {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000802, 0x1f000802}, + {0x0000a528, 0x2f005e42, 0x2f005e42, 0x21000620, 0x21000620}, + {0x0000a52c, 0x33005e44, 0x33005e44, 0x25000820, 0x25000820}, + {0x0000a530, 0x38005e65, 0x38005e65, 0x29000822, 0x29000822}, + {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2d000824, 0x2d000824}, + {0x0000a538, 0x40005e6b, 0x40005e6b, 0x30000828, 0x30000828}, + {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x3400082a, 0x3400082a}, + {0x0000a540, 0x49005e72, 0x49005e72, 0x38000849, 0x38000849}, + {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b000a2c, 0x3b000a2c}, + {0x0000a548, 0x53005f12, 0x53005f12, 0x3e000e2b, 0x3e000e2b}, + {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42000e2d, 0x42000e2d}, + {0x0000a550, 0x5e025f12, 0x5e025f12, 0x4500124a, 0x4500124a}, + {0x0000a554, 0x61027f12, 0x61027f12, 0x4900124c, 0x4900124c}, + {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c00126c, 0x4c00126c}, + {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x4f00128c, 0x4f00128c}, + {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x52001290, 0x52001290}, + {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, + {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, + {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, + {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, + {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, + {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, + {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001292, 0x56001292}, {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, - {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, - {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, - {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, - {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, - {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, - {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, - {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, - {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, - {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, - {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, - {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, - {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, - {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, - {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, - {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, - {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, - {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, - {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, - {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, - {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, - {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, - {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x00016044, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, + {0x0000a584, 0x04802222, 0x04802222, 0x02800001, 0x02800001}, + {0x0000a588, 0x09802421, 0x09802421, 0x05800003, 0x05800003}, + {0x0000a58c, 0x0d802621, 0x0d802621, 0x0a800005, 0x0a800005}, + {0x0000a590, 0x13804620, 0x13804620, 0x0e800201, 0x0e800201}, + {0x0000a594, 0x19804a20, 0x19804a20, 0x11800203, 0x11800203}, + {0x0000a598, 0x1d804e20, 0x1d804e20, 0x14800401, 0x14800401}, + {0x0000a59c, 0x21805420, 0x21805420, 0x18800403, 0x18800403}, + {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800602, 0x1b800602}, + {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800802, 0x1f800802}, + {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x21800620, 0x21800620}, + {0x0000a5ac, 0x33805e44, 0x33805e44, 0x25800820, 0x25800820}, + {0x0000a5b0, 0x38805e65, 0x38805e65, 0x29800822, 0x29800822}, + {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2d800824, 0x2d800824}, + {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x30800828, 0x30800828}, + {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x3480082a, 0x3480082a}, + {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38800849, 0x38800849}, + {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b800a2c, 0x3b800a2c}, + {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e800e2b, 0x3e800e2b}, + {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42800e2d, 0x42800e2d}, + {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x4580124a, 0x4580124a}, + {0x0000a5d4, 0x61827f12, 0x61827f12, 0x4980124c, 0x4980124c}, + {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c80126c, 0x4c80126c}, + {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x4f80128c, 0x4f80128c}, + {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x52801290, 0x52801290}, + {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801292, 0x56801292}, + {0x00016044, 0x056db2db, 0x056db2db, 0x022492db, 0x022492db}, {0x00016048, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, - {0x00016444, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, + {0x00016444, 0x056db2db, 0x056db2db, 0x022492db, 0x022492db}, {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, @@ -559,7 +563,7 @@ static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb5, 0x59025eb5, 0x42001a83, 0x42001a83}, + {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, @@ -604,13 +608,43 @@ static const u32 ar9340Modes_high_ob_db_tx_gain_table_1p0[][5] = { {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, - {0x00016048, 0x8e481266, 0x8e481266, 0x8e481266, 0x8e481266}, + {0x00016048, 0x8e481666, 0x8e481666, 0x8e481266, 0x8e481266}, + {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, {0x00016444, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, - {0x00016448, 0x8e481266, 0x8e481266, 0x8e481266, 0x8e481266}, + {0x00016448, 0x8e481666, 0x8e481666, 0x8e481266, 0x8e481266}, }; + static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a00ae, 0x206a00ae}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec82d2e, 0x7ec82d2e}, + {0x0000a2dc, 0xfef5d402, 0xfef5d402, 0xfdab5b52, 0xfdab5b52}, + {0x0000a2e0, 0xfe896600, 0xfe896600, 0xfd339c84, 0xfd339c84}, + {0x0000a2e4, 0xff01f800, 0xff01f800, 0xfec3e000, 0xfec3e000}, + {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffc0000, 0xfffc0000}, {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, @@ -676,15 +710,34 @@ static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x00016044, 0x036db2db, 0x036db2db, 0x036db2db, 0x036db2db}, - {0x00016048, 0x69b65266, 0x69b65266, 0x69b65266, 0x69b65266}, - {0x00016444, 0x036db2db, 0x036db2db, 0x036db2db, 0x036db2db}, - {0x00016448, 0x69b65266, 0x69b65266, 0x69b65266, 0x69b65266}, + {0x00016044, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, + {0x00016048, 0x8e480086, 0x8e480086, 0x8e480086, 0x8e480086}, + {0x00016444, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4, 0x03b6d2e4}, + {0x00016448, 0x8e480086, 0x8e480086, 0x8e480086, 0x8e480086}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0xfef5d402, 0xfef5d402, 0xfdab5b52, 0xfdab5b52}, + {0x0000b2e0, 0xfe896600, 0xfe896600, 0xfd339c84, 0xfd339c84}, + {0x0000b2e4, 0xff01f800, 0xff01f800, 0xfec3e000, 0xfec3e000}, + {0x0000b2e8, 0xfffe0000, 0xfffe0000, 0xfffc0000, 0xfffc0000}, }; - static const u32 ar9340Common_rx_gain_table_1p0[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, @@ -845,14 +898,14 @@ static const u32 ar9340Common_rx_gain_table_1p0[][2] = { {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, + {0x0000b080, 0x23232323}, + {0x0000b084, 0x21232323}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, {0x0000b0a0, 0x00000000}, {0x0000b0a4, 0x00000000}, {0x0000b0a8, 0x00000000}, @@ -944,7 +997,11 @@ static const u32 ar9340Common_rx_gain_table_1p0[][2] = { }; static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, @@ -952,8 +1009,8 @@ static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, + {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, + {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, @@ -965,19 +1022,19 @@ static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, + {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, @@ -1010,14 +1067,40 @@ static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { {0x0000a5f4, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5f8, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, {0x0000a5fc, 0x778a308c, 0x778a308c, 0x5d801eec, 0x5d801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, - {0x00016048, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, + {0x00016048, 0x24925666, 0x24925666, 0x24925266, 0x24925266}, + {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, + {0x00016288, 0xf0318000, 0xf0318000, 0xf0318000, 0xf0318000}, {0x00016444, 0x056db2db, 0x056db2db, 0x056db2db, 0x056db2db}, - {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, + {0x00016448, 0x24925666, 0x24925666, 0x24925266, 0x24925266}, }; static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, @@ -1025,8 +1108,8 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400}, - {0x0000a518, 0x21020220, 0x21020220, 0x15000402, 0x15000402}, - {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, + {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402}, + {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, {0x0000a520, 0x2b022220, 0x2b022220, 0x1b000603, 0x1b000603}, {0x0000a524, 0x2f022222, 0x2f022222, 0x1f000a02, 0x1f000a02}, {0x0000a528, 0x34022225, 0x34022225, 0x23000a04, 0x23000a04}, @@ -1038,19 +1121,19 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660}, {0x0000a544, 0x5302266c, 0x5302266c, 0x3b001861, 0x3b001861}, {0x0000a548, 0x5702286c, 0x5702286c, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x61042a6c, 0x61042a6c, 0x44001c84, 0x44001c84}, - {0x0000a554, 0x66062a6c, 0x66062a6c, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x7006308c, 0x7006308c, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x730a308a, 0x730a308a, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x770a308c, 0x770a308c, 0x56001eec, 0x56001eec}, + {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x61024a6c, 0x61024a6c, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x66026a6c, 0x66026a6c, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x7002708c, 0x7002708c, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x7302b08a, 0x7302b08a, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, @@ -1083,14 +1166,36 @@ static const u32 ar9340Modes_mixed_ob_db_tx_gain_table_1p0[][5] = { {0x0000a5f4, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5f8, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, {0x0000a5fc, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x056db2db, 0x056db2db, 0x03b6d2e4, 0x03b6d2e4}, - {0x00016048, 0x24927266, 0x24927266, 0x8e483266, 0x8e483266}, + {0x00016048, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, + {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, + {0x00016288, 0x30318000, 0x30318000, 0x00318000, 0x00318000}, {0x00016444, 0x056db2db, 0x056db2db, 0x03b6d2e4, 0x03b6d2e4}, - {0x00016448, 0x24927266, 0x24927266, 0x8e482266, 0x8e482266}, + {0x00016448, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, }; static const u32 ar9340_1p0_mac_core[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x00000008, 0x00000000}, {0x00000030, 0x00020085}, {0x00000034, 0x00000005}, @@ -1119,6 +1224,7 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x00008004, 0x00000000}, {0x00008008, 0x00000000}, {0x0000800c, 0x00000000}, + {0x00008010, 0x00080800}, {0x00008018, 0x00000000}, {0x00008020, 0x00000000}, {0x00008038, 0x00000000}, @@ -1146,7 +1252,7 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000080bc, 0x00000000}, {0x000080c0, 0x2a800000}, {0x000080c4, 0x06900168}, - {0x000080c8, 0x13881c20}, + {0x000080c8, 0x13881c22}, {0x000080cc, 0x01f40000}, {0x000080d0, 0x00252500}, {0x000080d4, 0x00a00000}, @@ -1250,11 +1356,11 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000083c4, 0x00000000}, {0x000083c8, 0x00000000}, {0x000083cc, 0x00000200}, - {0x000083d0, 0x000301ff}, + {0x000083d0, 0x000101ff}, }; static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, @@ -1514,12 +1620,11 @@ static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { }; static const u32 ar9340_1p0_soc_preamble[][2] = { - /* Addr allmodes */ - {0x000040a4, 0x00a0c1c9}, + /* Addr allmodes */ {0x00007008, 0x00000000}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, }; -#endif +#endif /* INITVALS_9340_H */ -- cgit v1.2.3 From 469979173ecda9c472c495608d6d540ec33ab85d Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 28 Jun 2012 02:33:41 +0200 Subject: ath9k: de-duplicate initvals The initvals tool from https://github.com/mcgrof/qca-swiss-army-knife has been modified to detect identical initval tables and replace them with macros. This patch contains the generated changes. On MIPS this reduces the binary size by 24 KB with no runtime changes. Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- .../net/wireless/ath/ath9k/ar9003_2p2_initvals.h | 1 + .../net/wireless/ath/ath9k/ar9330_1p1_initvals.h | 53 +- .../net/wireless/ath/ath9k/ar9330_1p2_initvals.h | 882 +---------- drivers/net/wireless/ath/ath9k/ar9340_initvals.h | 290 +--- .../net/wireless/ath/ath9k/ar9462_2p0_initvals.h | 5 +- drivers/net/wireless/ath/ath9k/ar9485_initvals.h | 1528 ++++++++------------ .../net/wireless/ath/ath9k/ar9580_1p0_initvals.h | 772 +--------- 7 files changed, 652 insertions(+), 2879 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h index 952cb2b4656b..89bf94d4d8a1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h index 1bd3a3d22101..6e1756bc3833 100644 --- a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h @@ -337,12 +337,7 @@ static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = { {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, }; -static const u32 ar9331_1p1_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; +#define ar9331_1p1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 static const u32 ar9331_1p1_xtal_25M[][2] = { /* Addr allmodes */ @@ -783,17 +778,7 @@ static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = { {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, }; -static const u32 ar9331_1p1_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; +#define ar9331_1p1_mac_postamble ar9300_2p2_mac_postamble static const u32 ar9331_1p1_soc_preamble[][2] = { /* Addr allmodes */ @@ -1112,38 +1097,4 @@ static const u32 ar9331_common_tx_gain_offset1_1[][1] = { {0x00000000}, }; -static const u32 ar9331_1p1_chansel_xtal_25M[] = { - 0x0101479e, - 0x0101d027, - 0x010258af, - 0x0102e138, - 0x010369c0, - 0x0103f249, - 0x01047ad1, - 0x0105035a, - 0x01058be2, - 0x0106146b, - 0x01069cf3, - 0x0107257c, - 0x0107ae04, - 0x0108f5b2, -}; - -static const u32 ar9331_1p1_chansel_xtal_40M[] = { - 0x00a0ccbe, - 0x00a12213, - 0x00a17769, - 0x00a1ccbe, - 0x00a22213, - 0x00a27769, - 0x00a2ccbe, - 0x00a32213, - 0x00a37769, - 0x00a3ccbe, - 0x00a42213, - 0x00a47769, - 0x00a4ccbe, - 0x00a5998b, -}; - #endif /* INITVALS_9330_1P1_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h index 0e6ca0834b34..57ed8a112173 100644 --- a/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,8 +18,8 @@ #ifndef INITVALS_9330_1P2_H #define INITVALS_9330_1P2_H -static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p2[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ +static const u32 ar9331_modes_high_ob_db_tx_gain_1p2[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, @@ -102,8 +103,14 @@ static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p2[][5] = { {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, }; +#define ar9331_modes_high_power_tx_gain_1p2 ar9331_modes_high_ob_db_tx_gain_1p2 + +#define ar9331_modes_low_ob_db_tx_gain_1p2 ar9331_modes_high_power_tx_gain_1p2 + +#define ar9331_modes_lowest_ob_db_tx_gain_1p2 ar9331_modes_low_ob_db_tx_gain_1p2 + static const u32 ar9331_1p2_baseband_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, @@ -147,191 +154,6 @@ static const u32 ar9331_1p2_baseband_postamble[][5] = { {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9331_modes_high_ob_db_tx_gain_1p2[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, - {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, - {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, - {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, - {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, - {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, - {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, - {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, - {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, - {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, - {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, - {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, - {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, - {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, - {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, - {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, - {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, - {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, - {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, - {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, - {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, - {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, - {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, - {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, - {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501}, - {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, - {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802}, - {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03}, - {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, -}; - -static const u32 ar9331_modes_low_ob_db_tx_gain_1p2[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, - {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, - {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, - {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, - {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, - {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, - {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, - {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, - {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, - {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, - {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, - {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, - {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, - {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, - {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, - {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, - {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, - {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, - {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, - {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, - {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, - {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, - {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, - {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, - {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501}, - {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, - {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802}, - {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03}, - {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, -}; - -static const u32 ar9331_1p2_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; - -static const u32 ar9331_1p2_xtal_25M[][2] = { - /* Addr allmodes */ - {0x00007038, 0x000002f8}, - {0x00008244, 0x0010f3d7}, - {0x0000824c, 0x0001e7ae}, - {0x0001609c, 0x0f508f29}, -}; - static const u32 ar9331_1p2_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, @@ -397,684 +219,24 @@ static const u32 ar9331_1p2_radio_core[][2] = { {0x000163d4, 0x00000000}, }; -static const u32 ar9331_1p2_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022}, -}; +#define ar9331_1p2_baseband_core_txfir_coeff_japan_2484 ar9331_1p1_baseband_core_txfir_coeff_japan_2484 -static const u32 ar9331_common_wo_xlna_rx_gain_1p2[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00060005}, - {0x0000a004, 0x00810080}, - {0x0000a008, 0x00830082}, - {0x0000a00c, 0x00850084}, - {0x0000a010, 0x01820181}, - {0x0000a014, 0x01840183}, - {0x0000a018, 0x01880185}, - {0x0000a01c, 0x018a0189}, - {0x0000a020, 0x02850284}, - {0x0000a024, 0x02890288}, - {0x0000a028, 0x028b028a}, - {0x0000a02c, 0x03850384}, - {0x0000a030, 0x03890388}, - {0x0000a034, 0x038b038a}, - {0x0000a038, 0x038d038c}, - {0x0000a03c, 0x03910390}, - {0x0000a040, 0x03930392}, - {0x0000a044, 0x03950394}, - {0x0000a048, 0x00000396}, - {0x0000a04c, 0x00000000}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x28282828}, - {0x0000a084, 0x28282828}, - {0x0000a088, 0x28282828}, - {0x0000a08c, 0x28282828}, - {0x0000a090, 0x28282828}, - {0x0000a094, 0x24242428}, - {0x0000a098, 0x171e1e1e}, - {0x0000a09c, 0x02020b0b}, - {0x0000a0a0, 0x02020202}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x22072208}, - {0x0000a0c4, 0x22052206}, - {0x0000a0c8, 0x22032204}, - {0x0000a0cc, 0x22012202}, - {0x0000a0d0, 0x221f2200}, - {0x0000a0d4, 0x221d221e}, - {0x0000a0d8, 0x33023303}, - {0x0000a0dc, 0x33003301}, - {0x0000a0e0, 0x331e331f}, - {0x0000a0e4, 0x4402331d}, - {0x0000a0e8, 0x44004401}, - {0x0000a0ec, 0x441e441f}, - {0x0000a0f0, 0x55025503}, - {0x0000a0f4, 0x55005501}, - {0x0000a0f8, 0x551e551f}, - {0x0000a0fc, 0x6602551d}, - {0x0000a100, 0x66006601}, - {0x0000a104, 0x661e661f}, - {0x0000a108, 0x7703661d}, - {0x0000a10c, 0x77017702}, - {0x0000a110, 0x00007700}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x111f1100}, - {0x0000a148, 0x111d111e}, - {0x0000a14c, 0x111b111c}, - {0x0000a150, 0x22032204}, - {0x0000a154, 0x22012202}, - {0x0000a158, 0x221f2200}, - {0x0000a15c, 0x221d221e}, - {0x0000a160, 0x33013302}, - {0x0000a164, 0x331f3300}, - {0x0000a168, 0x4402331e}, - {0x0000a16c, 0x44004401}, - {0x0000a170, 0x441e441f}, - {0x0000a174, 0x55015502}, - {0x0000a178, 0x551f5500}, - {0x0000a17c, 0x6602551e}, - {0x0000a180, 0x66006601}, - {0x0000a184, 0x661e661f}, - {0x0000a188, 0x7703661d}, - {0x0000a18c, 0x77017702}, - {0x0000a190, 0x00007700}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000296}, -}; +#define ar9331_1p2_xtal_25M ar9331_1p1_xtal_25M -static const u32 ar9331_1p2_baseband_core[][2] = { - /* Addr allmodes */ - {0x00009800, 0xafe68e30}, - {0x00009804, 0xfd14e000}, - {0x00009808, 0x9c0a8f6b}, - {0x0000980c, 0x04800000}, - {0x00009814, 0x9280c00a}, - {0x00009818, 0x00000000}, - {0x0000981c, 0x00020028}, - {0x00009834, 0x5f3ca3de}, - {0x00009838, 0x0108ecff}, - {0x0000983c, 0x14750600}, - {0x00009880, 0x201fff00}, - {0x00009884, 0x00001042}, - {0x000098a4, 0x00200400}, - {0x000098b0, 0x32840bbe}, - {0x000098d0, 0x004b6a8e}, - {0x000098d4, 0x00000820}, - {0x000098dc, 0x00000000}, - {0x000098f0, 0x00000000}, - {0x000098f4, 0x00000000}, - {0x00009c04, 0x00000000}, - {0x00009c08, 0x03200000}, - {0x00009c0c, 0x00000000}, - {0x00009c10, 0x00000000}, - {0x00009c14, 0x00046384}, - {0x00009c18, 0x05b6b440}, - {0x00009c1c, 0x00b6b440}, - {0x00009d00, 0xc080a333}, - {0x00009d04, 0x40206c10}, - {0x00009d08, 0x009c4060}, - {0x00009d0c, 0x1883800a}, - {0x00009d10, 0x01834061}, - {0x00009d14, 0x00c00400}, - {0x00009d18, 0x00000000}, - {0x00009e08, 0x0038233c}, - {0x00009e24, 0x9927b515}, - {0x00009e28, 0x12ef0200}, - {0x00009e30, 0x06336f77}, - {0x00009e34, 0x6af6532f}, - {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x0d261820}, - {0x00009e4c, 0x00001004}, - {0x00009e50, 0x00ff03f1}, - {0x00009fc0, 0x803e4788}, - {0x00009fc4, 0x0001efb5}, - {0x00009fcc, 0x40000014}, - {0x0000a20c, 0x00000000}, - {0x0000a220, 0x00000000}, - {0x0000a224, 0x00000000}, - {0x0000a228, 0x10002310}, - {0x0000a23c, 0x00000000}, - {0x0000a244, 0x0c000000}, - {0x0000a2a0, 0x00000001}, - {0x0000a2c0, 0x00000001}, - {0x0000a2c8, 0x00000000}, - {0x0000a2cc, 0x18c43433}, - {0x0000a2d4, 0x00000000}, - {0x0000a2dc, 0x00000000}, - {0x0000a2e0, 0x00000000}, - {0x0000a2e4, 0x00000000}, - {0x0000a2e8, 0x00000000}, - {0x0000a2ec, 0x00000000}, - {0x0000a2f0, 0x00000000}, - {0x0000a2f4, 0x00000000}, - {0x0000a2f8, 0x00000000}, - {0x0000a344, 0x00000000}, - {0x0000a34c, 0x00000000}, - {0x0000a350, 0x0000a000}, - {0x0000a364, 0x00000000}, - {0x0000a370, 0x00000000}, - {0x0000a390, 0x00000001}, - {0x0000a394, 0x00000444}, - {0x0000a398, 0x001f0e0f}, - {0x0000a39c, 0x0075393f}, - {0x0000a3a0, 0xb79f6427}, - {0x0000a3a4, 0x00000000}, - {0x0000a3a8, 0xaaaaaaaa}, - {0x0000a3ac, 0x3c466478}, - {0x0000a3c0, 0x20202020}, - {0x0000a3c4, 0x22222220}, - {0x0000a3c8, 0x20200020}, - {0x0000a3cc, 0x20202020}, - {0x0000a3d0, 0x20202020}, - {0x0000a3d4, 0x20202020}, - {0x0000a3d8, 0x20202020}, - {0x0000a3dc, 0x20202020}, - {0x0000a3e0, 0x20202020}, - {0x0000a3e4, 0x20202020}, - {0x0000a3e8, 0x20202020}, - {0x0000a3ec, 0x20202020}, - {0x0000a3f0, 0x00000000}, - {0x0000a3f4, 0x00000006}, - {0x0000a3f8, 0x0cdbd380}, - {0x0000a3fc, 0x000f0f01}, - {0x0000a400, 0x8fa91f01}, - {0x0000a404, 0x00000000}, - {0x0000a408, 0x0e79e5c6}, - {0x0000a40c, 0x00820820}, - {0x0000a414, 0x1ce739ce}, - {0x0000a418, 0x2d001dce}, - {0x0000a41c, 0x1ce739ce}, - {0x0000a420, 0x000001ce}, - {0x0000a424, 0x1ce739ce}, - {0x0000a428, 0x000001ce}, - {0x0000a42c, 0x1ce739ce}, - {0x0000a430, 0x1ce739ce}, - {0x0000a434, 0x00000000}, - {0x0000a438, 0x00001801}, - {0x0000a43c, 0x00000000}, - {0x0000a440, 0x00000000}, - {0x0000a444, 0x00000000}, - {0x0000a448, 0x04000000}, - {0x0000a44c, 0x00000001}, - {0x0000a450, 0x00010000}, - {0x0000a458, 0x00000000}, - {0x0000a640, 0x00000000}, - {0x0000a644, 0x3fad9d74}, - {0x0000a648, 0x0048060a}, - {0x0000a64c, 0x00003c37}, - {0x0000a670, 0x03020100}, - {0x0000a674, 0x09080504}, - {0x0000a678, 0x0d0c0b0a}, - {0x0000a67c, 0x13121110}, - {0x0000a680, 0x31301514}, - {0x0000a684, 0x35343332}, - {0x0000a688, 0x00000036}, - {0x0000a690, 0x00000838}, - {0x0000a7c0, 0x00000000}, - {0x0000a7c4, 0xfffffffc}, - {0x0000a7c8, 0x00000000}, - {0x0000a7cc, 0x00000000}, - {0x0000a7d0, 0x00000000}, - {0x0000a7d4, 0x00000004}, - {0x0000a7dc, 0x00000001}, -}; +#define ar9331_1p2_xtal_40M ar9331_1p1_xtal_40M -static const u32 ar9331_modes_high_power_tx_gain_1p2[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x3f001620, 0x3f001620}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x41001621, 0x41001621}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x44001640, 0x44001640}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x46001641, 0x46001641}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x48001642, 0x48001642}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4b001644, 0x4b001644}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4e001a81, 0x4e001a81}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x51001a83, 0x51001a83}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x54001c84, 0x54001c84}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x57001ce3, 0x57001ce3}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5b001ce5, 0x5b001ce5}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5f001ce9, 0x5f001ce9}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001eec, 0x66001eec}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x66001eec, 0x66001eec}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001eec, 0x66001eec}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001eec, 0x66001eec}, - {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, - {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, - {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, - {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, - {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, - {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, - {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, - {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, - {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, - {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, - {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, - {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, - {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, - {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, - {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, - {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, - {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, - {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, - {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, - {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, - {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, - {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, - {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, - {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, - {0x0000a618, 0x02008501, 0x02008501, 0x02008501, 0x02008501}, - {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, - {0x0000a620, 0x0300c802, 0x0300c802, 0x0300c802, 0x0300c802}, - {0x0000a624, 0x0300cc03, 0x0300cc03, 0x0300cc03, 0x0300cc03}, - {0x0000a628, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a62c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a630, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a634, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a638, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, - {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, -}; +#define ar9331_1p2_baseband_core ar9331_1p1_baseband_core -static const u32 ar9331_1p2_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; +#define ar9331_1p2_soc_postamble ar9331_1p1_soc_postamble -static const u32 ar9331_1p2_soc_preamble[][2] = { - /* Addr allmodes */ - {0x00007020, 0x00000000}, - {0x00007034, 0x00000002}, - {0x00007038, 0x000002f8}, -}; +#define ar9331_1p2_mac_postamble ar9331_1p1_mac_postamble -static const u32 ar9331_1p2_xtal_40M[][2] = { - /* Addr allmodes */ - {0x00007038, 0x000004c2}, - {0x00008244, 0x0010f400}, - {0x0000824c, 0x0001e800}, - {0x0001609c, 0x0b283f31}, -}; +#define ar9331_1p2_soc_preamble ar9331_1p1_soc_preamble -static const u32 ar9331_1p2_mac_core[][2] = { - /* Addr allmodes */ - {0x00000008, 0x00000000}, - {0x00000030, 0x00020085}, - {0x00000034, 0x00000005}, - {0x00000040, 0x00000000}, - {0x00000044, 0x00000000}, - {0x00000048, 0x00000008}, - {0x0000004c, 0x00000010}, - {0x00000050, 0x00000000}, - {0x00001040, 0x002ffc0f}, - {0x00001044, 0x002ffc0f}, - {0x00001048, 0x002ffc0f}, - {0x0000104c, 0x002ffc0f}, - {0x00001050, 0x002ffc0f}, - {0x00001054, 0x002ffc0f}, - {0x00001058, 0x002ffc0f}, - {0x0000105c, 0x002ffc0f}, - {0x00001060, 0x002ffc0f}, - {0x00001064, 0x002ffc0f}, - {0x000010f0, 0x00000100}, - {0x00001270, 0x00000000}, - {0x000012b0, 0x00000000}, - {0x000012f0, 0x00000000}, - {0x0000143c, 0x00000000}, - {0x0000147c, 0x00000000}, - {0x00008000, 0x00000000}, - {0x00008004, 0x00000000}, - {0x00008008, 0x00000000}, - {0x0000800c, 0x00000000}, - {0x00008018, 0x00000000}, - {0x00008020, 0x00000000}, - {0x00008038, 0x00000000}, - {0x0000803c, 0x00000000}, - {0x00008040, 0x00000000}, - {0x00008044, 0x00000000}, - {0x00008048, 0x00000000}, - {0x0000804c, 0xffffffff}, - {0x00008054, 0x00000000}, - {0x00008058, 0x00000000}, - {0x0000805c, 0x000fc78f}, - {0x00008060, 0x0000000f}, - {0x00008064, 0x00000000}, - {0x00008070, 0x00000310}, - {0x00008074, 0x00000020}, - {0x00008078, 0x00000000}, - {0x0000809c, 0x0000000f}, - {0x000080a0, 0x00000000}, - {0x000080a4, 0x02ff0000}, - {0x000080a8, 0x0e070605}, - {0x000080ac, 0x0000000d}, - {0x000080b0, 0x00000000}, - {0x000080b4, 0x00000000}, - {0x000080b8, 0x00000000}, - {0x000080bc, 0x00000000}, - {0x000080c0, 0x2a800000}, - {0x000080c4, 0x06900168}, - {0x000080c8, 0x13881c20}, - {0x000080cc, 0x01f40000}, - {0x000080d0, 0x00252500}, - {0x000080d4, 0x00a00000}, - {0x000080d8, 0x00400000}, - {0x000080dc, 0x00000000}, - {0x000080e0, 0xffffffff}, - {0x000080e4, 0x0000ffff}, - {0x000080e8, 0x3f3f3f3f}, - {0x000080ec, 0x00000000}, - {0x000080f0, 0x00000000}, - {0x000080f4, 0x00000000}, - {0x000080fc, 0x00020000}, - {0x00008100, 0x00000000}, - {0x00008108, 0x00000052}, - {0x0000810c, 0x00000000}, - {0x00008110, 0x00000000}, - {0x00008114, 0x000007ff}, - {0x00008118, 0x000000aa}, - {0x0000811c, 0x00003210}, - {0x00008124, 0x00000000}, - {0x00008128, 0x00000000}, - {0x0000812c, 0x00000000}, - {0x00008130, 0x00000000}, - {0x00008134, 0x00000000}, - {0x00008138, 0x00000000}, - {0x0000813c, 0x0000ffff}, - {0x00008144, 0xffffffff}, - {0x00008168, 0x00000000}, - {0x0000816c, 0x00000000}, - {0x00008170, 0x18486200}, - {0x00008174, 0x33332210}, - {0x00008178, 0x00000000}, - {0x0000817c, 0x00020000}, - {0x000081c0, 0x00000000}, - {0x000081c4, 0x33332210}, - {0x000081c8, 0x00000000}, - {0x000081cc, 0x00000000}, - {0x000081d4, 0x00000000}, - {0x000081ec, 0x00000000}, - {0x000081f0, 0x00000000}, - {0x000081f4, 0x00000000}, - {0x000081f8, 0x00000000}, - {0x000081fc, 0x00000000}, - {0x00008240, 0x00100000}, - {0x00008248, 0x00000800}, - {0x00008250, 0x00000000}, - {0x00008254, 0x00000000}, - {0x00008258, 0x00000000}, - {0x0000825c, 0x40000000}, - {0x00008260, 0x00080922}, - {0x00008264, 0x9d400010}, - {0x00008268, 0xffffffff}, - {0x0000826c, 0x0000ffff}, - {0x00008270, 0x00000000}, - {0x00008274, 0x40000000}, - {0x00008278, 0x003e4180}, - {0x0000827c, 0x00000004}, - {0x00008284, 0x0000002c}, - {0x00008288, 0x0000002c}, - {0x0000828c, 0x000000ff}, - {0x00008294, 0x00000000}, - {0x00008298, 0x00000000}, - {0x0000829c, 0x00000000}, - {0x00008300, 0x00000140}, - {0x00008314, 0x00000000}, - {0x0000831c, 0x0000010d}, - {0x00008328, 0x00000000}, - {0x0000832c, 0x00000007}, - {0x00008330, 0x00000302}, - {0x00008334, 0x00000700}, - {0x00008338, 0x00ff0000}, - {0x0000833c, 0x02400000}, - {0x00008340, 0x000107ff}, - {0x00008344, 0xaa48105b}, - {0x00008348, 0x008f0000}, - {0x0000835c, 0x00000000}, - {0x00008360, 0xffffffff}, - {0x00008364, 0xffffffff}, - {0x00008368, 0x00000000}, - {0x00008370, 0x00000000}, - {0x00008374, 0x000000ff}, - {0x00008378, 0x00000000}, - {0x0000837c, 0x00000000}, - {0x00008380, 0xffffffff}, - {0x00008384, 0xffffffff}, - {0x00008390, 0xffffffff}, - {0x00008394, 0xffffffff}, - {0x00008398, 0x00000000}, - {0x0000839c, 0x00000000}, - {0x000083a0, 0x00000000}, - {0x000083a4, 0x0000fa14}, - {0x000083a8, 0x000f0c00}, - {0x000083ac, 0x33332210}, - {0x000083b0, 0x33332210}, - {0x000083b4, 0x33332210}, - {0x000083b8, 0x33332210}, - {0x000083bc, 0x00000000}, - {0x000083c0, 0x00000000}, - {0x000083c4, 0x00000000}, - {0x000083c8, 0x00000000}, - {0x000083cc, 0x00000200}, - {0x000083d0, 0x000301ff}, -}; +#define ar9331_1p2_mac_core ar9331_1p1_mac_core -static const u32 ar9331_common_rx_gain_1p2[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x01800082}, - {0x0000a014, 0x01820181}, - {0x0000a018, 0x01840183}, - {0x0000a01c, 0x01880185}, - {0x0000a020, 0x018a0189}, - {0x0000a024, 0x02850284}, - {0x0000a028, 0x02890288}, - {0x0000a02c, 0x03850384}, - {0x0000a030, 0x03890388}, - {0x0000a034, 0x038b038a}, - {0x0000a038, 0x038d038c}, - {0x0000a03c, 0x03910390}, - {0x0000a040, 0x03930392}, - {0x0000a044, 0x03950394}, - {0x0000a048, 0x00000396}, - {0x0000a04c, 0x00000000}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x28282828}, - {0x0000a084, 0x28282828}, - {0x0000a088, 0x28282828}, - {0x0000a08c, 0x28282828}, - {0x0000a090, 0x28282828}, - {0x0000a094, 0x21212128}, - {0x0000a098, 0x171c1c1c}, - {0x0000a09c, 0x02020212}, - {0x0000a0a0, 0x00000202}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x111f1100}, - {0x0000a0c8, 0x111d111e}, - {0x0000a0cc, 0x111b111c}, - {0x0000a0d0, 0x22032204}, - {0x0000a0d4, 0x22012202}, - {0x0000a0d8, 0x221f2200}, - {0x0000a0dc, 0x221d221e}, - {0x0000a0e0, 0x33013302}, - {0x0000a0e4, 0x331f3300}, - {0x0000a0e8, 0x4402331e}, - {0x0000a0ec, 0x44004401}, - {0x0000a0f0, 0x441e441f}, - {0x0000a0f4, 0x55015502}, - {0x0000a0f8, 0x551f5500}, - {0x0000a0fc, 0x6602551e}, - {0x0000a100, 0x66006601}, - {0x0000a104, 0x661e661f}, - {0x0000a108, 0x7703661d}, - {0x0000a10c, 0x77017702}, - {0x0000a110, 0x00007700}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x111f1100}, - {0x0000a148, 0x111d111e}, - {0x0000a14c, 0x111b111c}, - {0x0000a150, 0x22032204}, - {0x0000a154, 0x22012202}, - {0x0000a158, 0x221f2200}, - {0x0000a15c, 0x221d221e}, - {0x0000a160, 0x33013302}, - {0x0000a164, 0x331f3300}, - {0x0000a168, 0x4402331e}, - {0x0000a16c, 0x44004401}, - {0x0000a170, 0x441e441f}, - {0x0000a174, 0x55015502}, - {0x0000a178, 0x551f5500}, - {0x0000a17c, 0x6602551e}, - {0x0000a180, 0x66006601}, - {0x0000a184, 0x661e661f}, - {0x0000a188, 0x7703661d}, - {0x0000a18c, 0x77017702}, - {0x0000a190, 0x00007700}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000296}, -}; +#define ar9331_common_wo_xlna_rx_gain_1p2 ar9331_common_wo_xlna_rx_gain_1p1 + +#define ar9331_common_rx_gain_1p2 ar9485_common_rx_gain_1_1 #endif /* INITVALS_9330_1P2_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h index 210fd79c6a72..1d8235e19f0f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h @@ -100,18 +100,7 @@ static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -static const u32 ar9340Modes_fast_clock_1p0[][3] = { - /* Addr 5G_HT20 5G_HT40 */ - {0x00001030, 0x00000268, 0x000004d0}, - {0x00001070, 0x0000018c, 0x00000318}, - {0x000010b0, 0x00000fd0, 0x00001fa0}, - {0x00008014, 0x044c044c, 0x08980898}, - {0x0000801c, 0x148ec02b, 0x148ec057}, - {0x00008318, 0x000044c0, 0x00008980}, - {0x00009e00, 0x0372131c, 0x0372131c}, - {0x0000a230, 0x0000000b, 0x00000016}, - {0x0000a254, 0x00000898, 0x00001130}, -}; +#define ar9340Modes_fast_clock_1p0 ar9300Modes_fast_clock_2p2 static const u32 ar9340_1p0_radio_core[][2] = { /* Addr allmodes */ @@ -224,22 +213,9 @@ static const u32 ar9340_1p0_radio_core_40M[][2] = { {0x000160b0, 0x01885f5a}, }; -static const u32 ar9340_1p0_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; +#define ar9340_1p0_mac_postamble ar9300_2p2_mac_postamble -static const u32 ar9340_1p0_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, -}; +#define ar9340_1p0_soc_postamble ar9300_2p2_soc_postamble static const u32 ar9340_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ @@ -1359,265 +1335,7 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000083d0, 0x000101ff}, }; -static const u32 ar9340Common_wo_xlna_rx_gain_table_1p0[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; +#define ar9340Common_wo_xlna_rx_gain_table_1p0 ar9300Common_wo_xlna_rx_gain_table_2p2 static const u32 ar9340_1p0_soc_preamble[][2] = { /* Addr allmodes */ diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index bbf48918a56c..4ef7dcccaa2f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2010 Atheros Communications Inc. + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1007,7 +1008,7 @@ static const u32 ar9462_2p0_radio_core[][2] = { static const u32 ar9462_2p0_soc_preamble[][2] = { /* Addr allmodes */ - {0x000040a4 ,0x00a0c1c9}, + {0x000040a4, 0x00a0c1c9}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index d16d029f81a9..fb4497fc7a3d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,599 +18,151 @@ #ifndef INITVALS_9485_H #define INITVALS_9485_H -static const u32 ar9485_1_1_mac_core[][2] = { - /* Addr allmodes */ - {0x00000008, 0x00000000}, - {0x00000030, 0x00020085}, - {0x00000034, 0x00000005}, - {0x00000040, 0x00000000}, - {0x00000044, 0x00000000}, - {0x00000048, 0x00000008}, - {0x0000004c, 0x00000010}, - {0x00000050, 0x00000000}, - {0x00001040, 0x002ffc0f}, - {0x00001044, 0x002ffc0f}, - {0x00001048, 0x002ffc0f}, - {0x0000104c, 0x002ffc0f}, - {0x00001050, 0x002ffc0f}, - {0x00001054, 0x002ffc0f}, - {0x00001058, 0x002ffc0f}, - {0x0000105c, 0x002ffc0f}, - {0x00001060, 0x002ffc0f}, - {0x00001064, 0x002ffc0f}, - {0x000010f0, 0x00000100}, - {0x00001270, 0x00000000}, - {0x000012b0, 0x00000000}, - {0x000012f0, 0x00000000}, - {0x0000143c, 0x00000000}, - {0x0000147c, 0x00000000}, - {0x00008000, 0x00000000}, - {0x00008004, 0x00000000}, - {0x00008008, 0x00000000}, - {0x0000800c, 0x00000000}, - {0x00008018, 0x00000000}, - {0x00008020, 0x00000000}, - {0x00008038, 0x00000000}, - {0x0000803c, 0x00000000}, - {0x00008040, 0x00000000}, - {0x00008044, 0x00000000}, - {0x00008048, 0x00000000}, - {0x0000804c, 0xffffffff}, - {0x00008054, 0x00000000}, - {0x00008058, 0x00000000}, - {0x0000805c, 0x000fc78f}, - {0x00008060, 0x0000000f}, - {0x00008064, 0x00000000}, - {0x00008070, 0x00000310}, - {0x00008074, 0x00000020}, - {0x00008078, 0x00000000}, - {0x0000809c, 0x0000000f}, - {0x000080a0, 0x00000000}, - {0x000080a4, 0x02ff0000}, - {0x000080a8, 0x0e070605}, - {0x000080ac, 0x0000000d}, - {0x000080b0, 0x00000000}, - {0x000080b4, 0x00000000}, - {0x000080b8, 0x00000000}, - {0x000080bc, 0x00000000}, - {0x000080c0, 0x2a800000}, - {0x000080c4, 0x06900168}, - {0x000080c8, 0x13881c22}, - {0x000080cc, 0x01f40000}, - {0x000080d0, 0x00252500}, - {0x000080d4, 0x00a00000}, - {0x000080d8, 0x00400000}, - {0x000080dc, 0x00000000}, - {0x000080e0, 0xffffffff}, - {0x000080e4, 0x0000ffff}, - {0x000080e8, 0x3f3f3f3f}, - {0x000080ec, 0x00000000}, - {0x000080f0, 0x00000000}, - {0x000080f4, 0x00000000}, - {0x000080fc, 0x00020000}, - {0x00008100, 0x00000000}, - {0x00008108, 0x00000052}, - {0x0000810c, 0x00000000}, - {0x00008110, 0x00000000}, - {0x00008114, 0x000007ff}, - {0x00008118, 0x000000aa}, - {0x0000811c, 0x00003210}, - {0x00008124, 0x00000000}, - {0x00008128, 0x00000000}, - {0x0000812c, 0x00000000}, - {0x00008130, 0x00000000}, - {0x00008134, 0x00000000}, - {0x00008138, 0x00000000}, - {0x0000813c, 0x0000ffff}, - {0x00008144, 0xffffffff}, - {0x00008168, 0x00000000}, - {0x0000816c, 0x00000000}, - {0x00008170, 0x18486200}, - {0x00008174, 0x33332210}, - {0x00008178, 0x00000000}, - {0x0000817c, 0x00020000}, - {0x000081c0, 0x00000000}, - {0x000081c4, 0x33332210}, - {0x000081d4, 0x00000000}, - {0x000081ec, 0x00000000}, - {0x000081f0, 0x00000000}, - {0x000081f4, 0x00000000}, - {0x000081f8, 0x00000000}, - {0x000081fc, 0x00000000}, - {0x00008240, 0x00100000}, - {0x00008244, 0x0010f400}, - {0x00008248, 0x00000800}, - {0x0000824c, 0x0001e800}, - {0x00008250, 0x00000000}, - {0x00008254, 0x00000000}, - {0x00008258, 0x00000000}, - {0x0000825c, 0x40000000}, - {0x00008260, 0x00080922}, - {0x00008264, 0x9ca00010}, - {0x00008268, 0xffffffff}, - {0x0000826c, 0x0000ffff}, - {0x00008270, 0x00000000}, - {0x00008274, 0x40000000}, - {0x00008278, 0x003e4180}, - {0x0000827c, 0x00000004}, - {0x00008284, 0x0000002c}, - {0x00008288, 0x0000002c}, - {0x0000828c, 0x000000ff}, - {0x00008294, 0x00000000}, - {0x00008298, 0x00000000}, - {0x0000829c, 0x00000000}, - {0x00008300, 0x00000140}, - {0x00008314, 0x00000000}, - {0x0000831c, 0x0000010d}, - {0x00008328, 0x00000000}, - {0x0000832c, 0x00000007}, - {0x00008330, 0x00000302}, - {0x00008334, 0x00000700}, - {0x00008338, 0x00ff0000}, - {0x0000833c, 0x02400000}, - {0x00008340, 0x000107ff}, - {0x00008344, 0xa248105b}, - {0x00008348, 0x008f0000}, - {0x0000835c, 0x00000000}, - {0x00008360, 0xffffffff}, - {0x00008364, 0xffffffff}, - {0x00008368, 0x00000000}, - {0x00008370, 0x00000000}, - {0x00008374, 0x000000ff}, - {0x00008378, 0x00000000}, - {0x0000837c, 0x00000000}, - {0x00008380, 0xffffffff}, - {0x00008384, 0xffffffff}, - {0x00008390, 0xffffffff}, - {0x00008394, 0xffffffff}, - {0x00008398, 0x00000000}, - {0x0000839c, 0x00000000}, - {0x000083a0, 0x00000000}, - {0x000083a4, 0x0000fa14}, - {0x000083a8, 0x000f0c00}, - {0x000083ac, 0x33332210}, - {0x000083b0, 0x33332210}, - {0x000083b4, 0x33332210}, - {0x000083b8, 0x33332210}, - {0x000083bc, 0x00000000}, - {0x000083c0, 0x00000000}, - {0x000083c4, 0x00000000}, - {0x000083c8, 0x00000000}, - {0x000083cc, 0x00000200}, - {0x000083d0, 0x000301ff}, -}; - -static const u32 ar9485_1_1_baseband_core[][2] = { - /* Addr allmodes */ - {0x00009800, 0xafe68e30}, - {0x00009804, 0xfd14e000}, - {0x00009808, 0x9c0a8f6b}, - {0x0000980c, 0x04800000}, - {0x00009814, 0x9280c00a}, - {0x00009818, 0x00000000}, - {0x0000981c, 0x00020028}, - {0x00009834, 0x5f3ca3de}, - {0x00009838, 0x0108ecff}, - {0x0000983c, 0x14750600}, - {0x00009880, 0x201fff00}, - {0x00009884, 0x00001042}, - {0x000098a4, 0x00200400}, - {0x000098b0, 0x52440bbe}, - {0x000098d0, 0x004b6a8e}, - {0x000098d4, 0x00000820}, - {0x000098dc, 0x00000000}, - {0x000098f0, 0x00000000}, - {0x000098f4, 0x00000000}, - {0x00009c04, 0x00000000}, - {0x00009c08, 0x03200000}, - {0x00009c0c, 0x00000000}, - {0x00009c10, 0x00000000}, - {0x00009c14, 0x00046384}, - {0x00009c18, 0x05b6b440}, - {0x00009c1c, 0x00b6b440}, - {0x00009d00, 0xc080a333}, - {0x00009d04, 0x40206c10}, - {0x00009d08, 0x009c4060}, - {0x00009d0c, 0x1883800a}, - {0x00009d10, 0x01834061}, - {0x00009d14, 0x00c00400}, - {0x00009d18, 0x00000000}, - {0x00009d1c, 0x00000000}, - {0x00009e08, 0x0038233c}, - {0x00009e24, 0x9927b515}, - {0x00009e28, 0x12ef0200}, - {0x00009e30, 0x06336f77}, - {0x00009e34, 0x6af6532f}, - {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x0d261820}, - {0x00009e4c, 0x00001004}, - {0x00009e50, 0x00ff03f1}, - {0x00009fc0, 0x80be4788}, - {0x00009fc4, 0x0001efb5}, - {0x00009fcc, 0x40000014}, - {0x0000a20c, 0x00000000}, - {0x0000a210, 0x00000000}, - {0x0000a220, 0x00000000}, - {0x0000a224, 0x00000000}, - {0x0000a228, 0x10002310}, - {0x0000a23c, 0x00000000}, - {0x0000a244, 0x0c000000}, - {0x0000a2a0, 0x00000001}, - {0x0000a2c0, 0x00000001}, - {0x0000a2c8, 0x00000000}, - {0x0000a2cc, 0x18c43433}, - {0x0000a2d4, 0x00000000}, - {0x0000a2dc, 0x00000000}, - {0x0000a2e0, 0x00000000}, - {0x0000a2e4, 0x00000000}, - {0x0000a2e8, 0x00000000}, - {0x0000a2ec, 0x00000000}, - {0x0000a2f0, 0x00000000}, - {0x0000a2f4, 0x00000000}, - {0x0000a2f8, 0x00000000}, - {0x0000a344, 0x00000000}, - {0x0000a34c, 0x00000000}, - {0x0000a350, 0x0000a000}, - {0x0000a364, 0x00000000}, - {0x0000a370, 0x00000000}, - {0x0000a390, 0x00000001}, - {0x0000a394, 0x00000444}, - {0x0000a398, 0x001f0e0f}, - {0x0000a39c, 0x0075393f}, - {0x0000a3a0, 0xb79f6427}, - {0x0000a3a4, 0x000000ff}, - {0x0000a3a8, 0x3b3b3b3b}, - {0x0000a3ac, 0x2f2f2f2f}, - {0x0000a3c0, 0x20202020}, - {0x0000a3c4, 0x22222220}, - {0x0000a3c8, 0x20200020}, - {0x0000a3cc, 0x20202020}, - {0x0000a3d0, 0x20202020}, - {0x0000a3d4, 0x20202020}, - {0x0000a3d8, 0x20202020}, - {0x0000a3dc, 0x20202020}, - {0x0000a3e0, 0x20202020}, - {0x0000a3e4, 0x20202020}, - {0x0000a3e8, 0x20202020}, - {0x0000a3ec, 0x20202020}, - {0x0000a3f0, 0x00000000}, - {0x0000a3f4, 0x00000006}, - {0x0000a3f8, 0x0cdbd380}, - {0x0000a3fc, 0x000f0f01}, - {0x0000a400, 0x8fa91f01}, - {0x0000a404, 0x00000000}, - {0x0000a408, 0x0e79e5c6}, - {0x0000a40c, 0x00820820}, - {0x0000a414, 0x1ce739cf}, - {0x0000a418, 0x2d0019ce}, - {0x0000a41c, 0x1ce739ce}, - {0x0000a420, 0x000001ce}, - {0x0000a424, 0x1ce739ce}, - {0x0000a428, 0x000001ce}, - {0x0000a42c, 0x1ce739ce}, - {0x0000a430, 0x1ce739ce}, - {0x0000a434, 0x00000000}, - {0x0000a438, 0x00001801}, - {0x0000a43c, 0x00000000}, - {0x0000a440, 0x00000000}, - {0x0000a444, 0x00000000}, - {0x0000a448, 0x04000000}, - {0x0000a44c, 0x00000001}, - {0x0000a450, 0x00010000}, - {0x0000a5c4, 0xbfad9d74}, - {0x0000a5c8, 0x0048060a}, - {0x0000a5cc, 0x00000637}, - {0x0000a760, 0x03020100}, - {0x0000a764, 0x09080504}, - {0x0000a768, 0x0d0c0b0a}, - {0x0000a76c, 0x13121110}, - {0x0000a770, 0x31301514}, - {0x0000a774, 0x35343332}, - {0x0000a778, 0x00000036}, - {0x0000a780, 0x00000838}, - {0x0000a7c0, 0x00000000}, - {0x0000a7c4, 0xfffffffc}, - {0x0000a7c8, 0x00000000}, - {0x0000a7cc, 0x00000000}, - {0x0000a7d0, 0x00000000}, - {0x0000a7d4, 0x00000004}, - {0x0000a7dc, 0x00000000}, -}; - -static const u32 ar9485Common_1_1[][2] = { - /* Addr allmodes */ - {0x00007010, 0x00000022}, - {0x00007020, 0x00000000}, - {0x00007034, 0x00000002}, - {0x00007038, 0x000004c2}, -}; - -static const u32 ar9485_1_1_baseband_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, - {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, - {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, - {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, - {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, - {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, - {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, - {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, - {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, - {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, - {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, - {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, - {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, - {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, - {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, - {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, - {0x0000a204, 0x01303fc0, 0x01303fc4, 0x01303fc4, 0x01303fc0}, - {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, - {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, - {0x0000a234, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff}, - {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, - {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, - {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, - {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, - {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, - {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, - {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, - {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, - {0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0}, - {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982}, - {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, - {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000be04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, - {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, -}; - -static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, - {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, - {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, -}; - -static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, - {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, - {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, -}; - -static const u32 ar9485_1_1_radio_postamble[][2] = { - /* Addr allmodes */ - {0x0001609c, 0x0b283f31}, - {0x000160ac, 0x24611800}, - {0x000160b0, 0x03284f3e}, - {0x0001610c, 0x00170000}, - {0x00016140, 0x50804008}, -}; - -static const u32 ar9485_1_1_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; +/* AR9485 1.0 */ -static const u32 ar9485_1_1_radio_core[][2] = { - /* Addr allmodes */ - {0x00016000, 0x36db6db6}, - {0x00016004, 0x6db6db40}, - {0x00016008, 0x73800000}, - {0x0001600c, 0x00000000}, - {0x00016040, 0x7f80fff8}, - {0x0001604c, 0x000f0278}, - {0x00016050, 0x4db6db8c}, - {0x00016054, 0x6db60000}, - {0x00016080, 0x00080000}, - {0x00016084, 0x0e48048c}, - {0x00016088, 0x14214514}, - {0x0001608c, 0x119f081e}, - {0x00016090, 0x24926490}, - {0x00016098, 0xd28b3330}, - {0x000160a0, 0xc2108ffe}, - {0x000160a4, 0x812fc370}, - {0x000160a8, 0x423c8000}, - {0x000160b4, 0x92480040}, - {0x000160c0, 0x006db6db}, - {0x000160c4, 0x0186db60}, - {0x000160c8, 0x6db6db6c}, - {0x000160cc, 0x6de6fbe0}, - {0x000160d0, 0xf7dfcf3c}, - {0x00016100, 0x04cb0001}, - {0x00016104, 0xfff80015}, - {0x00016108, 0x00080010}, - {0x00016144, 0x01884080}, - {0x00016148, 0x00008040}, - {0x00016240, 0x08400000}, - {0x00016244, 0x1bf90f00}, - {0x00016248, 0x00000000}, - {0x0001624c, 0x00000000}, - {0x00016280, 0x01000015}, - {0x00016284, 0x00d30000}, - {0x00016288, 0x00318000}, - {0x0001628c, 0x50000000}, - {0x00016290, 0x4b96210f}, - {0x00016380, 0x00000000}, - {0x00016384, 0x00000000}, - {0x00016388, 0x00800700}, - {0x0001638c, 0x00800700}, - {0x00016390, 0x00800700}, - {0x00016394, 0x00000000}, - {0x00016398, 0x00000000}, - {0x0001639c, 0x00000000}, - {0x000163a0, 0x00000001}, - {0x000163a4, 0x00000001}, - {0x000163a8, 0x00000000}, - {0x000163ac, 0x00000000}, - {0x000163b0, 0x00000000}, - {0x000163b4, 0x00000000}, - {0x000163b8, 0x00000000}, - {0x000163bc, 0x00000000}, - {0x000163c0, 0x000000a0}, - {0x000163c4, 0x000c0000}, - {0x000163c8, 0x14021402}, - {0x000163cc, 0x00001402}, - {0x000163d0, 0x00000000}, - {0x000163d4, 0x00000000}, - {0x00016c40, 0x13188278}, - {0x00016c44, 0x12000000}, -}; +#define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18052e5e}, +static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x18012e5e}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0000080c}, }; +static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00060005}, + {0x0000a004, 0x00810080}, + {0x0000a008, 0x00830082}, + {0x0000a00c, 0x00850084}, + {0x0000a010, 0x01820181}, + {0x0000a014, 0x01840183}, + {0x0000a018, 0x01880185}, + {0x0000a01c, 0x018a0189}, + {0x0000a020, 0x02850284}, + {0x0000a024, 0x02890288}, + {0x0000a028, 0x028b028a}, + {0x0000a02c, 0x03850384}, + {0x0000a030, 0x03890388}, + {0x0000a034, 0x038b038a}, + {0x0000a038, 0x038d038c}, + {0x0000a03c, 0x03910390}, + {0x0000a040, 0x03930392}, + {0x0000a044, 0x03950394}, + {0x0000a048, 0x00000396}, + {0x0000a04c, 0x00000000}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x28282828}, + {0x0000a084, 0x28282828}, + {0x0000a088, 0x28282828}, + {0x0000a08c, 0x28282828}, + {0x0000a090, 0x28282828}, + {0x0000a094, 0x24242428}, + {0x0000a098, 0x171e1e1e}, + {0x0000a09c, 0x02020b0b}, + {0x0000a0a0, 0x02020202}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x22072208}, + {0x0000a0c4, 0x22052206}, + {0x0000a0c8, 0x22032204}, + {0x0000a0cc, 0x22012202}, + {0x0000a0d0, 0x221f2200}, + {0x0000a0d4, 0x221d221e}, + {0x0000a0d8, 0x33023303}, + {0x0000a0dc, 0x33003301}, + {0x0000a0e0, 0x331e331f}, + {0x0000a0e4, 0x4402331d}, + {0x0000a0e8, 0x44004401}, + {0x0000a0ec, 0x441e441f}, + {0x0000a0f0, 0x55025503}, + {0x0000a0f4, 0x55005501}, + {0x0000a0f8, 0x551e551f}, + {0x0000a0fc, 0x6602551d}, + {0x0000a100, 0x66006601}, + {0x0000a104, 0x661e661f}, + {0x0000a108, 0x7703661d}, + {0x0000a10c, 0x77017702}, + {0x0000a110, 0x00007700}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x111f1100}, + {0x0000a148, 0x111d111e}, + {0x0000a14c, 0x111b111c}, + {0x0000a150, 0x22032204}, + {0x0000a154, 0x22012202}, + {0x0000a158, 0x221f2200}, + {0x0000a15c, 0x221d221e}, + {0x0000a160, 0x33013302}, + {0x0000a164, 0x331f3300}, + {0x0000a168, 0x4402331e}, + {0x0000a16c, 0x44004401}, + {0x0000a170, 0x441e441f}, + {0x0000a174, 0x55015502}, + {0x0000a178, 0x551f5500}, + {0x0000a17c, 0x6602551e}, + {0x0000a180, 0x66006601}, + {0x0000a184, 0x661e661f}, + {0x0000a188, 0x7703661d}, + {0x0000a18c, 0x77017702}, + {0x0000a190, 0x00007700}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000296}, +}; + static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -681,8 +234,14 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; +#define ar9485Modes_high_ob_db_tx_gain_1_1 ar9485Modes_high_power_tx_gain_1_1 + +#define ar9485Modes_low_ob_db_tx_gain_1_1 ar9485Modes_high_ob_db_tx_gain_1_1 + +#define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 + static const u32 ar9485_1_1[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x0000a580, 0x00000000}, {0x0000a584, 0x00000000}, {0x0000a588, 0x00000000}, @@ -701,194 +260,210 @@ static const u32 ar9485_1_1[][2] = { {0x0000a5bc, 0x00000000}, }; -static const u32 ar9485_modes_green_ob_db_tx_gain_1_1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, - {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, - {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x06000203, 0x06000203}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0a000401, 0x0a000401}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x0e000403, 0x0e000403}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x12000405, 0x12000405}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x15000604, 0x15000604}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x18000605, 0x18000605}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x1c000a04, 0x1c000a04}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x21000a06, 0x21000a06}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x29000a24, 0x29000a24}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x2f000e21, 0x2f000e21}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000e20, 0x31000e20}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x33000e20, 0x33000e20}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b50c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b510, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b514, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b518, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b51c, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b520, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b524, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b528, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, - {0x0000b52c, 0x0000002a, 0x0000002a, 0x0000002a, 0x0000002a}, - {0x0000b530, 0x0000003a, 0x0000003a, 0x0000003a, 0x0000003a}, - {0x0000b534, 0x0000004a, 0x0000004a, 0x0000004a, 0x0000004a}, - {0x0000b538, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b53c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b540, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b544, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b548, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b54c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b550, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b554, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b558, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b55c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b560, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b564, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b568, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b56c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b570, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b574, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b578, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x0000b57c, 0x0000005b, 0x0000005b, 0x0000005b, 0x0000005b}, - {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, - {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, -}; - -static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18013e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, -}; - -static const u32 ar9485_1_1_soc_preamble[][2] = { - /* Addr allmodes */ - {0x00004014, 0xba280400}, - {0x00004090, 0x00aa10aa}, - {0x000040a4, 0x00a0c9c9}, - {0x00007010, 0x00000022}, - {0x00007020, 0x00000000}, - {0x00007034, 0x00000002}, - {0x00007038, 0x000004c2}, - {0x00007048, 0x00000002}, -}; - -static const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; - -static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, - {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, - {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, -}; - -static const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { - /* Addr 5G_HT2 5G_HT40 */ - {0x00009e00, 0x03721821, 0x03721821}, - {0x0000a230, 0x0000400b, 0x00004016}, - {0x0000a254, 0x00000898, 0x00001130}, +static const u32 ar9485_1_1_radio_core[][2] = { + /* Addr allmodes */ + {0x00016000, 0x36db6db6}, + {0x00016004, 0x6db6db40}, + {0x00016008, 0x73800000}, + {0x0001600c, 0x00000000}, + {0x00016040, 0x7f80fff8}, + {0x0001604c, 0x000f0278}, + {0x00016050, 0x4db6db8c}, + {0x00016054, 0x6db60000}, + {0x00016080, 0x00080000}, + {0x00016084, 0x0e48048c}, + {0x00016088, 0x14214514}, + {0x0001608c, 0x119f081e}, + {0x00016090, 0x24926490}, + {0x00016098, 0xd28b3330}, + {0x000160a0, 0xc2108ffe}, + {0x000160a4, 0x812fc370}, + {0x000160a8, 0x423c8000}, + {0x000160b4, 0x92480040}, + {0x000160c0, 0x006db6db}, + {0x000160c4, 0x0186db60}, + {0x000160c8, 0x6db6db6c}, + {0x000160cc, 0x6de6fbe0}, + {0x000160d0, 0xf7dfcf3c}, + {0x00016100, 0x04cb0001}, + {0x00016104, 0xfff80015}, + {0x00016108, 0x00080010}, + {0x00016144, 0x01884080}, + {0x00016148, 0x00008040}, + {0x00016240, 0x08400000}, + {0x00016244, 0x1bf90f00}, + {0x00016248, 0x00000000}, + {0x0001624c, 0x00000000}, + {0x00016280, 0x01000015}, + {0x00016284, 0x00d30000}, + {0x00016288, 0x00318000}, + {0x0001628c, 0x50000000}, + {0x00016290, 0x4b96210f}, + {0x00016380, 0x00000000}, + {0x00016384, 0x00000000}, + {0x00016388, 0x00800700}, + {0x0001638c, 0x00800700}, + {0x00016390, 0x00800700}, + {0x00016394, 0x00000000}, + {0x00016398, 0x00000000}, + {0x0001639c, 0x00000000}, + {0x000163a0, 0x00000001}, + {0x000163a4, 0x00000001}, + {0x000163a8, 0x00000000}, + {0x000163ac, 0x00000000}, + {0x000163b0, 0x00000000}, + {0x000163b4, 0x00000000}, + {0x000163b8, 0x00000000}, + {0x000163bc, 0x00000000}, + {0x000163c0, 0x000000a0}, + {0x000163c4, 0x000c0000}, + {0x000163c8, 0x14021402}, + {0x000163cc, 0x00001402}, + {0x000163d0, 0x00000000}, + {0x000163d4, 0x00000000}, + {0x00016c40, 0x13188278}, + {0x00016c44, 0x12000000}, }; -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18012e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, +static const u32 ar9485_1_1_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a8f6b}, + {0x0000980c, 0x04800000}, + {0x00009814, 0x9280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x5f3ca3de}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x14750600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x52440bbe}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0x00000000}, + {0x00009c08, 0x03200000}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x1883800a}, + {0x00009d10, 0x01834061}, + {0x00009d14, 0x00c00400}, + {0x00009d18, 0x00000000}, + {0x00009d1c, 0x00000000}, + {0x00009e08, 0x0038233c}, + {0x00009e24, 0x9927b515}, + {0x00009e28, 0x12ef0200}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009fc0, 0x80be4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x0000a20c, 0x00000000}, + {0x0000a210, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a2a0, 0x00000001}, + {0x0000a2c0, 0x00000001}, + {0x0000a2c8, 0x00000000}, + {0x0000a2cc, 0x18c43433}, + {0x0000a2d4, 0x00000000}, + {0x0000a2dc, 0x00000000}, + {0x0000a2e0, 0x00000000}, + {0x0000a2e4, 0x00000000}, + {0x0000a2e8, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x000000ff}, + {0x0000a3a8, 0x3b3b3b3b}, + {0x0000a3ac, 0x2f2f2f2f}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000006}, + {0x0000a3f8, 0x0cdbd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce739cf}, + {0x0000a418, 0x2d0019ce}, + {0x0000a41c, 0x1ce739ce}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce739ce}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00000000}, + {0x0000a440, 0x00000000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x04000000}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a5c4, 0xbfad9d74}, + {0x0000a5c8, 0x0048060a}, + {0x0000a5cc, 0x00000637}, + {0x0000a760, 0x03020100}, + {0x0000a764, 0x09080504}, + {0x0000a768, 0x0d0c0b0a}, + {0x0000a76c, 0x13121110}, + {0x0000a770, 0x31301514}, + {0x0000a774, 0x35343332}, + {0x0000a778, 0x00000036}, + {0x0000a780, 0x00000838}, + {0x0000a7c0, 0x00000000}, + {0x0000a7c4, 0xfffffffc}, + {0x0000a7c8, 0x00000000}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000000}, }; static const u32 ar9485_common_rx_gain_1_1[][2] = { - /* Addr allmodes */ + /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, @@ -945,153 +520,14 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a0d4, 0x22012202}, {0x0000a0d8, 0x221f2200}, {0x0000a0dc, 0x221d221e}, - {0x0000a0e0, 0x33013302}, - {0x0000a0e4, 0x331f3300}, - {0x0000a0e8, 0x4402331e}, - {0x0000a0ec, 0x44004401}, - {0x0000a0f0, 0x441e441f}, - {0x0000a0f4, 0x55015502}, - {0x0000a0f8, 0x551f5500}, - {0x0000a0fc, 0x6602551e}, - {0x0000a100, 0x66006601}, - {0x0000a104, 0x661e661f}, - {0x0000a108, 0x7703661d}, - {0x0000a10c, 0x77017702}, - {0x0000a110, 0x00007700}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x111f1100}, - {0x0000a148, 0x111d111e}, - {0x0000a14c, 0x111b111c}, - {0x0000a150, 0x22032204}, - {0x0000a154, 0x22012202}, - {0x0000a158, 0x221f2200}, - {0x0000a15c, 0x221d221e}, - {0x0000a160, 0x33013302}, - {0x0000a164, 0x331f3300}, - {0x0000a168, 0x4402331e}, - {0x0000a16c, 0x44004401}, - {0x0000a170, 0x441e441f}, - {0x0000a174, 0x55015502}, - {0x0000a178, 0x551f5500}, - {0x0000a17c, 0x6602551e}, - {0x0000a180, 0x66006601}, - {0x0000a184, 0x661e661f}, - {0x0000a188, 0x7703661d}, - {0x0000a18c, 0x77017702}, - {0x0000a190, 0x00007700}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000296}, -}; - -static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18053e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, -}; - -static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00060005}, - {0x0000a004, 0x00810080}, - {0x0000a008, 0x00830082}, - {0x0000a00c, 0x00850084}, - {0x0000a010, 0x01820181}, - {0x0000a014, 0x01840183}, - {0x0000a018, 0x01880185}, - {0x0000a01c, 0x018a0189}, - {0x0000a020, 0x02850284}, - {0x0000a024, 0x02890288}, - {0x0000a028, 0x028b028a}, - {0x0000a02c, 0x03850384}, - {0x0000a030, 0x03890388}, - {0x0000a034, 0x038b038a}, - {0x0000a038, 0x038d038c}, - {0x0000a03c, 0x03910390}, - {0x0000a040, 0x03930392}, - {0x0000a044, 0x03950394}, - {0x0000a048, 0x00000396}, - {0x0000a04c, 0x00000000}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x28282828}, - {0x0000a084, 0x28282828}, - {0x0000a088, 0x28282828}, - {0x0000a08c, 0x28282828}, - {0x0000a090, 0x28282828}, - {0x0000a094, 0x24242428}, - {0x0000a098, 0x171e1e1e}, - {0x0000a09c, 0x02020b0b}, - {0x0000a0a0, 0x02020202}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x22072208}, - {0x0000a0c4, 0x22052206}, - {0x0000a0c8, 0x22032204}, - {0x0000a0cc, 0x22012202}, - {0x0000a0d0, 0x221f2200}, - {0x0000a0d4, 0x221d221e}, - {0x0000a0d8, 0x33023303}, - {0x0000a0dc, 0x33003301}, - {0x0000a0e0, 0x331e331f}, - {0x0000a0e4, 0x4402331d}, - {0x0000a0e8, 0x44004401}, - {0x0000a0ec, 0x441e441f}, - {0x0000a0f0, 0x55025503}, - {0x0000a0f4, 0x55005501}, - {0x0000a0f8, 0x551e551f}, - {0x0000a0fc, 0x6602551d}, + {0x0000a0e0, 0x33013302}, + {0x0000a0e4, 0x331f3300}, + {0x0000a0e8, 0x4402331e}, + {0x0000a0ec, 0x44004401}, + {0x0000a0f0, 0x441e441f}, + {0x0000a0f4, 0x55015502}, + {0x0000a0f8, 0x551f5500}, + {0x0000a0fc, 0x6602551e}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, @@ -1158,4 +594,260 @@ static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { {0x0000a1fc, 0x00000296}, }; -#endif +static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x18052e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000080c}, +}; + +static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x18053e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000080c}, +}; + +static const u32 ar9485_1_1_soc_preamble[][2] = { + /* Addr allmodes */ + {0x00004014, 0xba280400}, + {0x00004090, 0x00aa10aa}, + {0x000040a4, 0x00a0c9c9}, + {0x00007010, 0x00000022}, + {0x00007020, 0x00000000}, + {0x00007034, 0x00000002}, + {0x00007038, 0x000004c2}, + {0x00007048, 0x00000002}, +}; + +static const u32 ar9485_fast_clock_1_1_baseband_postamble[][3] = { + /* Addr 5G_HT20 5G_HT40 */ + {0x00009e00, 0x03721821, 0x03721821}, + {0x0000a230, 0x0000400b, 0x00004016}, + {0x0000a254, 0x00000898, 0x00001130}, +}; + +static const u32 ar9485_1_1_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, + {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, + {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, + {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, + {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, + {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, + {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x01303fc0, 0x01303fc4, 0x01303fc4, 0x01303fc0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, + {0x0000a234, 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff}, + {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0}, + {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000be04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, + {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, +}; + +static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x18013e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000080c}, +}; + +static const u32 ar9485_1_1_radio_postamble[][2] = { + /* Addr allmodes */ + {0x0001609c, 0x0b283f31}, + {0x000160ac, 0x24611800}, + {0x000160b0, 0x03284f3e}, + {0x0001610c, 0x00170000}, + {0x00016140, 0x50804008}, +}; + +static const u32 ar9485_1_1_mac_core[][2] = { + /* Addr allmodes */ + {0x00000008, 0x00000000}, + {0x00000030, 0x00020085}, + {0x00000034, 0x00000005}, + {0x00000040, 0x00000000}, + {0x00000044, 0x00000000}, + {0x00000048, 0x00000008}, + {0x0000004c, 0x00000010}, + {0x00000050, 0x00000000}, + {0x00001040, 0x002ffc0f}, + {0x00001044, 0x002ffc0f}, + {0x00001048, 0x002ffc0f}, + {0x0000104c, 0x002ffc0f}, + {0x00001050, 0x002ffc0f}, + {0x00001054, 0x002ffc0f}, + {0x00001058, 0x002ffc0f}, + {0x0000105c, 0x002ffc0f}, + {0x00001060, 0x002ffc0f}, + {0x00001064, 0x002ffc0f}, + {0x000010f0, 0x00000100}, + {0x00001270, 0x00000000}, + {0x000012b0, 0x00000000}, + {0x000012f0, 0x00000000}, + {0x0000143c, 0x00000000}, + {0x0000147c, 0x00000000}, + {0x00008000, 0x00000000}, + {0x00008004, 0x00000000}, + {0x00008008, 0x00000000}, + {0x0000800c, 0x00000000}, + {0x00008018, 0x00000000}, + {0x00008020, 0x00000000}, + {0x00008038, 0x00000000}, + {0x0000803c, 0x00000000}, + {0x00008040, 0x00000000}, + {0x00008044, 0x00000000}, + {0x00008048, 0x00000000}, + {0x0000804c, 0xffffffff}, + {0x00008054, 0x00000000}, + {0x00008058, 0x00000000}, + {0x0000805c, 0x000fc78f}, + {0x00008060, 0x0000000f}, + {0x00008064, 0x00000000}, + {0x00008070, 0x00000310}, + {0x00008074, 0x00000020}, + {0x00008078, 0x00000000}, + {0x0000809c, 0x0000000f}, + {0x000080a0, 0x00000000}, + {0x000080a4, 0x02ff0000}, + {0x000080a8, 0x0e070605}, + {0x000080ac, 0x0000000d}, + {0x000080b0, 0x00000000}, + {0x000080b4, 0x00000000}, + {0x000080b8, 0x00000000}, + {0x000080bc, 0x00000000}, + {0x000080c0, 0x2a800000}, + {0x000080c4, 0x06900168}, + {0x000080c8, 0x13881c22}, + {0x000080cc, 0x01f40000}, + {0x000080d0, 0x00252500}, + {0x000080d4, 0x00a00000}, + {0x000080d8, 0x00400000}, + {0x000080dc, 0x00000000}, + {0x000080e0, 0xffffffff}, + {0x000080e4, 0x0000ffff}, + {0x000080e8, 0x3f3f3f3f}, + {0x000080ec, 0x00000000}, + {0x000080f0, 0x00000000}, + {0x000080f4, 0x00000000}, + {0x000080fc, 0x00020000}, + {0x00008100, 0x00000000}, + {0x00008108, 0x00000052}, + {0x0000810c, 0x00000000}, + {0x00008110, 0x00000000}, + {0x00008114, 0x000007ff}, + {0x00008118, 0x000000aa}, + {0x0000811c, 0x00003210}, + {0x00008124, 0x00000000}, + {0x00008128, 0x00000000}, + {0x0000812c, 0x00000000}, + {0x00008130, 0x00000000}, + {0x00008134, 0x00000000}, + {0x00008138, 0x00000000}, + {0x0000813c, 0x0000ffff}, + {0x00008144, 0xffffffff}, + {0x00008168, 0x00000000}, + {0x0000816c, 0x00000000}, + {0x00008170, 0x18486200}, + {0x00008174, 0x33332210}, + {0x00008178, 0x00000000}, + {0x0000817c, 0x00020000}, + {0x000081c0, 0x00000000}, + {0x000081c4, 0x33332210}, + {0x000081d4, 0x00000000}, + {0x000081ec, 0x00000000}, + {0x000081f0, 0x00000000}, + {0x000081f4, 0x00000000}, + {0x000081f8, 0x00000000}, + {0x000081fc, 0x00000000}, + {0x00008240, 0x00100000}, + {0x00008244, 0x0010f400}, + {0x00008248, 0x00000800}, + {0x0000824c, 0x0001e800}, + {0x00008250, 0x00000000}, + {0x00008254, 0x00000000}, + {0x00008258, 0x00000000}, + {0x0000825c, 0x40000000}, + {0x00008260, 0x00080922}, + {0x00008264, 0x9ca00010}, + {0x00008268, 0xffffffff}, + {0x0000826c, 0x0000ffff}, + {0x00008270, 0x00000000}, + {0x00008274, 0x40000000}, + {0x00008278, 0x003e4180}, + {0x0000827c, 0x00000004}, + {0x00008284, 0x0000002c}, + {0x00008288, 0x0000002c}, + {0x0000828c, 0x000000ff}, + {0x00008294, 0x00000000}, + {0x00008298, 0x00000000}, + {0x0000829c, 0x00000000}, + {0x00008300, 0x00000140}, + {0x00008314, 0x00000000}, + {0x0000831c, 0x0000010d}, + {0x00008328, 0x00000000}, + {0x0000832c, 0x00000007}, + {0x00008330, 0x00000302}, + {0x00008334, 0x00000700}, + {0x00008338, 0x00ff0000}, + {0x0000833c, 0x02400000}, + {0x00008340, 0x000107ff}, + {0x00008344, 0xa248105b}, + {0x00008348, 0x008f0000}, + {0x0000835c, 0x00000000}, + {0x00008360, 0xffffffff}, + {0x00008364, 0xffffffff}, + {0x00008368, 0x00000000}, + {0x00008370, 0x00000000}, + {0x00008374, 0x000000ff}, + {0x00008378, 0x00000000}, + {0x0000837c, 0x00000000}, + {0x00008380, 0xffffffff}, + {0x00008384, 0xffffffff}, + {0x00008390, 0xffffffff}, + {0x00008394, 0xffffffff}, + {0x00008398, 0x00000000}, + {0x0000839c, 0x00000000}, + {0x000083a0, 0x00000000}, + {0x000083a4, 0x0000fa14}, + {0x000083a8, 0x000f0c00}, + {0x000083ac, 0x33332210}, + {0x000083b0, 0x33332210}, + {0x000083b4, 0x33332210}, + {0x000083b8, 0x33332210}, + {0x000083bc, 0x00000000}, + {0x000083c0, 0x00000000}, + {0x000083c4, 0x00000000}, + {0x000083c8, 0x00000000}, + {0x000083cc, 0x00000200}, + {0x000083d0, 0x000301ff}, +}; + +#endif /* INITVALS_9485_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h index 06b3f0df9fad..6e1915aee712 100644 --- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2010 Atheros Communications Inc. + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,18 +20,7 @@ /* AR9580 1.0 */ -static const u32 ar9580_1p0_modes_fast_clock[][3] = { - /* Addr 5G_HT20 5G_HT40 */ - {0x00001030, 0x00000268, 0x000004d0}, - {0x00001070, 0x0000018c, 0x00000318}, - {0x000010b0, 0x00000fd0, 0x00001fa0}, - {0x00008014, 0x044c044c, 0x08980898}, - {0x0000801c, 0x148ec02b, 0x148ec057}, - {0x00008318, 0x000044c0, 0x00008980}, - {0x00009e00, 0x0372131c, 0x0372131c}, - {0x0000a230, 0x0000000b, 0x00000016}, - {0x0000a254, 0x00000898, 0x00001130}, -}; +#define ar9580_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2 static const u32 ar9580_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ @@ -208,17 +198,7 @@ static const u32 ar9580_1p0_baseband_core[][2] = { {0x0000c420, 0x00000000}, }; -static const u32 ar9580_1p0_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; +#define ar9580_1p0_mac_postamble ar9300_2p2_mac_postamble static const u32 ar9580_1p0_low_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ @@ -326,111 +306,7 @@ static const u32 ar9580_1p0_low_ob_db_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9580_1p0_high_power_tx_gain_table[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, - {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21002220, 0x21002220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, - {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, - {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x61024a6c, 0x61024a6c, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x66026a6c, 0x66026a6c, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x7002708c, 0x7002708c, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x7302b08a, 0x7302b08a, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x5d001eec, 0x5d001eec}, - {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, - {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, - {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, - {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x1c800223, 0x1c800223, 0x12800400, 0x12800400}, - {0x0000a598, 0x21802220, 0x21802220, 0x16800402, 0x16800402}, - {0x0000a59c, 0x27802223, 0x27802223, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1c800603, 0x1c800603}, - {0x0000a5a4, 0x2f822222, 0x2f822222, 0x21800a02, 0x21800a02}, - {0x0000a5a8, 0x34822225, 0x34822225, 0x25800a04, 0x25800a04}, - {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x28800a20, 0x28800a20}, - {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2c800e20, 0x2c800e20}, - {0x0000a5b4, 0x4282242a, 0x4282242a, 0x30800e22, 0x30800e22}, - {0x0000a5b8, 0x4782244a, 0x4782244a, 0x34800e24, 0x34800e24}, - {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x38801640, 0x38801640}, - {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x3c801660, 0x3c801660}, - {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3f801861, 0x3f801861}, - {0x0000a5c8, 0x5782286c, 0x5782286c, 0x43801a81, 0x43801a81}, - {0x0000a5cc, 0x5c82486b, 0x5c82486b, 0x47801a83, 0x47801a83}, - {0x0000a5d0, 0x61824a6c, 0x61824a6c, 0x4a801c84, 0x4a801c84}, - {0x0000a5d4, 0x66826a6c, 0x66826a6c, 0x4e801ce3, 0x4e801ce3}, - {0x0000a5d8, 0x6b826e6c, 0x6b826e6c, 0x52801ce5, 0x52801ce5}, - {0x0000a5dc, 0x7082708c, 0x7082708c, 0x56801ce9, 0x56801ce9}, - {0x0000a5e0, 0x7382b08a, 0x7382b08a, 0x5a801ceb, 0x5a801ceb}, - {0x0000a5e4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a5e8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a5ec, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f0, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f4, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a5f8, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a5fc, 0x7782b08c, 0x7782b08c, 0x5d801eec, 0x5d801eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, - {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, - {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, - {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, - {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, - {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, - {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, - {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, -}; +#define ar9580_1p0_high_power_tx_gain_table ar9580_1p0_low_ob_db_tx_gain_table static const u32 ar9580_1p0_lowest_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ @@ -538,12 +414,7 @@ static const u32 ar9580_1p0_lowest_ob_db_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9580_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; +#define ar9580_1p0_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 static const u32 ar9580_1p0_mac_core[][2] = { /* Addr allmodes */ @@ -808,376 +679,11 @@ static const u32 ar9580_1p0_mixed_ob_db_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9580_1p0_wo_xlna_rx_gain_table[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; +#define ar9580_1p0_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 -static const u32 ar9580_1p0_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, -}; +#define ar9580_1p0_soc_postamble ar9300_2p2_soc_postamble -static const u32 ar9580_1p0_high_ob_db_tx_gain_table[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, - {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, - {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, - {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, - {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, - {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, - {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, - {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, - {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, - {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, - {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, - {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, - {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, - {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, - {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, - {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, - {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, - {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, - {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, - {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, - {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, - {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, - {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, - {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, - {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, - {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, - {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, - {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, - {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, - {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, - {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, - {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, - {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, - {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, - {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, - {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, - {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, - {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, - {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, - {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, - {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, - {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000c2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000c2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000c2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x00016044, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, - {0x00016048, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, - {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016444, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, - {0x00016448, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, - {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, - {0x00016844, 0x056db2e4, 0x056db2e4, 0x056db2e4, 0x056db2e4}, - {0x00016848, 0x8e480001, 0x8e480001, 0x8e480001, 0x8e480001}, - {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, -}; +#define ar9580_1p0_high_ob_db_tx_gain_table ar9300Modes_high_ob_db_tx_gain_table_2p2 static const u32 ar9580_1p0_soc_preamble[][2] = { /* Addr allmodes */ @@ -1189,265 +695,7 @@ static const u32 ar9580_1p0_soc_preamble[][2] = { {0x00007048, 0x00000008}, }; -static const u32 ar9580_1p0_rx_gain_table[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x01910190}, - {0x0000a030, 0x01930192}, - {0x0000a034, 0x01950194}, - {0x0000a038, 0x038a0196}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, - {0x0000a094, 0x11111717}, - {0x0000a098, 0x00030311}, - {0x0000a09c, 0x00000000}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x2a2d2f32}, - {0x0000b084, 0x21232328}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; +#define ar9580_1p0_rx_gain_table ar9462_common_rx_gain_table_2p0 static const u32 ar9580_1p0_radio_core[][2] = { /* Addr allmodes */ -- cgit v1.2.3 From 1a1fb970472d66d1668ff66bc46c2cd977f9a4fc Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 27 Jun 2012 19:57:56 -0700 Subject: mwifiex: wakeup main thread to handle command queued We miss to wakeup main thread after adding command to cmd pending queue at follwing places. These commands are handled later when main thread is woken up for handling an interrupt for sleep event from firmware. This adds worst case delay of 50msec. 1) We don't wakeup main thread when asynchronous command is added to cmd pending queue. Move queue_work() call from mwifiex_wait_queue_complete() to mwifiex_send_cmd_async() to wakeup main thread for sync as well as async commands. 2) Scan operation is triggered due to following reasons a) request from user (ex. "iw scan" command) b) Scan performed by driver internally. In first case main thread is woken up when first scan command is queued in cmd pending queue (we don't need to wakeup main thread for subsequent scan commands, because they are queued in scan command response handler), but it is not done for second case. queue_work() is moved inside mwifiex_scan_networks() to handle both the cases. Signed-off-by: Amitkumar Karwar Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 +- drivers/net/wireless/mwifiex/cmdevt.c | 1 + drivers/net/wireless/mwifiex/main.h | 4 ++-- drivers/net/wireless/mwifiex/scan.c | 25 +++---------------------- drivers/net/wireless/mwifiex/sta_ioctl.c | 3 --- 5 files changed, 7 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 9c2e08e4b093..02d0ee8609fa 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1422,7 +1422,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, priv->user_scan_cfg->chan_list[i].scan_time = 0; } - if (mwifiex_set_user_scan_ioctl(priv, priv->user_scan_cfg)) + if (mwifiex_scan_networks(priv, priv->user_scan_cfg)) return -EFAULT; if (request->ie && request->ie_len) { diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index ea37b887a874..c68adec3cc8b 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -578,6 +578,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, } else { adapter->cmd_queued = cmd_node; mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + queue_work(adapter->workqueue, &adapter->main_work); } return ret; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 0b3b5aa9830d..7cd95cc99a85 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -949,8 +949,8 @@ int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, struct mwifiex_rate_cfg *rate); int mwifiex_request_scan(struct mwifiex_private *priv, struct cfg80211_ssid *req_ssid); -int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv, - struct mwifiex_user_scan_cfg *scan_req); +int mwifiex_scan_networks(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in); int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); int mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, u16 channel); diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index efaf26ccd6ba..884ed6377003 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1296,8 +1296,8 @@ mwifiex_radio_type_to_band(u8 radio_type) * order to send the appropriate scan commands to firmware to populate or * update the internal driver scan table. */ -static int mwifiex_scan_networks(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in) +int mwifiex_scan_networks(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in) { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; @@ -1362,6 +1362,7 @@ static int mwifiex_scan_networks(struct mwifiex_private *priv, adapter->cmd_queued = cmd_node; mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + queue_work(adapter->workqueue, &adapter->main_work); } else { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); @@ -1377,26 +1378,6 @@ static int mwifiex_scan_networks(struct mwifiex_private *priv, return ret; } -/* - * Sends IOCTL request to start a scan with user configurations. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - * - * Upon completion, it also generates a wireless event to notify - * applications. - */ -int mwifiex_set_user_scan_ioctl(struct mwifiex_private *priv, - struct mwifiex_user_scan_cfg *scan_req) -{ - int status; - - status = mwifiex_scan_networks(priv, scan_req); - queue_work(priv->adapter->workqueue, &priv->adapter->main_work); - - return status; -} - /* * This function prepares a scan command to be sent to the firmware. * diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 106c449477b2..f2fd2423214f 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -66,9 +66,6 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "cmd pending\n"); atomic_inc(&adapter->cmd_pending); - /* Status pending, wake up main process */ - queue_work(adapter->workqueue, &adapter->main_work); - /* Wait for completion */ wait_event_interruptible(adapter->cmd_wait_q.wait, *(cmd_queued->condition)); -- cgit v1.2.3 From caa8984f59b8be220767886ae27de855f77e6a61 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 27 Jun 2012 19:57:57 -0700 Subject: mwifiex: use correct firmware command to get power limits "priv->max_tx_power_level" and "priv->min_tx_power_level" variables are initialized to maximum and minimum power levels supported by hardware by sending correct firmware command. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 9 +++++++++ drivers/net/wireless/mwifiex/sta_cmd.c | 23 ++++++++++++++++++++++- drivers/net/wireless/mwifiex/sta_cmdresp.c | 27 +++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 1184141839ae..ffb6cdfdb797 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -226,6 +226,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_RF_REG_ACCESS 0x001b #define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad #define HostCmd_CMD_802_11_RF_CHANNEL 0x001d +#define HostCmd_CMD_RF_TX_PWR 0x001e #define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 #define HostCmd_CMD_MAC_CONTROL 0x0028 #define HostCmd_CMD_802_11_AD_HOC_START 0x002b @@ -876,6 +877,13 @@ struct host_cmd_ds_txpwr_cfg { __le32 mode; } __packed; +struct host_cmd_ds_rf_tx_pwr { + __le16 action; + __le16 cur_level; + u8 max_power; + u8 min_power; +} __packed; + struct mwifiex_bcn_param { u8 bssid[ETH_ALEN]; u8 rssi; @@ -1361,6 +1369,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_tx_rate_query tx_rate; struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; struct host_cmd_ds_txpwr_cfg txp_cfg; + struct host_cmd_ds_rf_tx_pwr txp; struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; struct host_cmd_ds_802_11_scan scan; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 40e025da6bc2..2d4319a8941f 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -259,6 +259,23 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, return 0; } +/* + * This function prepares command to get RF Tx power. + */ +static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR); + txp->action = cpu_to_le16(cmd_action); + + return 0; +} + /* * This function prepares command to set Host Sleep configuration. * @@ -1055,6 +1072,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, data_buf); break; + case HostCmd_CMD_RF_TX_PWR: + ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action, + data_buf); + break; case HostCmd_CMD_802_11_PS_MODE_ENH: ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, (uint16_t)cmd_oid, data_buf); @@ -1283,7 +1304,7 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) priv->data_rate = 0; /* get tx power */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_TXPWR_CFG, + ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_RF_TX_PWR, HostCmd_ACT_GEN_GET, 0, NULL); if (ret) return -1; diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index a79ed9bd9695..4cb2c1c78397 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -450,6 +450,30 @@ static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, return 0; } +/* + * This function handles the command response of get RF Tx power. + */ +static int mwifiex_ret_rf_tx_power(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; + u16 action = le16_to_cpu(txp->action); + + priv->tx_power_level = le16_to_cpu(txp->cur_level); + + if (action == HostCmd_ACT_GEN_GET) { + priv->max_tx_power_level = txp->max_power; + priv->min_tx_power_level = txp->min_power; + } + + dev_dbg(priv->adapter->dev, + "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + /* * This function handles the command response of set/get MAC address. * @@ -847,6 +871,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_TXPWR_CFG: ret = mwifiex_ret_tx_power_cfg(priv, resp); break; + case HostCmd_CMD_RF_TX_PWR: + ret = mwifiex_ret_rf_tx_power(priv, resp); + break; case HostCmd_CMD_802_11_PS_MODE_ENH: ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); break; -- cgit v1.2.3 From 2041d7dff287333036145ba3aab57e3b675babef Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 27 Jun 2012 19:57:58 -0700 Subject: mwifiex: do not advertise custom regulatory domain capability Since we don't support custom regulatory domains, WIPHY_FLAG_CUSTOM_REGULATORY should not be enabled during wiphy registration. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 02d0ee8609fa..cf97c2aec8b4 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1746,7 +1746,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) memcpy(wiphy->perm_addr, priv->curr_addr, ETH_ALEN); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; /* Reserve space for mwifiex specific private data for BSS */ wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); -- cgit v1.2.3 From 34202e28fe7fc8551313f9a035a8857db83de757 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Wed, 27 Jun 2012 19:57:59 -0700 Subject: mwifiex: retrieve correct max_power information in reg_notifier handler As we don't provide custom regulatory rules to cfg80211, "chan->max_power" remains uninitialized (0dbm) and "chan->max_reg_power" will contain maximum power for a channel extracted from regulatory rules provided by CRDA; hence use "chan->max_reg_power" in reg_notifier handler instead of "chan->max_power" to set max_power in firmware. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index cf97c2aec8b4..bb06d2e3274a 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -260,13 +260,13 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) flag = 1; first_chan = (u32) ch->hw_value; next_chan = first_chan; - max_pwr = ch->max_power; + max_pwr = ch->max_reg_power; no_of_parsed_chan = 1; continue; } if (ch->hw_value == next_chan + 1 && - ch->max_power == max_pwr) { + ch->max_reg_power == max_pwr) { next_chan++; no_of_parsed_chan++; } else { @@ -277,7 +277,7 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) no_of_triplet++; first_chan = (u32) ch->hw_value; next_chan = first_chan; - max_pwr = ch->max_power; + max_pwr = ch->max_reg_power; no_of_parsed_chan = 1; } } -- cgit v1.2.3 From d9b8706843a501034d09bea63ca6723a2ed02b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Thu, 21 Jun 2012 23:11:18 +0000 Subject: net: qmi_wwan: fix Oops while disconnecting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit usbnet_disconnect() will set intfdata to NULL before calling the minidriver unbind function. The cdc_wdm subdriver cannot know that it is disconnecting until the qmi_wwan unbind function has called its disconnect function. This means that we must be able to support the cdc_wdm subdriver operating normally while usbnet_disconnect() is running, and in particular that intfdata may be NULL. The only place this matters is in qmi_wwan_cdc_wdm_manage_power which is called from cdc_wdm. Simply testing for NULL intfdata there is sufficient to allow it to continue working at all times. Fixes this Oops where a cdc-wdm device was closed while the USB device was disconnecting, causing wdm_release to call qmi_wwan_cdc_wdm_manage_power after intfdata was set to NULL by usbnet_disconnect: [41819.087460] BUG: unable to handle kernel NULL pointer dereference at 00000080 [41819.087815] IP: [] qmi_wwan_manage_power+0x68/0x90 [qmi_wwan] [41819.088028] *pdpt = 000000000314f001 *pde = 0000000000000000 [41819.088028] Oops: 0002 [#1] SMP [41819.088028] Modules linked in: qmi_wwan option usb_wwan usbserial usbnet cdc_wdm nls_iso8859_1 nls_cp437 vfat fat usb_storage bnep rfcomm bluetooth parport_pc ppdev binfmt_misc iptable_nat nf_nat nf_conntrack_ipv4 nf_conntrack nf_defrag_ipv4 iptable_mangle iptable_filter ip_tables x_tables dm_crypt uvcvideo snd_hda_codec_realtek snd_hda_intel videobuf2_core snd_hda_codec joydev videodev videobuf2_vmalloc hid_multitouch snd_hwdep arc4 videobuf2_memops snd_pcm snd_seq_midi snd_rawmidi snd_seq_midi_event ath9k mac80211 snd_seq ath9k_common ath9k_hw ath snd_timer snd_seq_device sparse_keymap dm_multipath scsi_dh coretemp mac_hid snd soundcore cfg80211 snd_page_alloc psmouse serio_raw microcode lp parport dm_mirror dm_region_hash dm_log usbhid hid i915 drm_kms_helper drm r8169 i2c_algo_bit wmi video [last unloaded: qmi_wwan] [41819.088028] [41819.088028] Pid: 23292, comm: qmicli Not tainted 3.4.0-5-generic #11-Ubuntu GIGABYTE T1005/T1005 [41819.088028] EIP: 0060:[] EFLAGS: 00010246 CPU: 1 [41819.088028] EIP is at qmi_wwan_manage_power+0x68/0x90 [qmi_wwan] [41819.088028] EAX: 00000000 EBX: 00000000 ECX: 000000c3 EDX: 00000000 [41819.088028] ESI: c3b27658 EDI: 00000000 EBP: c298bea4 ESP: c298be98 [41819.088028] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 [41819.088028] CR0: 8005003b CR2: 00000080 CR3: 3605e000 CR4: 000007f0 [41819.088028] DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 [41819.088028] DR6: ffff0ff0 DR7: 00000400 [41819.088028] Process qmicli (pid: 23292, ti=c298a000 task=f343b280 task.ti=c298a000) [41819.088028] Stack: [41819.088028] 00000000 c3b27658 e2a80d00 c298beb0 f864051a c3b27600 c298bec0 f9027099 [41819.088028] c2fd6000 00000008 c298bef0 c1147f96 00000001 00000000 00000000 f4e54790 [41819.088028] ecf43a00 ecf43a00 c2fd6008 c2fd6000 ebbd7600 ffffffb9 c298bf08 c1144474 [41819.088028] Call Trace: [41819.088028] [] qmi_wwan_cdc_wdm_manage_power+0x1a/0x20 [qmi_wwan] [41819.088028] [] wdm_release+0x69/0x70 [cdc_wdm] [41819.088028] [] fput+0xe6/0x210 [41819.088028] [] filp_close+0x54/0x80 [41819.088028] [] put_files_struct+0x75/0xc0 [41819.088028] [] exit_files+0x46/0x60 [41819.088028] [] do_exit+0x141/0x780 [41819.088028] [] ? wake_up_state+0xf/0x20 [41819.088028] [] ? signal_wake_up+0x28/0x40 [41819.088028] [] ? zap_other_threads+0x6b/0x80 [41819.088028] [] do_group_exit+0x34/0xa0 [41819.088028] [] sys_exit_group+0x18/0x20 [41819.088028] [] sysenter_do_call+0x12/0x28 [41819.088028] Code: 04 83 e7 01 c1 e7 03 0f b6 42 18 83 e0 f7 09 f8 88 42 18 8b 43 04 e8 48 9a dd c8 89 f0 8b 5d f4 8b 75 f8 8b 7d fc 89 ec 5d c3 90 ff 88 80 00 00 00 0f 94 c0 84 c0 75 b7 31 f6 8b 5d f4 89 f0 [41819.088028] EIP: [] qmi_wwan_manage_power+0x68/0x90 [qmi_wwan] SS:ESP 0068:c298be98 [41819.088028] CR2: 0000000000000080 [41819.149492] ---[ end trace 0944479ff8257f55 ]--- Reported-by: Marius Bjørnstad Kotsbak Cc: # v3.4 Signed-off-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/qmi_wwan.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 3767a1225860..b01960fcfbc9 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -197,6 +197,10 @@ err: static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) { struct usbnet *dev = usb_get_intfdata(intf); + + /* can be called while disconnecting */ + if (!dev) + return 0; return qmi_wwan_manage_power(dev, on); } -- cgit v1.2.3 From 9740e001932f59ee007d13ee3f39bb1b61086651 Mon Sep 17 00:00:00 2001 From: Claudiu Manoil Date: Thu, 28 Jun 2012 04:40:53 +0000 Subject: gianfar: Fix RXICr/TXICr programming for multi-queue mode The correct behavior is to program the interrupt coalescing regs (RXICr/TXICr) in accordance with the Rx/Tx Q's "rx/txcoalescing" flag. That is, if the coalescing flag is 0 for a given Rx/Tx queue then the corresponding coalescing register should be cleared. This behavior is correctly implemented for the single-queue mode (SQ_SG_MODE), but not for the multi-queue mode (MQ_MG_MODE). This fixes the later case. Signed-off-by: Claudiu Manoil Signed-off-by: David S. Miller --- drivers/net/ethernet/freescale/gianfar.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 0741aded9eb0..f2db8fca46a1 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1804,18 +1804,16 @@ void gfar_configure_coalescing(struct gfar_private *priv, if (priv->mode == MQ_MG_MODE) { baddr = ®s->txic0; for_each_set_bit(i, &tx_mask, priv->num_tx_queues) { - if (likely(priv->tx_queue[i]->txcoalescing)) { - gfar_write(baddr + i, 0); + gfar_write(baddr + i, 0); + if (likely(priv->tx_queue[i]->txcoalescing)) gfar_write(baddr + i, priv->tx_queue[i]->txic); - } } baddr = ®s->rxic0; for_each_set_bit(i, &rx_mask, priv->num_rx_queues) { - if (likely(priv->rx_queue[i]->rxcoalescing)) { - gfar_write(baddr + i, 0); + gfar_write(baddr + i, 0); + if (likely(priv->rx_queue[i]->rxcoalescing)) gfar_write(baddr + i, priv->rx_queue[i]->rxic); - } } } } -- cgit v1.2.3 From 61c91dd4a58b21a783e37208f4d02e3cb4b637c4 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 28 Jun 2012 16:46:27 -0700 Subject: Input: wacom - fix retrieving touch_max bug rep_data is not an array anymore, so taking it's address when passing to wacom_get_report() is wrong. Signed-off-by: Ping Cheng Tested-by: Rafi Rubin Reviewed-by: Jason Gerecke Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index b3a8bd3514b2..23a933da75cd 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -213,7 +213,7 @@ static void wacom_retrieve_report_data(struct usb_interface *intf, rep_data[0] = 12; result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT, - rep_data[0], &rep_data, 2, + rep_data[0], rep_data, 2, WAC_MSG_RETRIES); if (result >= 0 && rep_data[1] > 2) -- cgit v1.2.3 From 1cecc5cc0658e128bcad0b29edb96f286066571d Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Thu, 28 Jun 2012 16:47:30 -0700 Subject: Input: wacom - don't retrieve touch_max when it is predefined Some models, such as 0xE6, report more fingers than we process. Reported-by: Jonathan Nieder Signed-off-by: Ping Cheng Tested-by: Nils Kanning Tested-by: Rafi Rubin Reviewed-by: Jason Gerecke Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_sys.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 23a933da75cd..b145841bdbe7 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -398,7 +398,9 @@ static int wacom_parse_hid(struct usb_interface *intf, break; case HID_USAGE_CONTACTMAX: - wacom_retrieve_report_data(intf, features); + /* leave touch_max as is if predefined */ + if (!features->touch_max) + wacom_retrieve_report_data(intf, features); i++; break; } -- cgit v1.2.3 From 76fbc247b9aebc30f6d2c8ec1f69edcb68eaa328 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 28 Jun 2012 06:12:32 +0000 Subject: davinci_cpdma: include linux/module.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a number of warnings such as: CC drivers/net/ethernet/ti/davinci_cpdma.o drivers/net/ethernet/ti/davinci_cpdma.c:279:1: warning: data definition has no type or storage class drivers/net/ethernet/ti/davinci_cpdma.c:279:1: warning: type defaults to ‘int’ in declaration of ‘EXPORT_SYMBOL_GPL’ drivers/net/ethernet/ti/davinci_cpdma.c:279:1: warning: parameter names (without types) in function declaration Signed-off-by: Daniel Mack Cc: Vaibhav Hiremath Cc: David S. Miller Cc: Christian Riesch Signed-off-by: David S. Miller --- drivers/net/ethernet/ti/davinci_cpdma.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c index d614c374ed9d..3b5c4571b55e 100644 --- a/drivers/net/ethernet/ti/davinci_cpdma.c +++ b/drivers/net/ethernet/ti/davinci_cpdma.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 58050fce3530939372e6c2f4b4beb76fcb4caa65 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Thu, 28 Jun 2012 03:57:45 +0000 Subject: net: Use NLMSG_DEFAULT_SIZE in combination with nlmsg_new() Using NLMSG_GOODSIZE results in multiple pages being used as nlmsg_new() will automatically add the size of the netlink header to the payload thus exceeding the page limit. NLMSG_DEFAULT_SIZE takes this into account. Signed-off-by: Thomas Graf Cc: Jiri Pirko Cc: Dmitry Eremin-Solenikov Cc: Sergey Lapin Cc: Johannes Berg Cc: Lauro Ramos Venancio Cc: Aloisio Almeida Jr Cc: Samuel Ortiz Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 8 ++++---- drivers/net/wireless/mac80211_hwsim.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 5350eeaa22ce..89853c31e7f4 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1476,7 +1476,7 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info) void *hdr; int err; - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return -ENOMEM; @@ -1538,7 +1538,7 @@ static int team_nl_send_generic(struct genl_info *info, struct team *team, struct sk_buff *skb; int err; - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; @@ -1648,7 +1648,7 @@ static int __send_and_alloc_skb(struct sk_buff **pskb, if (err) return err; } - *pskb = genlmsg_new(NLMSG_DEFAULT_SIZE - GENL_HDRLEN, GFP_KERNEL); + *pskb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!*pskb) return -ENOMEM; return 0; @@ -2016,7 +2016,7 @@ static int team_nl_send_event_port_list_get(struct team *team) int err; struct net *net = dev_net(team->dev); - skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index a0b7cfd34685..a9ba3f7ea62b 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -571,7 +571,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, skb_dequeue(&data->pending); } - skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (skb == NULL) goto nla_put_failure; -- cgit v1.2.3 From 7fa8ad6df71f4778b23e17b0106aaef02b518684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Thu, 28 Jun 2012 20:36:20 +0000 Subject: caif-hsi: Fix merge issues. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the failing merge in net-next by reverting the last net-next merge for caif_hsi.c and then merge in the commit: "caif-hsi: Bugfix - Piggyback'ed embedded CAIF frame lost" from the net repository. The commit:"caif-hsi: Add missing return in error path" from net repository was dropped, as it changed code previously removed in the net-next repository. Signed-off-by: Sjur Brændeland Signed-off-by: David S. Miller --- drivers/net/caif/caif_hsi.c | 72 +++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 45 deletions(-) (limited to 'drivers') diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 087eb83454c0..0def8b3106f4 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -1131,51 +1131,7 @@ static void cfhsi_setup(struct net_device *dev) cfhsi->cfdev.use_stx = false; cfhsi->cfdev.use_fcs = false; cfhsi->ndev = dev; -} - -int cfhsi_probe(struct platform_device *pdev) -{ - struct cfhsi_ops *(*get_ops)(void); - struct cfhsi *cfhsi = NULL; - struct net_device *ndev; - int res; - - ndev = alloc_netdev(sizeof(struct cfhsi), "cfhsi%d", cfhsi_setup); - if (!ndev) - return -ENODEV; - - cfhsi = netdev_priv(ndev); - cfhsi->ndev = ndev; - cfhsi->pdev = pdev; - - get_ops = symbol_get(cfhsi_get_ops); - if (!get_ops) { - pr_err("%s: failed to get the cfhsi_ops\n", __func__); - return -ENODEV; - } - - /* Assign the HSI device. */ - cfhsi->ops = (*get_ops)(); - if (!cfhsi->ops) { - pr_err("%s: failed to get the cfhsi_ops\n", __func__); - goto err; - } - - /* Assign the driver to this HSI device. */ - cfhsi->ops->cb_ops = &cfhsi->cb_ops; - res = register_netdevice(ndev); - if (res) { - dev_err(&ndev->dev, "%s: Registration error: %d.\n", - __func__, res); - free_netdev(ndev); - } - /* Add CAIF HSI device to list. */ - list_add_tail(&cfhsi->list, &cfhsi_list); - - return res; -err: - symbol_put(cfhsi_get_ops); - return -ENODEV; + cfhsi->cfg = hsi_default_config; } static int cfhsi_open(struct net_device *ndev) @@ -1454,6 +1410,7 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct cfhsi *cfhsi = NULL; + struct cfhsi_ops *(*get_ops)(void); ASSERT_RTNL(); @@ -1461,7 +1418,32 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, cfhsi_netlink_parms(data, cfhsi); dev_net_set(cfhsi->ndev, src_net); + get_ops = symbol_get(cfhsi_get_ops); + if (!get_ops) { + pr_err("%s: failed to get the cfhsi_ops\n", __func__); + return -ENODEV; + } + + /* Assign the HSI device. */ + cfhsi->ops = (*get_ops)(); + if (!cfhsi->ops) { + pr_err("%s: failed to get the cfhsi_ops\n", __func__); + goto err; + } + + /* Assign the driver to this HSI device. */ + cfhsi->ops->cb_ops = &cfhsi->cb_ops; + if (register_netdevice(dev)) { + pr_warn("%s: caif_hsi device registration failed\n", __func__); + goto err; + } + /* Add CAIF HSI device to list. */ + list_add_tail(&cfhsi->list, &cfhsi_list); + return 0; +err: + symbol_put(cfhsi_get_ops); + return -ENODEV; } static struct rtnl_link_ops caif_hsi_link_ops __read_mostly = { -- cgit v1.2.3 From 1e0b6eac6a150a35b45d019681b0021896354ae5 Mon Sep 17 00:00:00 2001 From: Annie Li Date: Wed, 27 Jun 2012 00:46:58 +0000 Subject: xen/netback: only non-freed SKB is queued into tx_queue After SKB is queued into tx_queue, it will be freed if request_gop is NULL. However, no dequeue action is called in this situation, it is likely that tx_queue constains freed SKB. This patch should fix this issue, and it is based on 3.5.0-rc4+. This issue is found through code inspection, no bug is seen with it currently. I run netperf test for several hours, and no network regression was found. Signed-off-by: Annie Li Acked-by: Ian Campbell Signed-off-by: David S. Miller --- drivers/net/xen-netback/netback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index f4a6fcaeffb1..682633bfe00f 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -1363,8 +1363,6 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk) INVALID_PENDING_IDX); } - __skb_queue_tail(&netbk->tx_queue, skb); - netbk->pending_cons++; request_gop = xen_netbk_get_requests(netbk, vif, @@ -1376,6 +1374,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk) } gop = request_gop; + __skb_queue_tail(&netbk->tx_queue, skb); + vif->tx.req_cons = idx; xen_netbk_check_rx_xenvif(vif); -- cgit v1.2.3 From a777c892cea917b6c67c61f8dff47d8caae7b8a1 Mon Sep 17 00:00:00 2001 From: Manish chopra Date: Thu, 28 Jun 2012 20:12:21 +0000 Subject: netxen_nic: restrict force firmware dump when dump is disabled. o Set the ethtool_dump flag (=ETH_FW_DUMP_DISABLE) when dump is disabled. o update driver version to 4.0.80 Signed-off-by: Manish chopra Signed-off-by: Rajesh Borundia Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/netxen/netxen_nic.h | 4 ++-- drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index 37ccbe54e62d..eb3dfdbb642b 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -53,8 +53,8 @@ #define _NETXEN_NIC_LINUX_MAJOR 4 #define _NETXEN_NIC_LINUX_MINOR 0 -#define _NETXEN_NIC_LINUX_SUBVERSION 79 -#define NETXEN_NIC_LINUX_VERSIONID "4.0.79" +#define _NETXEN_NIC_LINUX_SUBVERSION 80 +#define NETXEN_NIC_LINUX_VERSIONID "4.0.80" #define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c)) #define _major(v) (((v) >> 24) & 0xff) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index 9103e3e36c28..10468e7932dd 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -826,7 +826,12 @@ netxen_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) dump->len = mdump->md_dump_size; else dump->len = 0; - dump->flag = mdump->md_capture_mask; + + if (!mdump->md_enabled) + dump->flag = ETH_FW_DUMP_DISABLE; + else + dump->flag = mdump->md_capture_mask; + dump->version = adapter->fw_version; return 0; } @@ -840,8 +845,10 @@ netxen_set_dump(struct net_device *netdev, struct ethtool_dump *val) switch (val->flag) { case NX_FORCE_FW_DUMP_KEY: - if (!mdump->md_enabled) - mdump->md_enabled = 1; + if (!mdump->md_enabled) { + netdev_info(netdev, "FW dump not enabled\n"); + return 0; + } if (adapter->fw_mdump_rdy) { netdev_info(netdev, "Previous dump not cleared, not forcing dump\n"); return 0; -- cgit v1.2.3 From d6f1c316346c95b0a280954d5afca29459921a24 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Jun 2012 16:49:29 +0200 Subject: iwlwifi: add trailing newline to some messages Some messages were missing a trailing newline, add it. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/trans.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index bac0eb0d046d..203d575e4493 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -1141,7 +1141,7 @@ static int iwl_trans_tx_stop(struct iwl_trans *trans) FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); if (ret < 0) IWL_ERR(trans, - "Failing on timeout while stopping DMA channel %d [0x%08x]", + "Failing on timeout while stopping DMA channel %d [0x%08x]\n", ch, iwl_read_direct32(trans, FH_TSSR_TX_STATUS_REG)); @@ -1149,7 +1149,8 @@ static int iwl_trans_tx_stop(struct iwl_trans *trans) spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); if (!trans_pcie->txq) { - IWL_WARN(trans, "Stopping tx queues that aren't allocated..."); + IWL_WARN(trans, + "Stopping tx queues that aren't allocated...\n"); return 0; } @@ -1426,7 +1427,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) err = iwl_prepare_card_hw(trans); if (err) { - IWL_ERR(trans, "Error while preparing HW: %d", err); + IWL_ERR(trans, "Error while preparing HW: %d\n", err); goto err_free_irq; } @@ -2127,13 +2128,14 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, err = pci_request_regions(pdev, DRV_NAME); if (err) { - dev_printk(KERN_ERR, &pdev->dev, "pci_request_regions failed"); + dev_printk(KERN_ERR, &pdev->dev, + "pci_request_regions failed\n"); goto out_pci_disable_device; } trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); if (!trans_pcie->hw_base) { - dev_printk(KERN_ERR, &pdev->dev, "pci_ioremap_bar failed"); + dev_printk(KERN_ERR, &pdev->dev, "pci_ioremap_bar failed\n"); err = -ENODEV; goto out_pci_release_regions; } @@ -2154,7 +2156,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, err = pci_enable_msi(pdev); if (err) dev_printk(KERN_ERR, &pdev->dev, - "pci_enable_msi failed(0X%x)", err); + "pci_enable_msi failed(0X%x)\n", err); trans->dev = &pdev->dev; trans_pcie->irq = pdev->irq; -- cgit v1.2.3 From 346c265a48169d73a69c88ce9d6b65e6bb3b1f20 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 28 Jun 2012 18:45:38 +0200 Subject: iwlwifi: fix debug message level Debug messages should be printed using dev_dbg() not dev_err() which requires DEBUG to be defined. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/iwl-debug.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c index 0f8fcd1d4fe2..87535a67de76 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -61,6 +61,9 @@ * *****************************************************************************/ +#define DEBUG + +#include #include #include #include "iwl-debug.h" @@ -124,7 +127,7 @@ void __iwl_dbg(struct device *dev, #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(level) && (!limit || net_ratelimit())) - dev_err(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', + dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', function, &vaf); #endif trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); -- cgit v1.2.3 From bb0adf6ca6c78758c12b054fe3f25023033072ab Mon Sep 17 00:00:00 2001 From: Jean Pihet Date: Thu, 28 Jun 2012 09:55:38 -0700 Subject: ARM: OMAP2+: do not allow SmartReflex to be built as a module Disable the module option for POWER_AVS since this is currently not supported. This patch fixes these error in the case POWER_AVS is set to 'm': arch/arm/mach-omap2/built-in.o: In function `sr_class3_configure': arch/arm/mach-omap2/smartreflex-class3.c:43: undefined reference to `sr_configure_errgen' arch/arm/mach-omap2/built-in.o: In function `sr_class3_disable': arch/arm/mach-omap2/smartreflex-class3.c:33: undefined reference to `sr_disable_errgen' arch/arm/mach-omap2/smartreflex-class3.c:35: undefined reference to `sr_disable' arch/arm/mach-omap2/built-in.o: In function `sr_class3_enable': arch/arm/mach-omap2/smartreflex-class3.c:28: undefined reference to `sr_enable' arch/arm/mach-omap2/built-in.o: In function `sr_class3_init': arch/arm/mach-omap2/smartreflex-class3.c:59: undefined reference to `sr_register_class' Reported-by: Tony Lindgren Signed-off-by: Jean Pihet Signed-off-by: Kevin Hilman [tony@atomide.com: updated to use relative paths for the build error] Signed-off-by: Tony Lindgren --- drivers/power/avs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig index 18493f7a8f6f..2a1008b61121 100644 --- a/drivers/power/avs/Kconfig +++ b/drivers/power/avs/Kconfig @@ -1,5 +1,5 @@ menuconfig POWER_AVS - tristate "Adaptive Voltage Scaling class support" + bool "Adaptive Voltage Scaling class support" help AVS is a power management technique which finely controls the operating voltage of a device in order to optimize (i.e. reduce) -- cgit v1.2.3 From c21b328ea8c7c71cd2daf50557db440bbaa7ef55 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 28 Jun 2012 17:53:07 -0400 Subject: drm/radeon: fix VM page table setup on SI Cayman and trinity allow for variable sized VM page tables, but SI requires that all page tables be the same size. The current code assumes variablely sized VM page tables so SI may end up with part of each page table overlapping with other memory which could end up being interpreted by the VM hw as garbage. Change the code to better accomodate SI. Allocate enough space for at least 2 full page tables and always set last_pfn to max_pfn on SI so each VM is backed by a full page table. This limits us to only 2 VMs active at any given time on SI. This will be rectified and the code can be reunified once we move to two level page tables. Signed-off-by: Alex Deucher Reviewed-by: Jerome Glisse Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_gart.c | 13 +++++++++++-- drivers/gpu/drm/radeon/si.c | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index 59d44937dd9f..84b648a7ddd8 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -289,8 +289,9 @@ int radeon_vm_manager_init(struct radeon_device *rdev) rdev->vm_manager.enabled = false; /* mark first vm as always in use, it's the system one */ + /* allocate enough for 2 full VM pts */ r = radeon_sa_bo_manager_init(rdev, &rdev->vm_manager.sa_manager, - rdev->vm_manager.max_pfn * 8, + rdev->vm_manager.max_pfn * 8 * 2, RADEON_GEM_DOMAIN_VRAM); if (r) { dev_err(rdev->dev, "failed to allocate vm bo (%dKB)\n", @@ -633,7 +634,15 @@ int radeon_vm_init(struct radeon_device *rdev, struct radeon_vm *vm) mutex_init(&vm->mutex); INIT_LIST_HEAD(&vm->list); INIT_LIST_HEAD(&vm->va); - vm->last_pfn = 0; + /* SI requires equal sized PTs for all VMs, so always set + * last_pfn to max_pfn. cayman allows variable sized + * pts so we can grow then as needed. Once we switch + * to two level pts we can unify this again. + */ + if (rdev->family >= CHIP_TAHITI) + vm->last_pfn = rdev->vm_manager.max_pfn; + else + vm->last_pfn = 0; /* map the ib pool buffer at 0 in virtual address space, set * read only */ diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index c7b61f16ecfd..0b0279291a73 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -2365,12 +2365,12 @@ int si_pcie_gart_enable(struct radeon_device *rdev) WREG32(0x15DC, 0); /* empty context1-15 */ - /* FIXME start with 1G, once using 2 level pt switch to full + /* FIXME start with 4G, once using 2 level pt switch to full * vm size space */ /* set vm size, must be a multiple of 4 */ WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0); - WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, (1 << 30) / RADEON_GPU_PAGE_SIZE); + WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn); for (i = 1; i < 16; i++) { if (i < 8) WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2), -- cgit v1.2.3 From ae0eef66088777cf252c6b91d3eb5ef2f30a67c5 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 29 Jun 2012 09:32:45 +0000 Subject: cnic: Fix mmap regression. commit 1f85d58cdf15354a7120fc9ccc9bb9c45b53af88 cnic: Remove uio mem[0]. introduced a regression as older versions of userspace app still rely on this mmap. Restore the mmap functionality and get the base address from pci_resource_start() as the nedev->base_addr has been deprecated for PCI devices. Update version to 2.5.12. Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/cnic.c | 8 +++++++- drivers/net/ethernet/broadcom/cnic_if.h | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index f897306b0eb7..22ad7b6d9048 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -1063,9 +1063,13 @@ static int cnic_init_uio(struct cnic_dev *dev) uinfo = &udev->cnic_uinfo; - uinfo->mem[0].memtype = UIO_MEM_NONE; + uinfo->mem[0].addr = pci_resource_start(dev->pcidev, 0); + uinfo->mem[0].internal_addr = dev->regview; + uinfo->mem[0].memtype = UIO_MEM_PHYS; if (test_bit(CNIC_F_BNX2_CLASS, &dev->flags)) { + uinfo->mem[0].size = MB_GET_CID_ADDR(TX_TSS_CID + + TX_MAX_TSS_RINGS + 1); uinfo->mem[1].addr = (unsigned long) cp->status_blk.gen & PAGE_MASK; if (cp->ethdev->drv_state & CNIC_DRV_STATE_USING_MSIX) @@ -1075,6 +1079,8 @@ static int cnic_init_uio(struct cnic_dev *dev) uinfo->name = "bnx2_cnic"; } else if (test_bit(CNIC_F_BNX2X_CLASS, &dev->flags)) { + uinfo->mem[0].size = pci_resource_len(dev->pcidev, 0); + uinfo->mem[1].addr = (unsigned long) cp->bnx2x_def_status_blk & PAGE_MASK; uinfo->mem[1].size = sizeof(*cp->bnx2x_def_status_blk); diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index 54f68f07692e..5cb88881bba1 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -14,8 +14,8 @@ #include "bnx2x/bnx2x_mfw_req.h" -#define CNIC_MODULE_VERSION "2.5.11" -#define CNIC_MODULE_RELDATE "June 27, 2012" +#define CNIC_MODULE_VERSION "2.5.12" +#define CNIC_MODULE_RELDATE "June 29, 2012" #define CNIC_ULP_RDMA 0 #define CNIC_ULP_ISCSI 1 -- cgit v1.2.3 From a31f2d17b331db970259e875b7223d3aba7e3821 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 29 Jun 2012 06:15:21 +0000 Subject: netlink: add netlink_kernel_cfg parameter to netlink_kernel_create This patch adds the following structure: struct netlink_kernel_cfg { unsigned int groups; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; }; That can be passed to netlink_kernel_create to set optional configurations for netlink kernel sockets. I've populated this structure by looking for NULL and zero parameters at the existing code. The remaining parameters that always need to be set are still left in the original interface. That includes optional parameters for the netlink socket creation. This allows easy extensibility of this interface in the future. This patch also adapts all callers to use this new interface. Signed-off-by: Pablo Neira Ayuso Signed-off-by: David S. Miller --- drivers/connector/connector.c | 13 +++++++++---- drivers/infiniband/core/netlink.c | 7 +++++-- drivers/scsi/scsi_netlink.c | 7 +++++-- drivers/scsi/scsi_transport_iscsi.c | 9 ++++++--- drivers/staging/gdm72xx/netlink_k.c | 6 ++++-- 5 files changed, 29 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 34e0e9e4d913..116cf8d02834 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -251,15 +251,20 @@ static const struct file_operations cn_file_ops = { .release = single_release }; +static struct cn_dev cdev = { + .input = cn_rx_skb, +}; + static int __devinit cn_init(void) { struct cn_dev *dev = &cdev; - - dev->input = cn_rx_skb; + struct netlink_kernel_cfg cfg = { + .groups = CN_NETLINK_USERS + 0xf, + .input = dev->input, + }; dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR, - CN_NETLINK_USERS + 0xf, - dev->input, NULL, THIS_MODULE); + THIS_MODULE, &cfg); if (!dev->nls) return -EIO; diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c index 1e691dca1820..3ae2bfd31015 100644 --- a/drivers/infiniband/core/netlink.c +++ b/drivers/infiniband/core/netlink.c @@ -173,8 +173,11 @@ static void ibnl_rcv(struct sk_buff *skb) int __init ibnl_init(void) { - nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 0, ibnl_rcv, - NULL, THIS_MODULE); + struct netlink_kernel_cfg cfg = { + .input = ibnl_rcv, + }; + + nls = netlink_kernel_create(&init_net, NETLINK_RDMA, THIS_MODULE, &cfg); if (!nls) { pr_warn("Failed to create netlink socket\n"); return -ENOMEM; diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index c77628afbf9f..8818dd681c19 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -486,6 +486,10 @@ void scsi_netlink_init(void) { int error; + struct netlink_kernel_cfg cfg = { + .input = scsi_nl_rcv_msg, + .groups = SCSI_NL_GRP_CNT, + }; INIT_LIST_HEAD(&scsi_nl_drivers); @@ -497,8 +501,7 @@ scsi_netlink_init(void) } scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT, - SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL, - THIS_MODULE); + THIS_MODULE, &cfg); if (!scsi_nl_sock) { printk(KERN_ERR "%s: register of receive handler failed\n", __func__); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 1cf640e575da..6042954d8f3b 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -2936,7 +2936,10 @@ EXPORT_SYMBOL_GPL(iscsi_unregister_transport); static __init int iscsi_transport_init(void) { int err; - + struct netlink_kernel_cfg cfg = { + .groups = 1, + .input = iscsi_if_rx, + }; printk(KERN_INFO "Loading iSCSI transport class v%s.\n", ISCSI_TRANSPORT_VERSION); @@ -2966,8 +2969,8 @@ static __init int iscsi_transport_init(void) if (err) goto unregister_conn_class; - nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, - NULL, THIS_MODULE); + nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, + THIS_MODULE, &cfg); if (!nls) { err = -ENOBUFS; goto unregister_session_class; diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c index 2489bb5597ca..87c3a07ed80e 100644 --- a/drivers/staging/gdm72xx/netlink_k.c +++ b/drivers/staging/gdm72xx/netlink_k.c @@ -88,13 +88,15 @@ struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type, void *msg, int len)) { struct sock *sock; + struct netlink_kernel_cfg cfg = { + .input = netlink_rcv, + }; #if !defined(DEFINE_MUTEX) init_MUTEX(&netlink_mutex); #endif - sock = netlink_kernel_create(&init_net, unit, 0, netlink_rcv, NULL, - THIS_MODULE); + sock = netlink_kernel_create(&init_net, unit, THIS_MODULE, &cfg); if (sock) rcv_cb = cb; -- cgit v1.2.3 From b03738430c7537d5f87948e0b35d8aaf2688c6b4 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 20 Jun 2012 09:48:43 +0800 Subject: ACPI video: Still use ACPI backlight control if _DOS doesn't exist This fixes a regression in 3.4-rc1 caused by commit ea9f8856bd6d4ed45885b06a338f7362cd6c60e5 (ACPI video: Harden video bus adding.) Some platforms don't have _DOS control method, but the ACPI backlight still works. We should not invoke _DOS for these platforms. https://bugzilla.kernel.org/show_bug.cgi?id=43168 Cc: Igor Murzov Cc: stable@vger.kernel.org Signed-off-by: Zhang Rui Signed-off-by: Len Brown --- drivers/acpi/video.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 9577b6fa2650..4134b300b9cd 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -558,6 +558,8 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; + if (!video->cap._DOS) + return 0; if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) return -EINVAL; -- cgit v1.2.3 From 5f1601261050251a5ca293378b492a69d590dacb Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Wed, 13 Jun 2012 16:10:45 -0500 Subject: acpi_pad: fix power_saving thread deadlock The acpi_pad driver can get stuck in destroy_power_saving_task() waiting for kthread_stop() to stop a power_saving thread. The problem is that the isolated_cpus_lock mutex is owned when destroy_power_saving_task() calls kthread_stop(), which waits for a power_saving thread to end, and the power_saving thread tries to acquire the isolated_cpus_lock when it calls round_robin_cpu(). This patch fixes the issue by making round_robin_cpu() use its own mutex. https://bugzilla.kernel.org/show_bug.cgi?id=42981 Cc: stable@vger.kernel.org Signed-off-by: Stuart Hayes Signed-off-by: Len Brown --- drivers/acpi/acpi_pad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index a43fa1a57d57..1502c50273b5 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -36,6 +36,7 @@ #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" #define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 static DEFINE_MUTEX(isolated_cpus_lock); +static DEFINE_MUTEX(round_robin_lock); static unsigned long power_saving_mwait_eax; @@ -107,7 +108,7 @@ static void round_robin_cpu(unsigned int tsk_index) if (!alloc_cpumask_var(&tmp, GFP_KERNEL)) return; - mutex_lock(&isolated_cpus_lock); + mutex_lock(&round_robin_lock); cpumask_clear(tmp); for_each_cpu(cpu, pad_busy_cpus) cpumask_or(tmp, tmp, topology_thread_cpumask(cpu)); @@ -116,7 +117,7 @@ static void round_robin_cpu(unsigned int tsk_index) if (cpumask_empty(tmp)) cpumask_andnot(tmp, cpu_online_mask, pad_busy_cpus); if (cpumask_empty(tmp)) { - mutex_unlock(&isolated_cpus_lock); + mutex_unlock(&round_robin_lock); return; } for_each_cpu(cpu, tmp) { @@ -131,7 +132,7 @@ static void round_robin_cpu(unsigned int tsk_index) tsk_in_cpu[tsk_index] = preferred_cpu; cpumask_set_cpu(preferred_cpu, pad_busy_cpus); cpu_weight[preferred_cpu]++; - mutex_unlock(&isolated_cpus_lock); + mutex_unlock(&round_robin_lock); set_cpus_allowed_ptr(current, cpumask_of(preferred_cpu)); } -- cgit v1.2.3 From f2f2c8b42d5b1858e1680133fcd4bea5c8dcdb2c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 29 Jun 2012 05:10:06 +0000 Subject: virtio_net: use IFF_LIVE_ADDR_CHANGE priv_flag Acked-by: Michael S. Tsirkin Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 36a16d581f71..1db445b2ecc7 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -679,12 +679,11 @@ static int virtnet_set_mac_address(struct net_device *dev, void *p) { struct virtnet_info *vi = netdev_priv(dev); struct virtio_device *vdev = vi->vdev; - struct sockaddr *addr = p; + int ret; - if (!is_valid_ether_addr(addr->sa_data)) - return -EADDRNOTAVAIL; - memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); - dev->addr_assign_type &= ~NET_ADDR_RANDOM; + ret = eth_mac_addr(dev, p); + if (ret) + return ret; if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) vdev->config->set(vdev, offsetof(struct virtio_net_config, mac), @@ -1063,7 +1062,7 @@ static int virtnet_probe(struct virtio_device *vdev) return -ENOMEM; /* Set up network device as normal. */ - dev->priv_flags |= IFF_UNICAST_FLT; + dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; dev->netdev_ops = &virtnet_netdev; dev->features = NETIF_F_HIGHDMA; -- cgit v1.2.3 From 5a1d1c8c78e55df2e876249f8e3a4a573efdbaa6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 29 Jun 2012 05:10:07 +0000 Subject: team: use IFF_LIVE_ADDR_CHANGE priv_flag Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/team/team.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 89853c31e7f4..9b94f53a9d4b 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1188,10 +1188,11 @@ static int team_set_mac_address(struct net_device *dev, void *p) { struct team *team = netdev_priv(dev); struct team_port *port; - struct sockaddr *addr = p; + int err; - dev->addr_assign_type &= ~NET_ADDR_RANDOM; - memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + err = eth_mac_addr(dev, p); + if (err) + return err; rcu_read_lock(); list_for_each_entry_rcu(port, &team->port_list, list) if (team->ops.port_change_mac) @@ -1393,7 +1394,7 @@ static void team_setup(struct net_device *dev) * bring us to promisc mode in case a unicast addr is added. * Let this up to underlay drivers. */ - dev->priv_flags |= IFF_UNICAST_FLT; + dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_GRO; -- cgit v1.2.3 From 0d1632b46ad76cde92085aa1ed422e9329443082 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 29 Jun 2012 05:10:08 +0000 Subject: dummy: use IFF_LIVE_ADDR_CHANGE priv_flag Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/dummy.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index bab0158f1cc3..9d6a0677466b 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -40,18 +40,6 @@ static int numdummies = 1; -static int dummy_set_address(struct net_device *dev, void *p) -{ - struct sockaddr *sa = p; - - if (!is_valid_ether_addr(sa->sa_data)) - return -EADDRNOTAVAIL; - - dev->addr_assign_type &= ~NET_ADDR_RANDOM; - memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); - return 0; -} - /* fake multicast ability */ static void set_multicast_list(struct net_device *dev) { @@ -118,7 +106,7 @@ static const struct net_device_ops dummy_netdev_ops = { .ndo_start_xmit = dummy_xmit, .ndo_validate_addr = eth_validate_addr, .ndo_set_rx_mode = set_multicast_list, - .ndo_set_mac_address = dummy_set_address, + .ndo_set_mac_address = eth_mac_addr, .ndo_get_stats64 = dummy_get_stats64, }; @@ -134,6 +122,7 @@ static void dummy_setup(struct net_device *dev) dev->tx_queue_len = 0; dev->flags |= IFF_NOARP; dev->flags &= ~IFF_MULTICAST; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO; dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX; eth_hw_addr_random(dev); -- cgit v1.2.3 From 46afededca7d8b0117e05b23e5cbcfa0e2bbbd92 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Tue, 19 Jun 2012 21:51:31 +0530 Subject: Bluetooth: cleanup dtl1_config Improve the error handling of dtl1_config() Signed-off-by: Devendra Naga Signed-off-by: Gustavo Padovan --- drivers/bluetooth/dtl1_cs.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index b1b37ccd3cd4..97a7784db4a2 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -586,29 +586,31 @@ static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data) static int dtl1_config(struct pcmcia_device *link) { dtl1_info_t *info = link->priv; - int i; + int ret; /* Look for a generic full-sized window */ link->resource[0]->end = 8; - if (pcmcia_loop_config(link, dtl1_confcheck, NULL) < 0) + ret = pcmcia_loop_config(link, dtl1_confcheck, NULL); + if (ret) goto failed; - i = pcmcia_request_irq(link, dtl1_interrupt); - if (i != 0) + ret = pcmcia_request_irq(link, dtl1_interrupt); + if (ret) goto failed; - i = pcmcia_enable_device(link); - if (i != 0) + ret = pcmcia_enable_device(link); + if (ret) goto failed; - if (dtl1_open(info) != 0) + ret = dtl1_open(info); + if (ret) goto failed; return 0; failed: dtl1_detach(link); - return -ENODEV; + return ret; } static const struct pcmcia_device_id dtl1_ids[] = { -- cgit v1.2.3 From 510df2515f6b0afdd7604aee21b02b8f339e9344 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 20 Jun 2012 14:40:12 +0300 Subject: Bluetooth: Fix warning: using int as NULL pointer Fix for warnings below: ... drivers/bluetooth/bt3c_cs.c:667:20: warning: Using plain integer as NULL pointer drivers/bluetooth/btuart_cs.c:596:20: warning: Using plain integer as NULL pointer ... Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- drivers/bluetooth/bt3c_cs.c | 2 +- drivers/bluetooth/btuart_cs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index b2b0fbbb43b5..8925b6d672a6 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -664,7 +664,7 @@ static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; - if (try == 0) + if (!try) p_dev->io_lines = 16; if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 65b8d996840c..21e803a6a281 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -593,7 +593,7 @@ static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data) { int *try = priv_data; - if (try == 0) + if (!try) p_dev->io_lines = 16; if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0)) -- cgit v1.2.3 From 74ad8fdaefe6ccb8ef1918394a9d04a036658346 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 29 Jun 2012 14:58:07 +0300 Subject: Bluetooth: bluecard_cs: Shorten scope for iobase Shortening scope shall silence some warnings reported by Geert Uytterhoeven: ... drivers/bluetooth/bluecard_cs.c: warning: unused variable 'iobase' [-Wunused-variable] ... Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo Padovan --- drivers/bluetooth/bluecard_cs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 585c88e01893..66c3a6770c41 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -621,7 +621,6 @@ static int bluecard_hci_flush(struct hci_dev *hdev) static int bluecard_hci_open(struct hci_dev *hdev) { bluecard_info_t *info = hci_get_drvdata(hdev); - unsigned int iobase = info->p_dev->resource[0]->start; if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); @@ -630,6 +629,8 @@ static int bluecard_hci_open(struct hci_dev *hdev) return 0; if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { + unsigned int iobase = info->p_dev->resource[0]->start; + /* Enable LED */ outb(0x08 | 0x20, iobase + 0x30); } @@ -641,7 +642,6 @@ static int bluecard_hci_open(struct hci_dev *hdev) static int bluecard_hci_close(struct hci_dev *hdev) { bluecard_info_t *info = hci_get_drvdata(hdev); - unsigned int iobase = info->p_dev->resource[0]->start; if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; @@ -649,6 +649,8 @@ static int bluecard_hci_close(struct hci_dev *hdev) bluecard_hci_flush(hdev); if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) { + unsigned int iobase = info->p_dev->resource[0]->start; + /* Disable LED */ outb(0x00, iobase + 0x30); } -- cgit v1.2.3 From 0e90b49ca4b891f085b57559a3071a4feefb496c Mon Sep 17 00:00:00 2001 From: Mitch A Williams Date: Sat, 30 Jun 2012 00:23:19 +0000 Subject: igbvf: fix divide by zero Using ethtool -C ethX rx-usecs 0 crashes with a divide by zero. Refactor this function to fix this issue and make it more clear what the intent of each conditional is. Add comment regarding using a setting of zero. CC: stable [3.3+] CC: David Ahern Signed-off-by: Mitch Williams Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/igbvf/ethtool.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 8ce67064b9c5..90eef07943f4 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -357,21 +357,28 @@ static int igbvf_set_coalesce(struct net_device *netdev, struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - if ((ec->rx_coalesce_usecs > IGBVF_MAX_ITR_USECS) || - ((ec->rx_coalesce_usecs > 3) && - (ec->rx_coalesce_usecs < IGBVF_MIN_ITR_USECS)) || - (ec->rx_coalesce_usecs == 2)) - return -EINVAL; - - /* convert to rate of irq's per second */ - if (ec->rx_coalesce_usecs && ec->rx_coalesce_usecs <= 3) { + if ((ec->rx_coalesce_usecs >= IGBVF_MIN_ITR_USECS) && + (ec->rx_coalesce_usecs <= IGBVF_MAX_ITR_USECS)) { + adapter->current_itr = ec->rx_coalesce_usecs << 2; + adapter->requested_itr = 1000000000 / + (adapter->current_itr * 256); + } else if ((ec->rx_coalesce_usecs == 3) || + (ec->rx_coalesce_usecs == 2)) { adapter->current_itr = IGBVF_START_ITR; adapter->requested_itr = ec->rx_coalesce_usecs; - } else { - adapter->current_itr = ec->rx_coalesce_usecs << 2; + } else if (ec->rx_coalesce_usecs == 0) { + /* + * The user's desire is to turn off interrupt throttling + * altogether, but due to HW limitations, we can't do that. + * Instead we set a very small value in EITR, which would + * allow ~967k interrupts per second, but allow the adapter's + * internal clocking to still function properly. + */ + adapter->current_itr = 4; adapter->requested_itr = 1000000000 / (adapter->current_itr * 256); - } + } else + return -EINVAL; writel(adapter->current_itr, hw->hw_addr + adapter->rx_ring->itr_register); -- cgit v1.2.3 From 2e1706f234f86ff71056ef69683d734fbf7e9e40 Mon Sep 17 00:00:00 2001 From: Bruce Allan Date: Sat, 30 Jun 2012 20:02:42 +0000 Subject: e1000e: remove use of IP payload checksum Currently only used when packet split mode is enabled with jumbo frames, IP payload checksum (for fragmented UDP packets) is mutually exclusive with receive hashing offload since the hardware uses the same space in the receive descriptor for the hardware-provided packet checksum and the RSS hash, respectively. Users currently must disable jumbos when receive hashing offload is enabled, or vice versa, because of this incompatibility. Since testing has shown that IP payload checksum does not provide any real benefit, just remove it so that there is no longer a choice between jumbos or receive hashing offload but not both as done in other Intel GbE drivers (e.g. e1000, igb). Also, add a missing check for IP checksum error reported by the hardware; let the stack verify the checksum when this happens. CC: stable [3.4] Signed-off-by: Bruce Allan Tested-by: Aaron Brown Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/e1000e/defines.h | 1 + drivers/net/ethernet/intel/e1000e/netdev.c | 75 ++++++----------------------- 2 files changed, 15 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h index 351a4097b2ba..76edbc1be33b 100644 --- a/drivers/net/ethernet/intel/e1000e/defines.h +++ b/drivers/net/ethernet/intel/e1000e/defines.h @@ -103,6 +103,7 @@ #define E1000_RXD_ERR_SEQ 0x04 /* Sequence Error */ #define E1000_RXD_ERR_CXE 0x10 /* Carrier Extension Error */ #define E1000_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */ +#define E1000_RXD_ERR_IPE 0x40 /* IP Checksum Error */ #define E1000_RXD_ERR_RXE 0x80 /* Rx Data Error */ #define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 31d37a2b5ba8..623e30b9964d 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -496,7 +496,7 @@ static void e1000_receive_skb(struct e1000_adapter *adapter, * @sk_buff: socket buffer with received data **/ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err, - __le16 csum, struct sk_buff *skb) + struct sk_buff *skb) { u16 status = (u16)status_err; u8 errors = (u8)(status_err >> 24); @@ -511,8 +511,8 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err, if (status & E1000_RXD_STAT_IXSM) return; - /* TCP/UDP checksum error bit is set */ - if (errors & E1000_RXD_ERR_TCPE) { + /* TCP/UDP checksum error bit or IP checksum error bit is set */ + if (errors & (E1000_RXD_ERR_TCPE | E1000_RXD_ERR_IPE)) { /* let the stack verify checksum errors */ adapter->hw_csum_err++; return; @@ -523,19 +523,7 @@ static void e1000_rx_checksum(struct e1000_adapter *adapter, u32 status_err, return; /* It must be a TCP or UDP packet with a valid checksum */ - if (status & E1000_RXD_STAT_TCPCS) { - /* TCP checksum is good */ - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else { - /* - * IP fragment with UDP payload - * Hardware complements the payload checksum, so we undo it - * and then put the value in host order for further stack use. - */ - __sum16 sum = (__force __sum16)swab16((__force u16)csum); - skb->csum = csum_unfold(~sum); - skb->ip_summed = CHECKSUM_COMPLETE; - } + skb->ip_summed = CHECKSUM_UNNECESSARY; adapter->hw_csum_good++; } @@ -954,8 +942,7 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, skb_put(skb, length); /* Receive Checksum Offload */ - e1000_rx_checksum(adapter, staterr, - rx_desc->wb.lower.hi_dword.csum_ip.csum, skb); + e1000_rx_checksum(adapter, staterr, skb); e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb); @@ -1341,8 +1328,7 @@ copydone: total_rx_bytes += skb->len; total_rx_packets++; - e1000_rx_checksum(adapter, staterr, - rx_desc->wb.lower.hi_dword.csum_ip.csum, skb); + e1000_rx_checksum(adapter, staterr, skb); e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb); @@ -1512,9 +1498,8 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done, } } - /* Receive Checksum Offload XXX recompute due to CRC strip? */ - e1000_rx_checksum(adapter, staterr, - rx_desc->wb.lower.hi_dword.csum_ip.csum, skb); + /* Receive Checksum Offload */ + e1000_rx_checksum(adapter, staterr, skb); e1000_rx_hash(netdev, rx_desc->wb.lower.hi_dword.rss, skb); @@ -3098,19 +3083,10 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) /* Enable Receive Checksum Offload for TCP and UDP */ rxcsum = er32(RXCSUM); - if (adapter->netdev->features & NETIF_F_RXCSUM) { + if (adapter->netdev->features & NETIF_F_RXCSUM) rxcsum |= E1000_RXCSUM_TUOFL; - - /* - * IPv4 payload checksum for UDP fragments must be - * used in conjunction with packet-split. - */ - if (adapter->rx_ps_pages) - rxcsum |= E1000_RXCSUM_IPPCSE; - } else { + else rxcsum &= ~E1000_RXCSUM_TUOFL; - /* no need to clear IPPCSE as it defaults to 0 */ - } ew32(RXCSUM, rxcsum); if (adapter->hw.mac.type == e1000_pch2lan) { @@ -5241,22 +5217,10 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; /* Jumbo frame support */ - if (max_frame > ETH_FRAME_LEN + ETH_FCS_LEN) { - if (!(adapter->flags & FLAG_HAS_JUMBO_FRAMES)) { - e_err("Jumbo Frames not supported.\n"); - return -EINVAL; - } - - /* - * IP payload checksum (enabled with jumbos/packet-split when - * Rx checksum is enabled) and generation of RSS hash is - * mutually exclusive in the hardware. - */ - if ((netdev->features & NETIF_F_RXCSUM) && - (netdev->features & NETIF_F_RXHASH)) { - e_err("Jumbo frames cannot be enabled when both receive checksum offload and receive hashing are enabled. Disable one of the receive offload features before enabling jumbos.\n"); - return -EINVAL; - } + if ((max_frame > ETH_FRAME_LEN + ETH_FCS_LEN) && + !(adapter->flags & FLAG_HAS_JUMBO_FRAMES)) { + e_err("Jumbo Frames not supported.\n"); + return -EINVAL; } /* Supported frame sizes */ @@ -6030,17 +5994,6 @@ static int e1000_set_features(struct net_device *netdev, NETIF_F_RXALL))) return 0; - /* - * IP payload checksum (enabled with jumbos/packet-split when Rx - * checksum is enabled) and generation of RSS hash is mutually - * exclusive in the hardware. - */ - if (adapter->rx_ps_pages && - (features & NETIF_F_RXCSUM) && (features & NETIF_F_RXHASH)) { - e_err("Enabling both receive checksum offload and receive hashing is not possible with jumbo frames. Disable jumbos or enable only one of the receive offload features.\n"); - return -EINVAL; - } - if (changed & NETIF_F_RXFCS) { if (features & NETIF_F_RXFCS) { adapter->flags2 &= ~FLAG2_CRC_STRIPPING; -- cgit v1.2.3 From ea2ab8711b8a4363e3b3ee0dc609d1b3c8b92899 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 27 Jun 2012 21:14:35 +0000 Subject: stmmac: do not use strict_strtoul but kstrtoint This patch replaces the obsolete strict_strtoul with kstrtoint. v2: also removed casting on kstrtoul. v3: use kstrtoint instead of kstrtoul due to all vars are integer. thanks to E. Dumazet. Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 27 +++++++++-------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 590e95b4cbfa..eba49cb810f9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2129,42 +2129,35 @@ static int __init stmmac_cmdline_opt(char *str) return -EINVAL; while ((opt = strsep(&str, ",")) != NULL) { if (!strncmp(opt, "debug:", 6)) { - if (strict_strtoul(opt + 6, 0, (unsigned long *)&debug)) + if (kstrtoint(opt + 6, 0, &debug)) goto err; } else if (!strncmp(opt, "phyaddr:", 8)) { - if (strict_strtoul(opt + 8, 0, - (unsigned long *)&phyaddr)) + if (kstrtoint(opt + 8, 0, &phyaddr)) goto err; } else if (!strncmp(opt, "dma_txsize:", 11)) { - if (strict_strtoul(opt + 11, 0, - (unsigned long *)&dma_txsize)) + if (kstrtoint(opt + 11, 0, &dma_txsize)) goto err; } else if (!strncmp(opt, "dma_rxsize:", 11)) { - if (strict_strtoul(opt + 11, 0, - (unsigned long *)&dma_rxsize)) + if (kstrtoint(opt + 11, 0, &dma_rxsize)) goto err; } else if (!strncmp(opt, "buf_sz:", 7)) { - if (strict_strtoul(opt + 7, 0, - (unsigned long *)&buf_sz)) + if (kstrtoint(opt + 7, 0, &buf_sz)) goto err; } else if (!strncmp(opt, "tc:", 3)) { - if (strict_strtoul(opt + 3, 0, (unsigned long *)&tc)) + if (kstrtoint(opt + 3, 0, &tc)) goto err; } else if (!strncmp(opt, "watchdog:", 9)) { - if (strict_strtoul(opt + 9, 0, - (unsigned long *)&watchdog)) + if (kstrtoint(opt + 9, 0, &watchdog)) goto err; } else if (!strncmp(opt, "flow_ctrl:", 10)) { - if (strict_strtoul(opt + 10, 0, - (unsigned long *)&flow_ctrl)) + if (kstrtoint(opt + 10, 0, &flow_ctrl)) goto err; } else if (!strncmp(opt, "pause:", 6)) { - if (strict_strtoul(opt + 6, 0, (unsigned long *)&pause)) + if (kstrtoint(opt + 6, 0, &pause)) goto err; #ifdef CONFIG_STMMAC_TIMER } else if (!strncmp(opt, "tmrate:", 7)) { - if (strict_strtoul(opt + 7, 0, - (unsigned long *)&tmrate)) + if (kstrtoint(opt + 7, 0, &tmrate)) goto err; #endif } -- cgit v1.2.3 From d765955d2ae0b88781a0db3a5bacfe4241925e09 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 27 Jun 2012 21:14:37 +0000 Subject: stmmac: add the Energy Efficient Ethernet support This patch adds the Energy Efficient Ethernet support to the stmmac. Please see the driver's documentation for further details about this support in the driver. Thanks also goes to Rayagond Kokatanur for his first implementation. Note: to clearly manage and expose the lpi interrupt status and eee ethtool stats I've had to do some modifications to the driver's design and I found really useful to move other parts of the code (e.g. mmc irq stat) in the main directly. So this means that some core has been reworked to introduce the EEE. v1: initial patch v2: fixed some sparse issues (typos) v3: erroneously sent the v2 renamed as v3 v4: o Fixed the return value of the stmmac_eee_init as suggested by D.Miller o Totally reviewed the ethtool support for EEE o Added a new internal parameter to tune the SW timer for TX LPI. v5: do not change any eee setting in case of the stmmac_ethtool_op_set_eee fails (it has to return -EOPNOTSUPP in that case). Signed-off-by: Giuseppe Cavallaro Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 31 +++- drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 20 +++ .../net/ethernet/stmicro/stmmac/dwmac1000_core.c | 101 ++++++++++++- .../net/ethernet/stmicro/stmmac/dwmac100_core.c | 4 +- drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h | 1 + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 8 + .../net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 57 +++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 166 ++++++++++++++++++++- .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 2 + 9 files changed, 372 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index bcd54d6e94fd..e2d083228f3a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -95,6 +95,16 @@ struct stmmac_extra_stats { unsigned long poll_n; unsigned long sched_timer_n; unsigned long normal_irq_n; + unsigned long mmc_tx_irq_n; + unsigned long mmc_rx_irq_n; + unsigned long mmc_rx_csum_offload_irq_n; + /* EEE */ + unsigned long irq_receive_pmt_irq_n; + unsigned long irq_tx_path_in_lpi_mode_n; + unsigned long irq_tx_path_exit_lpi_mode_n; + unsigned long irq_rx_path_in_lpi_mode_n; + unsigned long irq_rx_path_exit_lpi_mode_n; + unsigned long phy_eee_wakeup_error_n; }; /* CSR Frequency Access Defines*/ @@ -162,6 +172,17 @@ enum tx_dma_irq_status { handle_tx_rx = 3, }; +enum core_specific_irq_mask { + core_mmc_tx_irq = 1, + core_mmc_rx_irq = 2, + core_mmc_rx_csum_offload_irq = 4, + core_irq_receive_pmt_irq = 8, + core_irq_tx_path_in_lpi_mode = 16, + core_irq_tx_path_exit_lpi_mode = 32, + core_irq_rx_path_in_lpi_mode = 64, + core_irq_rx_path_exit_lpi_mode = 128, +}; + /* DMA HW capabilities */ struct dma_features { unsigned int mbps_10_100; @@ -208,6 +229,10 @@ struct dma_features { #define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */ #define MAC_RNABLE_RX 0x00000004 /* Receiver Enable */ +/* Default LPI timers */ +#define STMMAC_DEFAULT_LIT_LS_TIMER 0x3E8 +#define STMMAC_DEFAULT_TWT_LS_TIMER 0x0 + struct stmmac_desc_ops { /* DMA RX descriptor ring initialization */ void (*init_rx_desc) (struct dma_desc *p, unsigned int ring_size, @@ -278,7 +303,7 @@ struct stmmac_ops { /* Dump MAC registers */ void (*dump_regs) (void __iomem *ioaddr); /* Handle extra events on specific interrupts hw dependent */ - void (*host_irq_status) (void __iomem *ioaddr); + int (*host_irq_status) (void __iomem *ioaddr); /* Multicast filter setting */ void (*set_filter) (struct net_device *dev, int id); /* Flow control setting */ @@ -291,6 +316,10 @@ struct stmmac_ops { unsigned int reg_n); void (*get_umac_addr) (void __iomem *ioaddr, unsigned char *addr, unsigned int reg_n); + void (*set_eee_mode) (void __iomem *ioaddr); + void (*reset_eee_mode) (void __iomem *ioaddr); + void (*set_eee_timer) (void __iomem *ioaddr, int ls, int tw); + void (*set_eee_pls) (void __iomem *ioaddr, int link); }; struct mac_link { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 23478bf4ed7a..f90fcb5f9573 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -36,6 +36,7 @@ #define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ enum dwmac1000_irq_status { + lpiis_irq = 0x400, time_stamp_irq = 0x0200, mmc_rx_csum_offload_irq = 0x0080, mmc_tx_irq = 0x0040, @@ -60,6 +61,25 @@ enum power_event { power_down = 0x00000001, }; +/* Energy Efficient Ethernet (EEE) + * + * LPI status, timer and control register offset + */ +#define LPI_CTRL_STATUS 0x0030 +#define LPI_TIMER_CTRL 0x0034 + +/* LPI control and status defines */ +#define LPI_CTRL_STATUS_LPITXA 0x00080000 /* Enable LPI TX Automate */ +#define LPI_CTRL_STATUS_PLSEN 0x00040000 /* Enable PHY Link Status */ +#define LPI_CTRL_STATUS_PLS 0x00020000 /* PHY Link Status */ +#define LPI_CTRL_STATUS_LPIEN 0x00010000 /* LPI Enable */ +#define LPI_CTRL_STATUS_RLPIST 0x00000200 /* Receive LPI state */ +#define LPI_CTRL_STATUS_TLPIST 0x00000100 /* Transmit LPI state */ +#define LPI_CTRL_STATUS_RLPIEX 0x00000008 /* Receive LPI Exit */ +#define LPI_CTRL_STATUS_RLPIEN 0x00000004 /* Receive LPI Entry */ +#define LPI_CTRL_STATUS_TLPIEX 0x00000002 /* Transmit LPI Exit */ +#define LPI_CTRL_STATUS_TLPIEN 0x00000001 /* Transmit LPI Entry */ + /* GMAC HW ADDR regs */ #define GMAC_ADDR_HIGH(reg) (((reg > 15) ? 0x00000800 : 0x00000040) + \ (reg * 8)) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index b5e4d02f15c9..bfe022605498 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -194,26 +194,107 @@ static void dwmac1000_pmt(void __iomem *ioaddr, unsigned long mode) } -static void dwmac1000_irq_status(void __iomem *ioaddr) +static int dwmac1000_irq_status(void __iomem *ioaddr) { u32 intr_status = readl(ioaddr + GMAC_INT_STATUS); + int status = 0; /* Not used events (e.g. MMC interrupts) are not handled. */ - if ((intr_status & mmc_tx_irq)) - CHIP_DBG(KERN_DEBUG "GMAC: MMC tx interrupt: 0x%08x\n", + if ((intr_status & mmc_tx_irq)) { + CHIP_DBG(KERN_INFO "GMAC: MMC tx interrupt: 0x%08x\n", readl(ioaddr + GMAC_MMC_TX_INTR)); - if (unlikely(intr_status & mmc_rx_irq)) - CHIP_DBG(KERN_DEBUG "GMAC: MMC rx interrupt: 0x%08x\n", + status |= core_mmc_tx_irq; + } + if (unlikely(intr_status & mmc_rx_irq)) { + CHIP_DBG(KERN_INFO "GMAC: MMC rx interrupt: 0x%08x\n", readl(ioaddr + GMAC_MMC_RX_INTR)); - if (unlikely(intr_status & mmc_rx_csum_offload_irq)) - CHIP_DBG(KERN_DEBUG "GMAC: MMC rx csum offload: 0x%08x\n", + status |= core_mmc_rx_irq; + } + if (unlikely(intr_status & mmc_rx_csum_offload_irq)) { + CHIP_DBG(KERN_INFO "GMAC: MMC rx csum offload: 0x%08x\n", readl(ioaddr + GMAC_MMC_RX_CSUM_OFFLOAD)); + status |= core_mmc_rx_csum_offload_irq; + } if (unlikely(intr_status & pmt_irq)) { - CHIP_DBG(KERN_DEBUG "GMAC: received Magic frame\n"); + CHIP_DBG(KERN_INFO "GMAC: received Magic frame\n"); /* clear the PMT bits 5 and 6 by reading the PMT * status register. */ readl(ioaddr + GMAC_PMT); + status |= core_irq_receive_pmt_irq; } + /* MAC trx/rx EEE LPI entry/exit interrupts */ + if (intr_status & lpiis_irq) { + /* Clean LPI interrupt by reading the Reg 12 */ + u32 lpi_status = readl(ioaddr + LPI_CTRL_STATUS); + + if (lpi_status & LPI_CTRL_STATUS_TLPIEN) { + CHIP_DBG(KERN_INFO "GMAC TX entered in LPI\n"); + status |= core_irq_tx_path_in_lpi_mode; + } + if (lpi_status & LPI_CTRL_STATUS_TLPIEX) { + CHIP_DBG(KERN_INFO "GMAC TX exit from LPI\n"); + status |= core_irq_tx_path_exit_lpi_mode; + } + if (lpi_status & LPI_CTRL_STATUS_RLPIEN) { + CHIP_DBG(KERN_INFO "GMAC RX entered in LPI\n"); + status |= core_irq_rx_path_in_lpi_mode; + } + if (lpi_status & LPI_CTRL_STATUS_RLPIEX) { + CHIP_DBG(KERN_INFO "GMAC RX exit from LPI\n"); + status |= core_irq_rx_path_exit_lpi_mode; + } + } + + return status; +} + +static void dwmac1000_set_eee_mode(void __iomem *ioaddr) +{ + u32 value; + + /* Enable the link status receive on RGMII, SGMII ore SMII + * receive path and instruct the transmit to enter in LPI + * state. */ + value = readl(ioaddr + LPI_CTRL_STATUS); + value |= LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA; + writel(value, ioaddr + LPI_CTRL_STATUS); +} + +static void dwmac1000_reset_eee_mode(void __iomem *ioaddr) +{ + u32 value; + + value = readl(ioaddr + LPI_CTRL_STATUS); + value &= ~(LPI_CTRL_STATUS_LPIEN | LPI_CTRL_STATUS_LPITXA); + writel(value, ioaddr + LPI_CTRL_STATUS); +} + +static void dwmac1000_set_eee_pls(void __iomem *ioaddr, int link) +{ + u32 value; + + value = readl(ioaddr + LPI_CTRL_STATUS); + + if (link) + value |= LPI_CTRL_STATUS_PLS; + else + value &= ~LPI_CTRL_STATUS_PLS; + + writel(value, ioaddr + LPI_CTRL_STATUS); +} + +static void dwmac1000_set_eee_timer(void __iomem *ioaddr, int ls, int tw) +{ + int value = ((tw & 0xffff)) | ((ls & 0x7ff) << 16); + + /* Program the timers in the LPI timer control register: + * LS: minimum time (ms) for which the link + * status from PHY should be ok before transmitting + * the LPI pattern. + * TW: minimum time (us) for which the core waits + * after it has stopped transmitting the LPI pattern. + */ + writel(value, ioaddr + LPI_TIMER_CTRL); } static const struct stmmac_ops dwmac1000_ops = { @@ -226,6 +307,10 @@ static const struct stmmac_ops dwmac1000_ops = { .pmt = dwmac1000_pmt, .set_umac_addr = dwmac1000_set_umac_addr, .get_umac_addr = dwmac1000_get_umac_addr, + .set_eee_mode = dwmac1000_set_eee_mode, + .reset_eee_mode = dwmac1000_reset_eee_mode, + .set_eee_timer = dwmac1000_set_eee_timer, + .set_eee_pls = dwmac1000_set_eee_pls, }; struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 19e0f4eed2bc..f83210e7c221 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -72,9 +72,9 @@ static int dwmac100_rx_ipc_enable(void __iomem *ioaddr) return 0; } -static void dwmac100_irq_status(void __iomem *ioaddr) +static int dwmac100_irq_status(void __iomem *ioaddr) { - return; + return 0; } static void dwmac100_set_umac_addr(void __iomem *ioaddr, unsigned char *addr, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 6e0360f9cfde..e678ce39d014 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -70,6 +70,7 @@ #define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL) /* DMA Status register defines */ +#define DMA_STATUS_GLPII 0x40000000 /* GMAC LPI interrupt */ #define DMA_STATUS_GPI 0x10000000 /* PMT interrupt */ #define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ #define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index dc20c56efc9d..ab4c376cb276 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -87,6 +87,12 @@ struct stmmac_priv { #endif int clk_csr; int synopsys_id; + struct timer_list eee_ctrl_timer; + bool tx_path_in_lpi_mode; + int lpi_irq; + int eee_enabled; + int eee_active; + int tx_lpi_timer; }; extern int phyaddr; @@ -104,6 +110,8 @@ int stmmac_dvr_remove(struct net_device *ndev); struct stmmac_priv *stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, void __iomem *addr); +void stmmac_disable_eee_mode(struct stmmac_priv *priv); +bool stmmac_eee_init(struct stmmac_priv *priv); #ifdef CONFIG_HAVE_CLK static inline int stmmac_clk_enable(struct stmmac_priv *priv) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index ce431846fc6f..76fd61aa005f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -93,6 +93,16 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(poll_n), STMMAC_STAT(sched_timer_n), STMMAC_STAT(normal_irq_n), + STMMAC_STAT(normal_irq_n), + STMMAC_STAT(mmc_tx_irq_n), + STMMAC_STAT(mmc_rx_irq_n), + STMMAC_STAT(mmc_rx_csum_offload_irq_n), + STMMAC_STAT(irq_receive_pmt_irq_n), + STMMAC_STAT(irq_tx_path_in_lpi_mode_n), + STMMAC_STAT(irq_tx_path_exit_lpi_mode_n), + STMMAC_STAT(irq_rx_path_in_lpi_mode_n), + STMMAC_STAT(irq_rx_path_exit_lpi_mode_n), + STMMAC_STAT(phy_eee_wakeup_error_n), }; #define STMMAC_STATS_LEN ARRAY_SIZE(stmmac_gstrings_stats) @@ -366,6 +376,11 @@ static void stmmac_get_ethtool_stats(struct net_device *dev, (*(u32 *)p); } } + if (priv->eee_enabled) { + int val = phy_get_eee_err(priv->phydev); + if (val) + priv->xstats.phy_eee_wakeup_error_n = val; + } } for (i = 0; i < STMMAC_STATS_LEN; i++) { char *p = (char *)priv + stmmac_gstrings_stats[i].stat_offset; @@ -464,6 +479,46 @@ static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) return 0; } +static int stmmac_ethtool_op_get_eee(struct net_device *dev, + struct ethtool_eee *edata) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + if (!priv->dma_cap.eee) + return -EOPNOTSUPP; + + edata->eee_enabled = priv->eee_enabled; + edata->eee_active = priv->eee_active; + edata->tx_lpi_timer = priv->tx_lpi_timer; + + return phy_ethtool_get_eee(priv->phydev, edata); +} + +static int stmmac_ethtool_op_set_eee(struct net_device *dev, + struct ethtool_eee *edata) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + priv->eee_enabled = edata->eee_enabled; + + if (!priv->eee_enabled) + stmmac_disable_eee_mode(priv); + else { + /* We are asking for enabling the EEE but it is safe + * to verify all by invoking the eee_init function. + * In case of failure it will return an error. + */ + priv->eee_enabled = stmmac_eee_init(priv); + if (!priv->eee_enabled) + return -EOPNOTSUPP; + + /* Do not change tx_lpi_timer in case of failure */ + priv->tx_lpi_timer = edata->tx_lpi_timer; + } + + return phy_ethtool_set_eee(priv->phydev, edata); +} + static const struct ethtool_ops stmmac_ethtool_ops = { .begin = stmmac_check_if_running, .get_drvinfo = stmmac_ethtool_getdrvinfo, @@ -480,6 +535,8 @@ static const struct ethtool_ops stmmac_ethtool_ops = { .get_strings = stmmac_get_strings, .get_wol = stmmac_get_wol, .set_wol = stmmac_set_wol, + .get_eee = stmmac_ethtool_op_get_eee, + .set_eee = stmmac_ethtool_op_set_eee, .get_sset_count = stmmac_get_sset_count, .get_ts_info = ethtool_op_get_ts_info, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index eba49cb810f9..ea3bc0963bd7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -133,6 +133,12 @@ static const u32 default_msg_level = (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_TIMER); +#define STMMAC_DEFAULT_LPI_TIMER 1000 +static int eee_timer = STMMAC_DEFAULT_LPI_TIMER; +module_param(eee_timer, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(eee_timer, "LPI tx expiration time in msec"); +#define STMMAC_LPI_TIMER(x) (jiffies + msecs_to_jiffies(x)) + static irqreturn_t stmmac_interrupt(int irq, void *dev_id); #ifdef CONFIG_STMMAC_DEBUG_FS @@ -161,6 +167,8 @@ static void stmmac_verify_args(void) flow_ctrl = FLOW_OFF; if (unlikely((pause < 0) || (pause > 0xffff))) pause = PAUSE_TIME; + if (eee_timer < 0) + eee_timer = STMMAC_DEFAULT_LPI_TIMER; } static void stmmac_clk_csr_set(struct stmmac_priv *priv) @@ -229,6 +237,85 @@ static inline void stmmac_hw_fix_mac_speed(struct stmmac_priv *priv) phydev->speed); } +static void stmmac_enable_eee_mode(struct stmmac_priv *priv) +{ + /* Check and enter in LPI mode */ + if ((priv->dirty_tx == priv->cur_tx) && + (priv->tx_path_in_lpi_mode == false)) + priv->hw->mac->set_eee_mode(priv->ioaddr); +} + +void stmmac_disable_eee_mode(struct stmmac_priv *priv) +{ + /* Exit and disable EEE in case of we are are in LPI state. */ + priv->hw->mac->reset_eee_mode(priv->ioaddr); + del_timer_sync(&priv->eee_ctrl_timer); + priv->tx_path_in_lpi_mode = false; +} + +/** + * stmmac_eee_ctrl_timer + * @arg : data hook + * Description: + * If there is no data transfer and if we are not in LPI state, + * then MAC Transmitter can be moved to LPI state. + */ +static void stmmac_eee_ctrl_timer(unsigned long arg) +{ + struct stmmac_priv *priv = (struct stmmac_priv *)arg; + + stmmac_enable_eee_mode(priv); + mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_TIMER(eee_timer)); +} + +/** + * stmmac_eee_init + * @priv: private device pointer + * Description: + * If the EEE support has been enabled while configuring the driver, + * if the GMAC actually supports the EEE (from the HW cap reg) and the + * phy can also manage EEE, so enable the LPI state and start the timer + * to verify if the tx path can enter in LPI state. + */ +bool stmmac_eee_init(struct stmmac_priv *priv) +{ + bool ret = false; + + /* MAC core supports the EEE feature. */ + if (priv->dma_cap.eee) { + /* Check if the PHY supports EEE */ + if (phy_init_eee(priv->phydev, 1)) + goto out; + + priv->eee_active = 1; + init_timer(&priv->eee_ctrl_timer); + priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer; + priv->eee_ctrl_timer.data = (unsigned long)priv; + priv->eee_ctrl_timer.expires = STMMAC_LPI_TIMER(eee_timer); + add_timer(&priv->eee_ctrl_timer); + + priv->hw->mac->set_eee_timer(priv->ioaddr, + STMMAC_DEFAULT_LIT_LS_TIMER, + priv->tx_lpi_timer); + + pr_info("stmmac: Energy-Efficient Ethernet initialized\n"); + + ret = true; + } +out: + return ret; +} + +static void stmmac_eee_adjust(struct stmmac_priv *priv) +{ + /* When the EEE has been already initialised we have to + * modify the PLS bit in the LPI ctrl & status reg according + * to the PHY link status. For this reason. + */ + if (priv->eee_enabled) + priv->hw->mac->set_eee_pls(priv->ioaddr, priv->phydev->link); +} + /** * stmmac_adjust_link * @dev: net device structure @@ -249,6 +336,7 @@ static void stmmac_adjust_link(struct net_device *dev) phydev->addr, phydev->link); spin_lock_irqsave(&priv->lock, flags); + if (phydev->link) { u32 ctrl = readl(priv->ioaddr + MAC_CTRL_REG); @@ -315,6 +403,8 @@ static void stmmac_adjust_link(struct net_device *dev) if (new_state && netif_msg_link(priv)) phy_print_status(phydev); + stmmac_eee_adjust(priv); + spin_unlock_irqrestore(&priv->lock, flags); DBG(probe, DEBUG, "stmmac_adjust_link: exiting\n"); @@ -332,7 +422,7 @@ static int stmmac_init_phy(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); struct phy_device *phydev; - char phy_id[MII_BUS_ID_SIZE + 3]; + char phy_id_fmt[MII_BUS_ID_SIZE + 3]; char bus_id[MII_BUS_ID_SIZE]; int interface = priv->plat->interface; priv->oldlink = 0; @@ -346,11 +436,12 @@ static int stmmac_init_phy(struct net_device *dev) snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x", priv->plat->bus_id); - snprintf(phy_id, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, + snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id, priv->plat->phy_addr); - pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id); + pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt); - phydev = phy_connect(dev, phy_id, &stmmac_adjust_link, 0, interface); + phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, 0, + interface); if (IS_ERR(phydev)) { pr_err("%s: Could not attach to PHY\n", dev->name); @@ -689,6 +780,11 @@ static void stmmac_tx(struct stmmac_priv *priv) } netif_tx_unlock(priv->dev); } + + if ((priv->eee_enabled) && (!priv->tx_path_in_lpi_mode)) { + stmmac_enable_eee_mode(priv); + mod_timer(&priv->eee_ctrl_timer, STMMAC_LPI_TIMER(eee_timer)); + } spin_unlock(&priv->tx_lock); } @@ -1027,6 +1123,17 @@ static int stmmac_open(struct net_device *dev) } } + /* Request the IRQ lines */ + if (priv->lpi_irq != -ENXIO) { + ret = request_irq(priv->lpi_irq, stmmac_interrupt, IRQF_SHARED, + dev->name, dev); + if (unlikely(ret < 0)) { + pr_err("%s: ERROR: allocating the LPI IRQ %d (%d)\n", + __func__, priv->lpi_irq, ret); + goto open_error_lpiirq; + } + } + /* Enable the MAC Rx/Tx */ stmmac_set_mac(priv->ioaddr, true); @@ -1062,12 +1169,19 @@ static int stmmac_open(struct net_device *dev) if (priv->phydev) phy_start(priv->phydev); + priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS_TIMER; + priv->eee_enabled = stmmac_eee_init(priv); + napi_enable(&priv->napi); skb_queue_head_init(&priv->rx_recycle); netif_start_queue(dev); return 0; +open_error_lpiirq: + if (priv->wol_irq != dev->irq) + free_irq(priv->wol_irq, dev); + open_error_wolirq: free_irq(dev->irq, dev); @@ -1093,6 +1207,9 @@ static int stmmac_release(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + if (priv->eee_enabled) + del_timer_sync(&priv->eee_ctrl_timer); + /* Stop and disconnect the PHY */ if (priv->phydev) { phy_stop(priv->phydev); @@ -1115,6 +1232,8 @@ static int stmmac_release(struct net_device *dev) free_irq(dev->irq, dev); if (priv->wol_irq != dev->irq) free_irq(priv->wol_irq, dev); + if (priv->lpi_irq != -ENXIO) + free_irq(priv->lpi_irq, dev); /* Stop TX/RX DMA and clear the descriptors */ priv->hw->dma->stop_tx(priv->ioaddr); @@ -1164,6 +1283,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) spin_lock(&priv->tx_lock); + if (priv->tx_path_in_lpi_mode) + stmmac_disable_eee_mode(priv); + entry = priv->cur_tx % txsize; #ifdef STMMAC_XMIT_DEBUG @@ -1540,10 +1662,37 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) return IRQ_NONE; } - if (priv->plat->has_gmac) - /* To handle GMAC own interrupts */ - priv->hw->mac->host_irq_status((void __iomem *) dev->base_addr); + /* To handle GMAC own interrupts */ + if (priv->plat->has_gmac) { + int status = priv->hw->mac->host_irq_status((void __iomem *) + dev->base_addr); + if (unlikely(status)) { + if (status & core_mmc_tx_irq) + priv->xstats.mmc_tx_irq_n++; + if (status & core_mmc_rx_irq) + priv->xstats.mmc_rx_irq_n++; + if (status & core_mmc_rx_csum_offload_irq) + priv->xstats.mmc_rx_csum_offload_irq_n++; + if (status & core_irq_receive_pmt_irq) + priv->xstats.irq_receive_pmt_irq_n++; + + /* For LPI we need to save the tx status */ + if (status & core_irq_tx_path_in_lpi_mode) { + priv->xstats.irq_tx_path_in_lpi_mode_n++; + priv->tx_path_in_lpi_mode = true; + } + if (status & core_irq_tx_path_exit_lpi_mode) { + priv->xstats.irq_tx_path_exit_lpi_mode_n++; + priv->tx_path_in_lpi_mode = false; + } + if (status & core_irq_rx_path_in_lpi_mode) + priv->xstats.irq_rx_path_in_lpi_mode_n++; + if (status & core_irq_rx_path_exit_lpi_mode) + priv->xstats.irq_rx_path_exit_lpi_mode_n++; + } + } + /* To handle DMA interrupts */ stmmac_dma_interrupt(priv); return IRQ_HANDLED; @@ -2155,6 +2304,9 @@ static int __init stmmac_cmdline_opt(char *str) } else if (!strncmp(opt, "pause:", 6)) { if (kstrtoint(opt + 6, 0, &pause)) goto err; + } else if (!strncmp(opt, "eee_timer:", 6)) { + if (kstrtoint(opt + 10, 0, &eee_timer)) + goto err; #ifdef CONFIG_STMMAC_TIMER } else if (!strncmp(opt, "tmrate:", 7)) { if (kstrtoint(opt + 7, 0, &tmrate)) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 20eb5026c49c..7d36163d0d23 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -156,6 +156,8 @@ static int stmmac_pltfr_probe(struct platform_device *pdev) if (priv->wol_irq == -ENXIO) priv->wol_irq = priv->dev->irq; + priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi"); + platform_set_drvdata(pdev, priv->dev); pr_debug("STMMAC platform driver registration completed"); -- cgit v1.2.3 From a59a4d1921664da63d801ba477950114c71c88c9 Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Wed, 27 Jun 2012 21:14:38 +0000 Subject: phy: add the EEE support and the way to access to the MMD registers. This patch adds the support for the Energy-Efficient Ethernet (EEE) to the Physical Abstraction Layer. To support the EEE we have to access to the MMD registers 3.20 and 7.60/61. So two new functions have been added to read/write the MMD registers (clause 45). An Ethernet driver (I tested the stmmac) can invoke the phy_init_eee to properly check if the EEE is supported by the PHYs and it can also set the clock stop enable bit in the 3.0 register. The phy_get_eee_err can be used for reporting the number of time where the PHY failed to complete its normal wake sequence. In the end, this patch also adds the EEE ethtool support implementing: o phy_ethtool_set_eee o phy_ethtool_get_eee v1: initial patch v2: fixed some errors especially on naming convention v3: renamed again the mmd read/write functions thank to Ben's feedback v4: moved file to phy.c and added the ethtool support. v5: fixed phy_adv_to_eee, phy_eee_to_supported, phy_eee_to_adv return values according to ethtool API (thanks to Ben's feedback). Renamed some macros to avoid too long names. v6: fixed kernel-doc comments to be properly parsed. Fixed the phy_init_eee function: we need to check which link mode was autonegotiated and then the corresponding bits in 7.60 and 7.61 registers. v7: reviewed the way to get the negotiated settings. v8: fixed a problem in the phy_init_eee return value erroneously added when included the phy_read_status call. v9: do not remove the MDIO_AN_EEE_ADV_100TX and MDIO_AN_EEE_ADV_1000T and fixed the eee_{cap,lp,adv} declaration as "int" instead of u16. Signed-off-by: Giuseppe Cavallaro Reviewed-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2e1c23731ded..7ca2ff97c368 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -967,3 +968,283 @@ void phy_state_machine(struct work_struct *work) schedule_delayed_work(&phydev->state_queue, PHY_STATE_TIME * HZ); } + +static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + /* Write the desired MMD Devad */ + bus->write(bus, addr, MII_MMD_CTRL, devad); + + /* Write the desired MMD register address */ + bus->write(bus, addr, MII_MMD_DATA, prtad); + + /* Select the Function : DATA with no post increment */ + bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); +} + +/** + * phy_read_mmd_indirect - reads data from the MMD registers + * @bus: the target MII bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * + * Description: it reads data from the MMD registers (clause 22 to access to + * clause 45) of the specified phy address. + * To read these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Read reg 14 // Read MMD data + */ +static int phy_read_mmd_indirect(struct mii_bus *bus, int prtad, int devad, + int addr) +{ + u32 ret; + + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Read the content of the MMD's selected register */ + ret = bus->read(bus, addr, MII_MMD_DATA); + + return ret; +} + +/** + * phy_write_mmd_indirect - writes data to the MMD registers + * @bus: the target MII bus + * @prtad: MMD Address + * @devad: MMD DEVAD + * @addr: PHY address on the MII bus + * @data: data to write in the MMD register + * + * Description: Write data from the MMD registers of the specified + * phy address. + * To write these register we have: + * 1) Write reg 13 // DEVAD + * 2) Write reg 14 // MMD Address + * 3) Write reg 13 // MMD Data Command for MMD DEVAD + * 3) Write reg 14 // Write MMD data + */ +static void phy_write_mmd_indirect(struct mii_bus *bus, int prtad, int devad, + int addr, u32 data) +{ + mmd_phy_indirect(bus, prtad, devad, addr); + + /* Write the data into MMD's selected register */ + bus->write(bus, addr, MII_MMD_DATA, data); +} + +static u32 phy_eee_to_adv(u16 eee_adv) +{ + u32 adv = 0; + + if (eee_adv & MDIO_EEE_100TX) + adv |= ADVERTISED_100baseT_Full; + if (eee_adv & MDIO_EEE_1000T) + adv |= ADVERTISED_1000baseT_Full; + if (eee_adv & MDIO_EEE_10GT) + adv |= ADVERTISED_10000baseT_Full; + if (eee_adv & MDIO_EEE_1000KX) + adv |= ADVERTISED_1000baseKX_Full; + if (eee_adv & MDIO_EEE_10GKX4) + adv |= ADVERTISED_10000baseKX4_Full; + if (eee_adv & MDIO_EEE_10GKR) + adv |= ADVERTISED_10000baseKR_Full; + + return adv; +} + +static u32 phy_eee_to_supported(u16 eee_caported) +{ + u32 supported = 0; + + if (eee_caported & MDIO_EEE_100TX) + supported |= SUPPORTED_100baseT_Full; + if (eee_caported & MDIO_EEE_1000T) + supported |= SUPPORTED_1000baseT_Full; + if (eee_caported & MDIO_EEE_10GT) + supported |= SUPPORTED_10000baseT_Full; + if (eee_caported & MDIO_EEE_1000KX) + supported |= SUPPORTED_1000baseKX_Full; + if (eee_caported & MDIO_EEE_10GKX4) + supported |= SUPPORTED_10000baseKX4_Full; + if (eee_caported & MDIO_EEE_10GKR) + supported |= SUPPORTED_10000baseKR_Full; + + return supported; +} + +static u16 phy_adv_to_eee(u32 adv) +{ + u16 reg = 0; + + if (adv & ADVERTISED_100baseT_Full) + reg |= MDIO_EEE_100TX; + if (adv & ADVERTISED_1000baseT_Full) + reg |= MDIO_EEE_1000T; + if (adv & ADVERTISED_10000baseT_Full) + reg |= MDIO_EEE_10GT; + if (adv & ADVERTISED_1000baseKX_Full) + reg |= MDIO_EEE_1000KX; + if (adv & ADVERTISED_10000baseKX4_Full) + reg |= MDIO_EEE_10GKX4; + if (adv & ADVERTISED_10000baseKR_Full) + reg |= MDIO_EEE_10GKR; + + return reg; +} + +/** + * phy_init_eee - init and check the EEE feature + * @phydev: target phy_device struct + * @clk_stop_enable: PHY may stop the clock during LPI + * + * Description: it checks if the Energy-Efficient Ethernet (EEE) + * is supported by looking at the MMD registers 3.20 and 7.60/61 + * and it programs the MMD register 3.0 setting the "Clock stop enable" + * bit if required. + */ +int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) +{ + int ret = -EPROTONOSUPPORT; + + /* According to 802.3az,the EEE is supported only in full duplex-mode. + * Also EEE feature is active when core is operating with MII, GMII + * or RGMII. + */ + if ((phydev->duplex == DUPLEX_FULL) && + ((phydev->interface == PHY_INTERFACE_MODE_MII) || + (phydev->interface == PHY_INTERFACE_MODE_GMII) || + (phydev->interface == PHY_INTERFACE_MODE_RGMII))) { + int eee_lp, eee_cap, eee_adv; + u32 lp, cap, adv; + int idx, status; + + /* Read phy status to properly get the right settings */ + status = phy_read_status(phydev); + if (status) + return status; + + /* First check if the EEE ability is supported */ + eee_cap = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + MDIO_MMD_PCS, phydev->addr); + if (eee_cap < 0) + return eee_cap; + + cap = phy_eee_to_supported(eee_cap); + if (!cap) + goto eee_exit; + + /* Check which link settings negotiated and verify it in + * the EEE advertising registers. + */ + eee_lp = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + MDIO_MMD_AN, phydev->addr); + if (eee_lp < 0) + return eee_lp; + + eee_adv = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (eee_adv < 0) + return eee_adv; + + adv = phy_eee_to_adv(eee_adv); + lp = phy_eee_to_adv(eee_lp); + idx = phy_find_setting(phydev->speed, phydev->duplex); + if ((lp & adv & settings[idx].setting)) + goto eee_exit; + + if (clk_stop_enable) { + /* Configure the PHY to stop receiving xMII + * clock while it is signaling LPI. + */ + int val = phy_read_mmd_indirect(phydev->bus, MDIO_CTRL1, + MDIO_MMD_PCS, + phydev->addr); + if (val < 0) + return val; + + val |= MDIO_PCS_CTRL1_CLKSTOP_EN; + phy_write_mmd_indirect(phydev->bus, MDIO_CTRL1, + MDIO_MMD_PCS, phydev->addr, val); + } + + ret = 0; /* EEE supported */ + } + +eee_exit: + return ret; +} +EXPORT_SYMBOL(phy_init_eee); + +/** + * phy_get_eee_err - report the EEE wake error count + * @phydev: target phy_device struct + * + * Description: it is to report the number of time where the PHY + * failed to complete its normal wake sequence. + */ +int phy_get_eee_err(struct phy_device *phydev) +{ + return phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_WK_ERR, + MDIO_MMD_PCS, phydev->addr); + +} +EXPORT_SYMBOL(phy_get_eee_err); + +/** + * phy_ethtool_get_eee - get EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it reportes the Supported/Advertisement/LP Advertisement + * capabilities. + */ +int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data) +{ + int val; + + /* Get Supported EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_PCS_EEE_ABLE, + MDIO_MMD_PCS, phydev->addr); + if (val < 0) + return val; + data->supported = phy_eee_to_supported(val); + + /* Get advertisement EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + data->advertised = phy_eee_to_adv(val); + + /* Get LP advertisement EEE */ + val = phy_read_mmd_indirect(phydev->bus, MDIO_AN_EEE_LPABLE, + MDIO_MMD_AN, phydev->addr); + if (val < 0) + return val; + data->lp_advertised = phy_eee_to_adv(val); + + return 0; +} +EXPORT_SYMBOL(phy_ethtool_get_eee); + +/** + * phy_ethtool_set_eee - set EEE supported and status + * @phydev: target phy_device struct + * @data: ethtool_eee data + * + * Description: it is to program the Advertisement EEE register. + */ +int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data) +{ + int val; + + val = phy_adv_to_eee(data->advertised); + phy_write_mmd_indirect(phydev->bus, MDIO_AN_EEE_ADV, MDIO_MMD_AN, + phydev->addr, val); + + return 0; +} +EXPORT_SYMBOL(phy_ethtool_set_eee); -- cgit v1.2.3 From 17621e11fda095459e2f986c019f52686c7a4ffb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:38 +0200 Subject: ACPI / PM: Drop pm_message_t argument from device suspend callback None of the drivers implementing the ACPI device suspend callback uses the pm_message_t argument of it, so this argument may be dropped entirely from that callback. This will simplify switching the ACPI bus type to PM handling based on struct dev_pm_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.c | 4 ++-- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/scan.c | 2 +- drivers/platform/x86/hp_accel.c | 2 +- drivers/platform/x86/sony-laptop.c | 2 +- drivers/platform/x86/toshiba_acpi.c | 3 +-- 6 files changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 0f0356ca1a9e..ed1e58dc19d8 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); -static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state); +static int acpi_fan_suspend(struct acpi_device *device); static int acpi_fan_resume(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { @@ -183,7 +183,7 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } -static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state) +static int acpi_fan_suspend(struct acpi_device *device) { if (!device) return -EINVAL; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 47a8caa89dbe..e28af8d38239 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -241,7 +241,7 @@ static void acpi_idle_bm_rld_restore(void) acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); } -int acpi_processor_suspend(struct acpi_device * device, pm_message_t state) +int acpi_processor_suspend(struct acpi_device * device) { if (acpi_idle_suspend == 1) return 0; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c8a1f3b68110..ec65ec9c529f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -296,7 +296,7 @@ static int acpi_device_suspend(struct device *dev, pm_message_t state) struct acpi_driver *acpi_drv = acpi_dev->driver; if (acpi_drv && acpi_drv->ops.suspend) - return acpi_drv->ops.suspend(acpi_dev, state); + return acpi_drv->ops.suspend(acpi_dev); return 0; } diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 22b2dfa73148..c9e052033173 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -353,7 +353,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) #ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +static int lis3lv02d_suspend(struct acpi_device *device) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(&lis3_dev); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 210d4ae547c2..2b604f376fdb 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4243,7 +4243,7 @@ err_free_resources: return result; } -static int sony_pic_suspend(struct acpi_device *device, pm_message_t state) +static int sony_pic_suspend(struct acpi_device *device) { if (sony_pic_disable(device)) return -ENXIO; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index dab10f6edcd4..fd90b6da0a4b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1296,8 +1296,7 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) } } -static int toshiba_acpi_suspend(struct acpi_device *acpi_dev, - pm_message_t state) +static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); u32 result; -- cgit v1.2.3 From 707156e600dbfd89e129239ee1d1d934cbe2119c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:49 +0200 Subject: ACPI / PM: Make acpi_bus_type use struct dev_pm_ops for PM handling Make the acpi_bus_type bus type define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct bus_type. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ec65ec9c529f..7c37be53334d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,7 +290,7 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -static int acpi_device_suspend(struct device *dev, pm_message_t state) +static int acpi_device_suspend(struct device *dev) { struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_driver *acpi_drv = acpi_dev->driver; @@ -310,6 +310,8 @@ static int acpi_device_resume(struct device *dev) return 0; } +static SIMPLE_DEV_PM_OPS(acpi_bus_pm, acpi_device_suspend, acpi_device_resume); + static int acpi_bus_match(struct device *dev, struct device_driver *drv) { struct acpi_device *acpi_dev = to_acpi_device(dev); @@ -441,12 +443,11 @@ static int acpi_device_remove(struct device * dev) struct bus_type acpi_bus_type = { .name = "acpi", - .suspend = acpi_device_suspend, - .resume = acpi_device_resume, .match = acpi_bus_match, .probe = acpi_device_probe, .remove = acpi_device_remove, .uevent = acpi_device_uevent, + .pm = &acpi_bus_pm, }; static int acpi_device_register(struct acpi_device *device) -- cgit v1.2.3 From 67699c5f0c68f5dc49e92b172d372c99cdd2bf09 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:25:59 +0200 Subject: ACPI / PM: Make acpi_bus_type use driver struct dev_pm_ops callbacks Modify acpi_bus_type so that it executes PM callbacks provided by drivers through their struct dev_pm_ops objects, if present, while still allowing the legacy ACPI PM callbacks to take precedence. This will make it possible to convert ACPI drivers one by one to handling PM through struct dev_pm_ops instead of the legacy way. The code added by this change is temporary and will be removed when all of the drivers in question have been switched over to the PM handling based on struct dev_pm_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 60 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7c37be53334d..af924ba85148 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,27 +290,61 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -static int acpi_device_suspend(struct device *dev) +#define ACPI_DEV_PM_CALLBACK(dev, callback, legacy_cb) \ +({ \ + struct acpi_device *__acpi_dev = to_acpi_device(dev); \ + struct acpi_driver *__acpi_drv = __acpi_dev->driver; \ + struct device_driver *__drv = dev->driver; \ + int __ret; \ + \ + if (__acpi_drv && __acpi_drv->ops.legacy_cb) \ + __ret = __acpi_drv->ops.legacy_cb(__acpi_dev); \ + else if (__drv && __drv->pm && __drv->pm->callback) \ + __ret = __drv->pm->callback(dev); \ + else \ + __ret = 0; \ + \ + __ret; \ +}) + +static int acpi_pm_suspend(struct device *dev) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = acpi_dev->driver; + return ACPI_DEV_PM_CALLBACK(dev, suspend, suspend); +} - if (acpi_drv && acpi_drv->ops.suspend) - return acpi_drv->ops.suspend(acpi_dev); - return 0; +static int acpi_pm_resume(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, resume, resume); } -static int acpi_device_resume(struct device *dev) +static int acpi_pm_freeze(struct device *dev) { - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_driver *acpi_drv = acpi_dev->driver; + return ACPI_DEV_PM_CALLBACK(dev, freeze, suspend); +} - if (acpi_drv && acpi_drv->ops.resume) - return acpi_drv->ops.resume(acpi_dev); - return 0; +static int acpi_pm_thaw(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, thaw, resume); +} + +static int acpi_pm_poweroff(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, poweroff, suspend); } -static SIMPLE_DEV_PM_OPS(acpi_bus_pm, acpi_device_suspend, acpi_device_resume); +static int acpi_pm_restore(struct device *dev) +{ + return ACPI_DEV_PM_CALLBACK(dev, restore, resume); +} + +static const struct dev_pm_ops acpi_bus_pm = { + .suspend = acpi_pm_suspend, + .resume = acpi_pm_resume, + .freeze = acpi_pm_freeze, + .thaw = acpi_pm_thaw, + .poweroff = acpi_pm_poweroff, + .restore = acpi_pm_restore, +}; static int acpi_bus_match(struct device *dev, struct device_driver *drv) { -- cgit v1.2.3 From 62fcbdd95ccabcff7b41a859032496f6ca492045 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:07 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the fan driver Make the ACPI fan driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index ed1e58dc19d8..669d9ee80d16 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -46,8 +46,6 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); -static int acpi_fan_suspend(struct acpi_device *device); -static int acpi_fan_resume(struct acpi_device *device); static const struct acpi_device_id fan_device_ids[] = { {"PNP0C0B", 0}, @@ -55,6 +53,10 @@ static const struct acpi_device_id fan_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, fan_device_ids); +static int acpi_fan_suspend(struct device *dev); +static int acpi_fan_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_fan_pm, acpi_fan_suspend, acpi_fan_resume); + static struct acpi_driver acpi_fan_driver = { .name = "fan", .class = ACPI_FAN_CLASS, @@ -62,9 +64,8 @@ static struct acpi_driver acpi_fan_driver = { .ops = { .add = acpi_fan_add, .remove = acpi_fan_remove, - .suspend = acpi_fan_suspend, - .resume = acpi_fan_resume, }, + .drv.pm = &acpi_fan_pm, }; /* thermal cooling device callbacks */ @@ -183,24 +184,24 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } -static int acpi_fan_suspend(struct acpi_device *device) +static int acpi_fan_suspend(struct device *dev) { - if (!device) + if (!dev) return -EINVAL; - acpi_bus_set_power(device->handle, ACPI_STATE_D0); + acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0); return AE_OK; } -static int acpi_fan_resume(struct acpi_device *device) +static int acpi_fan_resume(struct device *dev) { int result; - if (!device) + if (!dev) return -EINVAL; - result = acpi_bus_update_power(device->handle, NULL); + result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL); if (result) printk(KERN_ERR PREFIX "Error updating fan power state\n"); -- cgit v1.2.3 From 167cffb646aa4a7f3267b07eae1af16b54bc0e9b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:18 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the thermal driver Make the ACPI thermal driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 7dbebea1ec31..21dd4c268aef 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -98,7 +98,6 @@ MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); static int acpi_thermal_add(struct acpi_device *device); static int acpi_thermal_remove(struct acpi_device *device, int type); -static int acpi_thermal_resume(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id thermal_device_ids[] = { @@ -107,6 +106,9 @@ static const struct acpi_device_id thermal_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, thermal_device_ids); +static int acpi_thermal_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, NULL, acpi_thermal_resume); + static struct acpi_driver acpi_thermal_driver = { .name = "thermal", .class = ACPI_THERMAL_CLASS, @@ -114,9 +116,9 @@ static struct acpi_driver acpi_thermal_driver = { .ops = { .add = acpi_thermal_add, .remove = acpi_thermal_remove, - .resume = acpi_thermal_resume, .notify = acpi_thermal_notify, }, + .drv.pm = &acpi_thermal_pm, }; struct acpi_thermal_state { @@ -1041,16 +1043,17 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) return 0; } -static int acpi_thermal_resume(struct acpi_device *device) +static int acpi_thermal_resume(struct device *dev) { - struct acpi_thermal *tz = NULL; + struct acpi_thermal *tz; int i, j, power_state, result; - - if (!device || !acpi_driver_data(device)) + if (!dev) return -EINVAL; - tz = acpi_driver_data(device); + tz = acpi_driver_data(to_acpi_device(dev)); + if (!tz) + return -EINVAL; for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!(&tz->trips.active[i])) -- cgit v1.2.3 From e8110b64af8b7cce96d1878276770c76cb9c01d5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:26 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in processor driver Make the ACPI processor driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_driver.c | 6 ++++-- drivers/acpi/processor_idle.c | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0734086537b8..13103aeb0c4f 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -93,6 +93,9 @@ static const struct acpi_device_id processor_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); +static SIMPLE_DEV_PM_OPS(acpi_processor_pm, + acpi_processor_suspend, acpi_processor_resume); + static struct acpi_driver acpi_processor_driver = { .name = "processor", .class = ACPI_PROCESSOR_CLASS, @@ -100,10 +103,9 @@ static struct acpi_driver acpi_processor_driver = { .ops = { .add = acpi_processor_add, .remove = acpi_processor_remove, - .suspend = acpi_processor_suspend, - .resume = acpi_processor_resume, .notify = acpi_processor_notify, }, + .drv.pm = &acpi_processor_pm, }; #define INSTALL_NOTIFY_HANDLER 1 diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index e28af8d38239..6d9ec3e0b52e 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -241,7 +241,7 @@ static void acpi_idle_bm_rld_restore(void) acpi_write_bit_register(ACPI_BITREG_BUS_MASTER_RLD, saved_bm_rld); } -int acpi_processor_suspend(struct acpi_device * device) +int acpi_processor_suspend(struct device *dev) { if (acpi_idle_suspend == 1) return 0; @@ -251,7 +251,7 @@ int acpi_processor_suspend(struct acpi_device * device) return 0; } -int acpi_processor_resume(struct acpi_device * device) +int acpi_processor_resume(struct device *dev) { if (acpi_idle_suspend == 0) return 0; -- cgit v1.2.3 From ccda70698057fd8dae7beeac147cdeead33dd543 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:35 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the AC driver Make the ACPI AC adapter driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ac.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 6512b20aeccd..ff9f6bd48301 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -61,7 +61,6 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file); static int acpi_ac_add(struct acpi_device *device); static int acpi_ac_remove(struct acpi_device *device, int type); -static int acpi_ac_resume(struct acpi_device *device); static void acpi_ac_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id ac_device_ids[] = { @@ -70,6 +69,9 @@ static const struct acpi_device_id ac_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, ac_device_ids); +static int acpi_ac_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume); + static struct acpi_driver acpi_ac_driver = { .name = "ac", .class = ACPI_AC_CLASS, @@ -78,9 +80,9 @@ static struct acpi_driver acpi_ac_driver = { .ops = { .add = acpi_ac_add, .remove = acpi_ac_remove, - .resume = acpi_ac_resume, .notify = acpi_ac_notify, }, + .drv.pm = &acpi_ac_pm, }; struct acpi_ac { @@ -309,13 +311,18 @@ static int acpi_ac_add(struct acpi_device *device) return result; } -static int acpi_ac_resume(struct acpi_device *device) +static int acpi_ac_resume(struct device *dev) { struct acpi_ac *ac; unsigned old_state; - if (!device || !acpi_driver_data(device)) + + if (!dev) return -EINVAL; - ac = acpi_driver_data(device); + + ac = acpi_driver_data(to_acpi_device(dev)); + if (!ac) + return -EINVAL; + old_state = ac->state; if (acpi_ac_get_state(ac)) return 0; -- cgit v1.2.3 From a6f50dc8ef26819c510cda84477dd60e3d1c90bb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:43 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the battery driver Make the ACPI battery driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 7dd3f9fb9f3f..023f9c8534d0 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1044,17 +1044,24 @@ static int acpi_battery_remove(struct acpi_device *device, int type) } /* this is needed to learn about changes made in suspended state */ -static int acpi_battery_resume(struct acpi_device *device) +static int acpi_battery_resume(struct device *dev) { struct acpi_battery *battery; - if (!device) + + if (!dev) return -EINVAL; - battery = acpi_driver_data(device); + + battery = acpi_driver_data(to_acpi_device(dev)); + if (!battery) + return -EINVAL; + battery->update_time = 0; acpi_battery_update(battery); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume); + static struct acpi_driver acpi_battery_driver = { .name = "battery", .class = ACPI_BATTERY_CLASS, @@ -1062,10 +1069,10 @@ static struct acpi_driver acpi_battery_driver = { .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = acpi_battery_add, - .resume = acpi_battery_resume, .remove = acpi_battery_remove, .notify = acpi_battery_notify, }, + .drv.pm = &acpi_battery_pm, }; static void __init acpi_battery_init_async(void *unused, async_cookie_t cookie) -- cgit v1.2.3 From 1be532de834ab7b18ce20dc2e56e724770beb79d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:51 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the button driver Make the ACPI button driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index d27d072472f9..79d4c22f7a6d 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -76,19 +76,21 @@ MODULE_DEVICE_TABLE(acpi, button_device_ids); static int acpi_button_add(struct acpi_device *device); static int acpi_button_remove(struct acpi_device *device, int type); -static int acpi_button_resume(struct acpi_device *device); static void acpi_button_notify(struct acpi_device *device, u32 event); +static int acpi_button_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_button_pm, NULL, acpi_button_resume); + static struct acpi_driver acpi_button_driver = { .name = "button", .class = ACPI_BUTTON_CLASS, .ids = button_device_ids, .ops = { .add = acpi_button_add, - .resume = acpi_button_resume, .remove = acpi_button_remove, .notify = acpi_button_notify, }, + .drv.pm = &acpi_button_pm, }; struct acpi_button { @@ -308,8 +310,9 @@ static void acpi_button_notify(struct acpi_device *device, u32 event) } } -static int acpi_button_resume(struct acpi_device *device) +static int acpi_button_resume(struct device *dev) { + struct acpi_device *device = to_acpi_device(dev); struct acpi_button *button = acpi_driver_data(device); if (button->type == ACPI_BUTTON_TYPE_LID) -- cgit v1.2.3 From e579e2dd8f056cfeaf4d2b313a35ddafb3c4e531 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:26:59 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the power driver Make the ACPI power resource driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/power.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index dd6d6a3c6780..894d45c6bc67 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -60,7 +60,6 @@ ACPI_MODULE_NAME("power"); static int acpi_power_add(struct acpi_device *device); static int acpi_power_remove(struct acpi_device *device, int type); -static int acpi_power_resume(struct acpi_device *device); static const struct acpi_device_id power_device_ids[] = { {ACPI_POWER_HID, 0}, @@ -68,6 +67,9 @@ static const struct acpi_device_id power_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, power_device_ids); +static int acpi_power_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_power_pm, NULL, acpi_power_resume); + static struct acpi_driver acpi_power_driver = { .name = "power", .class = ACPI_POWER_CLASS, @@ -75,8 +77,8 @@ static struct acpi_driver acpi_power_driver = { .ops = { .add = acpi_power_add, .remove = acpi_power_remove, - .resume = acpi_power_resume, }, + .drv.pm = &acpi_power_pm, }; /* @@ -771,14 +773,16 @@ static int acpi_power_remove(struct acpi_device *device, int type) return 0; } -static int acpi_power_resume(struct acpi_device *device) +static int acpi_power_resume(struct device *dev) { int result = 0, state; + struct acpi_device *device; struct acpi_power_resource *resource; - if (!device) + if (!dev) return -EINVAL; + device = to_acpi_device(dev); resource = acpi_driver_data(device); if (!resource) return -EINVAL; -- cgit v1.2.3 From d202f77d2eb707d03a16adf952a397675cb2fc81 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:07 +0200 Subject: ACPI: Use struct dev_pm_ops for power management in the SBS driver Make the ACPI Smart Battery System driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sbs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 6e36d0c0057c..c0b9aa5faf4c 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -988,16 +988,18 @@ static void acpi_sbs_rmdirs(void) #endif } -static int acpi_sbs_resume(struct acpi_device *device) +static int acpi_sbs_resume(struct device *dev) { struct acpi_sbs *sbs; - if (!device) + if (!dev) return -EINVAL; - sbs = device->driver_data; + sbs = to_acpi_device(dev)->driver_data; acpi_sbs_callback(sbs); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume); + static struct acpi_driver acpi_sbs_driver = { .name = "sbs", .class = ACPI_SBS_CLASS, @@ -1005,8 +1007,8 @@ static struct acpi_driver acpi_sbs_driver = { .ops = { .add = acpi_sbs_add, .remove = acpi_sbs_remove, - .resume = acpi_sbs_resume, }, + .drv.pm = &acpi_sbs_pm, }; static int __init acpi_sbs_init(void) -- cgit v1.2.3 From 43d2fd3b9d5be9be879179bec4f08c0c47d1ccd7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:16 +0200 Subject: toshiba_acpi: Use struct dev_pm_ops for power management Make the toshiba_acpi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/toshiba_acpi.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index fd90b6da0a4b..c13ba5bac93f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -1296,9 +1296,9 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) } } -static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) +static int toshiba_acpi_suspend(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); u32 result; if (dev->hotkey_dev) @@ -1307,9 +1307,9 @@ static int toshiba_acpi_suspend(struct acpi_device *acpi_dev) return 0; } -static int toshiba_acpi_resume(struct acpi_device *acpi_dev) +static int toshiba_acpi_resume(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); u32 result; if (dev->hotkey_dev) @@ -1318,6 +1318,9 @@ static int toshiba_acpi_resume(struct acpi_device *acpi_dev) return 0; } +static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, + toshiba_acpi_suspend, toshiba_acpi_resume); + static struct acpi_driver toshiba_acpi_driver = { .name = "Toshiba ACPI driver", .owner = THIS_MODULE, @@ -1327,9 +1330,8 @@ static struct acpi_driver toshiba_acpi_driver = { .add = toshiba_acpi_add, .remove = toshiba_acpi_remove, .notify = toshiba_acpi_notify, - .suspend = toshiba_acpi_suspend, - .resume = toshiba_acpi_resume, }, + .drv.pm = &toshiba_acpi_pm, }; static int __init toshiba_acpi_init(void) -- cgit v1.2.3 From 4ce05b42603195386fa783c4f070ecc8682c189f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:25 +0200 Subject: hp_accel: Use struct dev_pm_ops for power management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the hp_accel driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Reviewed-by: Éric Piel --- drivers/platform/x86/hp_accel.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index c9e052033173..f4d91154ad67 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -353,20 +353,22 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) #ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device) +static int lis3lv02d_suspend(struct device *dev) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(&lis3_dev); return 0; } -static int lis3lv02d_resume(struct acpi_device *device) +static int lis3lv02d_resume(struct device *dev) { return lis3lv02d_poweron(&lis3_dev); } + +static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume); +#define HP_ACCEL_PM (&hp_accel_pm) #else -#define lis3lv02d_suspend NULL -#define lis3lv02d_resume NULL +#define HP_ACCEL_PM NULL #endif /* For the HP MDPS aka 3D Driveguard */ @@ -377,9 +379,8 @@ static struct acpi_driver lis3lv02d_driver = { .ops = { .add = lis3lv02d_add, .remove = lis3lv02d_remove, - .suspend = lis3lv02d_suspend, - .resume = lis3lv02d_resume, - } + }, + .drv.pm = HP_ACCEL_PM, }; static int __init lis3lv02d_init_module(void) -- cgit v1.2.3 From bb6b98d6e4f9aada309c54ebd6df1cc6151f3af1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:33 +0200 Subject: sony-laptop: Use struct dev_pm_ops for power management Make the sony-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/sony-laptop.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 2b604f376fdb..c7a236240785 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1452,7 +1452,7 @@ static void sony_nc_function_resume(void) &result); } -static int sony_nc_resume(struct acpi_device *device) +static int sony_nc_resume(struct device *dev) { struct sony_nc_value *item; acpi_handle handle; @@ -1484,6 +1484,8 @@ static int sony_nc_resume(struct acpi_device *device) return 0; } +static SIMPLE_DEV_PM_OPS(sony_nc_pm, NULL, sony_nc_resume); + static void sony_nc_rfkill_cleanup(void) { int i; @@ -2728,9 +2730,9 @@ static struct acpi_driver sony_nc_driver = { .ops = { .add = sony_nc_add, .remove = sony_nc_remove, - .resume = sony_nc_resume, .notify = sony_nc_notify, }, + .drv.pm = &sony_nc_pm, }; /*********** SPIC (SNY6001) Device ***********/ @@ -4243,19 +4245,22 @@ err_free_resources: return result; } -static int sony_pic_suspend(struct acpi_device *device) +static int sony_pic_suspend(struct device *dev) { - if (sony_pic_disable(device)) + if (sony_pic_disable(to_acpi_device(dev))) return -ENXIO; return 0; } -static int sony_pic_resume(struct acpi_device *device) +static int sony_pic_resume(struct device *dev) { - sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + sony_pic_enable(to_acpi_device(dev), + spic_dev.cur_ioport, spic_dev.cur_irq); return 0; } +static SIMPLE_DEV_PM_OPS(sony_pic_pm, sony_pic_suspend, sony_pic_resume); + static const struct acpi_device_id sony_pic_device_ids[] = { {SONY_PIC_HID, 0}, {"", 0}, @@ -4269,9 +4274,8 @@ static struct acpi_driver sony_pic_driver = { .ops = { .add = sony_pic_add, .remove = sony_pic_remove, - .suspend = sony_pic_suspend, - .resume = sony_pic_resume, }, + .drv.pm = &sony_pic_pm, }; static struct dmi_system_id __initdata sonypi_dmi_table[] = { -- cgit v1.2.3 From a7377419624c31d71e35485251f8548c728d5999 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:40 +0200 Subject: panasonic-laptop: Use struct dev_pm_ops for power management Make the panasonic-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/panasonic-laptop.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index ffff8b4b4949..24480074bcf0 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -177,7 +177,6 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, static int acpi_pcc_hotkey_add(struct acpi_device *device); static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type); -static int acpi_pcc_hotkey_resume(struct acpi_device *device); static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id pcc_device_ids[] = { @@ -189,6 +188,9 @@ static const struct acpi_device_id pcc_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, pcc_device_ids); +static int acpi_pcc_hotkey_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume); + static struct acpi_driver acpi_pcc_driver = { .name = ACPI_PCC_DRIVER_NAME, .class = ACPI_PCC_CLASS, @@ -196,9 +198,9 @@ static struct acpi_driver acpi_pcc_driver = { .ops = { .add = acpi_pcc_hotkey_add, .remove = acpi_pcc_hotkey_remove, - .resume = acpi_pcc_hotkey_resume, .notify = acpi_pcc_hotkey_notify, }, + .drv.pm = &acpi_pcc_hotkey_pm, }; static const struct key_entry panasonic_keymap[] = { @@ -538,11 +540,15 @@ static void acpi_pcc_destroy_input(struct pcc_acpi *pcc) /* kernel module interface */ -static int acpi_pcc_hotkey_resume(struct acpi_device *device) +static int acpi_pcc_hotkey_resume(struct device *dev) { - struct pcc_acpi *pcc = acpi_driver_data(device); + struct pcc_acpi *pcc; + + if (!dev) + return -EINVAL; - if (device == NULL || pcc == NULL) + pcc = acpi_driver_data(to_acpi_device(dev)); + if (!pcc) return -EINVAL; ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n", -- cgit v1.2.3 From d69239ae5847b29ea1902b2dd86f5afa41144ba3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:48 +0200 Subject: toshiba_bluetooth: Use struct dev_pm_ops for power management Make the toshiba_bluetooth driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Reviewed-by: Vikram Dhillon --- drivers/platform/x86/toshiba_bluetooth.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 5fb7186694df..715a43cb5e3c 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -34,7 +34,6 @@ MODULE_LICENSE("GPL"); static int toshiba_bt_rfkill_add(struct acpi_device *device); static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type); static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); -static int toshiba_bt_resume(struct acpi_device *device); static const struct acpi_device_id bt_device_ids[] = { { "TOS6205", 0}, @@ -42,6 +41,9 @@ static const struct acpi_device_id bt_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, bt_device_ids); +static int toshiba_bt_resume(struct device *dev); +static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume); + static struct acpi_driver toshiba_bt_rfkill_driver = { .name = "Toshiba BT", .class = "Toshiba", @@ -50,9 +52,9 @@ static struct acpi_driver toshiba_bt_rfkill_driver = { .add = toshiba_bt_rfkill_add, .remove = toshiba_bt_rfkill_remove, .notify = toshiba_bt_rfkill_notify, - .resume = toshiba_bt_resume, }, .owner = THIS_MODULE, + .drv.pm = &toshiba_bt_pm, }; @@ -88,9 +90,9 @@ static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) toshiba_bluetooth_enable(device->handle); } -static int toshiba_bt_resume(struct acpi_device *device) +static int toshiba_bt_resume(struct device *dev) { - return toshiba_bluetooth_enable(device->handle); + return toshiba_bluetooth_enable(to_acpi_device(dev)->handle); } static int toshiba_bt_rfkill_add(struct acpi_device *device) -- cgit v1.2.3 From 44cb98c48e19f506e178c519f5da2c23a156e002 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:27:55 +0200 Subject: xo15-ebook: Use struct dev_pm_ops for power management Make the xo15-ebook driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/xo15-ebook.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index fad153dc0355..849c07c13bf6 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -77,11 +77,13 @@ static void ebook_switch_notify(struct acpi_device *device, u32 event) } } -static int ebook_switch_resume(struct acpi_device *device) +static int ebook_switch_resume(struct device *dev) { - return ebook_send_state(device); + return ebook_send_state(to_acpi_device(dev)); } +static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); + static int ebook_switch_add(struct acpi_device *device) { struct ebook_switch *button; @@ -161,10 +163,10 @@ static struct acpi_driver xo15_ebook_driver = { .ids = ebook_device_ids, .ops = { .add = ebook_switch_add, - .resume = ebook_switch_resume, .remove = ebook_switch_remove, .notify = ebook_switch_notify, }, + .drv.pm = &ebook_switch_pm, }; static int __init xo15_ebook_init(void) -- cgit v1.2.3 From 81bc495401955bf7a62d04f8c794718476a2b093 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:39:48 +0200 Subject: classmate-laptop: Use struct dev_pm_ops for power management Make the classmate-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo --- drivers/platform/x86/classmate-laptop.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 94f93b621d7b..e2230a2b2f8e 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -362,15 +362,18 @@ static int cmpc_tablet_remove(struct acpi_device *acpi, int type) return cmpc_remove_acpi_notify_device(acpi); } -static int cmpc_tablet_resume(struct acpi_device *acpi) +static int cmpc_tablet_resume(struct device *dev) { - struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); + unsigned long long val = 0; - if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) + if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) input_report_switch(inputdev, SW_TABLET_MODE, !val); return 0; } +static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume); + static const struct acpi_device_id cmpc_tablet_device_ids[] = { {CMPC_TABLET_HID, 0}, {"", 0} @@ -384,9 +387,9 @@ static struct acpi_driver cmpc_tablet_acpi_driver = { .ops = { .add = cmpc_tablet_add, .remove = cmpc_tablet_remove, - .resume = cmpc_tablet_resume, .notify = cmpc_tablet_handler, - } + }, + .drv.pm = &cmpc_tablet_pm, }; -- cgit v1.2.3 From d26c844bc1c5e9745deffccae8d205e9b77a0fb1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:39:57 +0200 Subject: fujitsu-tablet: Use struct dev_pm_ops for power management Make the fujitsu-tablet driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/fujitsu-tablet.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index da267eae8ba8..d2e41735a47b 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -440,12 +440,14 @@ static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type) return 0; } -static int acpi_fujitsu_resume(struct acpi_device *adev) +static int acpi_fujitsu_resume(struct device *dev) { fujitsu_reset(); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume); + static struct acpi_driver acpi_fujitsu_driver = { .name = MODULENAME, .class = "hotkey", @@ -453,8 +455,8 @@ static struct acpi_driver acpi_fujitsu_driver = { .ops = { .add = acpi_fujitsu_add, .remove = acpi_fujitsu_remove, - .resume = acpi_fujitsu_resume, - } + }, + .drv.pm = &acpi_fujitsu_pm, }; static int __init fujitsu_module_init(void) -- cgit v1.2.3 From c5dec0182256361a3f823316e8fb85263f76efe7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:05 +0200 Subject: acpi_power_meter: Use struct dev_pm_ops for power management Make the ACPI power meter driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki --- drivers/hwmon/acpi_power_meter.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 34ad5a27a7e9..e3fcf8146834 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -929,20 +929,25 @@ static int acpi_power_meter_remove(struct acpi_device *device, int type) return 0; } -static int acpi_power_meter_resume(struct acpi_device *device) +static int acpi_power_meter_resume(struct device *dev) { struct acpi_power_meter_resource *resource; - if (!device || !acpi_driver_data(device)) + if (!dev) + return -EINVAL; + + resource = acpi_driver_data(to_acpi_device(dev)); + if (!resource) return -EINVAL; - resource = acpi_driver_data(device); free_capabilities(resource); read_capabilities(resource); return 0; } +static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume); + static struct acpi_driver acpi_power_meter_driver = { .name = "power_meter", .class = ACPI_POWER_METER_CLASS, @@ -950,9 +955,9 @@ static struct acpi_driver acpi_power_meter_driver = { .ops = { .add = acpi_power_meter_add, .remove = acpi_power_meter_remove, - .resume = acpi_power_meter_resume, .notify = acpi_power_meter_notify, }, + .drv.pm = &acpi_power_meter_pm, }; /* Module init/exit routines */ -- cgit v1.2.3 From 13db85528fd606b2dfd8f1a5952158e4ad6ce51a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:13 +0200 Subject: ACPI / PM: Do not execute legacy driver PM callbacks Since all ACPI drivers in the tree should have been switched to power management handling based on struct dev_pm_ops, modify the ACPI bus type driver so that is doesn't execute legacy driver power management callbacks from the functions pointed to by the members of the acpi_bus_pm structure. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index af924ba85148..c384e59c3d9a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,51 +290,45 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -#define ACPI_DEV_PM_CALLBACK(dev, callback, legacy_cb) \ +#define ACPI_DEV_PM_CALLBACK(dev, callback) \ ({ \ - struct acpi_device *__acpi_dev = to_acpi_device(dev); \ - struct acpi_driver *__acpi_drv = __acpi_dev->driver; \ struct device_driver *__drv = dev->driver; \ - int __ret; \ + int __ret = 0; \ \ - if (__acpi_drv && __acpi_drv->ops.legacy_cb) \ - __ret = __acpi_drv->ops.legacy_cb(__acpi_dev); \ - else if (__drv && __drv->pm && __drv->pm->callback) \ + if (__drv && __drv->pm && __drv->pm->callback) \ __ret = __drv->pm->callback(dev); \ - else \ - __ret = 0; \ \ __ret; \ }) static int acpi_pm_suspend(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, suspend, suspend); + return ACPI_DEV_PM_CALLBACK(dev, suspend); } static int acpi_pm_resume(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, resume, resume); + return ACPI_DEV_PM_CALLBACK(dev, resume); } static int acpi_pm_freeze(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, freeze, suspend); + return ACPI_DEV_PM_CALLBACK(dev, freeze); } static int acpi_pm_thaw(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, thaw, resume); + return ACPI_DEV_PM_CALLBACK(dev, thaw); } static int acpi_pm_poweroff(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, poweroff, suspend); + return ACPI_DEV_PM_CALLBACK(dev, poweroff); } static int acpi_pm_restore(struct device *dev) { - return ACPI_DEV_PM_CALLBACK(dev, restore, resume); + return ACPI_DEV_PM_CALLBACK(dev, restore); } static const struct dev_pm_ops acpi_bus_pm = { -- cgit v1.2.3 From d91ee328c1e8b3992ffbf84a1a984c2a7f4ebff7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 29 Jun 2012 23:40:29 +0200 Subject: ACPI / PM: Drop PM callbacks from the ACPI bus type Since the ACPI bus type's PM callbacks only execute the driver ones without doing anything else, they can be dropped, because the driver callbacks will be executed by the PM core directly if bus type (or other subsystem) callbacks are not present. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c384e59c3d9a..fdda49336560 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -290,56 +290,6 @@ static void acpi_device_release(struct device *dev) kfree(acpi_dev); } -#define ACPI_DEV_PM_CALLBACK(dev, callback) \ -({ \ - struct device_driver *__drv = dev->driver; \ - int __ret = 0; \ - \ - if (__drv && __drv->pm && __drv->pm->callback) \ - __ret = __drv->pm->callback(dev); \ - \ - __ret; \ -}) - -static int acpi_pm_suspend(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, suspend); -} - -static int acpi_pm_resume(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, resume); -} - -static int acpi_pm_freeze(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, freeze); -} - -static int acpi_pm_thaw(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, thaw); -} - -static int acpi_pm_poweroff(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, poweroff); -} - -static int acpi_pm_restore(struct device *dev) -{ - return ACPI_DEV_PM_CALLBACK(dev, restore); -} - -static const struct dev_pm_ops acpi_bus_pm = { - .suspend = acpi_pm_suspend, - .resume = acpi_pm_resume, - .freeze = acpi_pm_freeze, - .thaw = acpi_pm_thaw, - .poweroff = acpi_pm_poweroff, - .restore = acpi_pm_restore, -}; - static int acpi_bus_match(struct device *dev, struct device_driver *drv) { struct acpi_device *acpi_dev = to_acpi_device(dev); @@ -475,7 +425,6 @@ struct bus_type acpi_bus_type = { .probe = acpi_device_probe, .remove = acpi_device_remove, .uevent = acpi_device_uevent, - .pm = &acpi_bus_pm, }; static int acpi_device_register(struct acpi_device *device) -- cgit v1.2.3 From fd3c3a424e43e4f85385de0c2609c490605611a6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:18:44 +0200 Subject: thinkpad_acpi: Drop pm_message_t arguments from suspend routines Multiple suspend routines in drivers/platform/x86/thinkpad_acpi.c use take pm_message_t arguments that aren't used by any of them. Make those routines take no arguments as that's what they should do. Signed-off-by: Rafael J. Wysocki Acked-by: Henrique de Moraes Holschuh Acked-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 8b5610d88418..070e555d4bd8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -277,7 +277,7 @@ struct ibm_struct { int (*write) (char *); void (*exit) (void); void (*resume) (void); - void (*suspend) (pm_message_t state); + void (*suspend) (void); void (*shutdown) (void); struct list_head all_drivers; @@ -931,7 +931,7 @@ static int tpacpi_suspend_handler(struct platform_device *pdev, &tpacpi_all_drivers, all_drivers) { if (ibm->suspend) - (ibm->suspend)(state); + (ibm->suspend)(); } return 0; @@ -3758,7 +3758,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) } } -static void hotkey_suspend(pm_message_t state) +static void hotkey_suspend(void) { /* Do these on suspend, we get the events on early resume! */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; @@ -6329,7 +6329,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) return 0; } -static void brightness_suspend(pm_message_t state) +static void brightness_suspend(void) { tpacpi_brightness_checkpoint_nvram(); } @@ -6748,7 +6748,7 @@ static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { .get = volume_alsa_mute_get, }; -static void volume_suspend(pm_message_t state) +static void volume_suspend(void) { tpacpi_volume_checkpoint_nvram(); } @@ -8107,7 +8107,7 @@ static void fan_exit(void) flush_workqueue(tpacpi_wq); } -static void fan_suspend(pm_message_t state) +static void fan_suspend(void) { int rc; -- cgit v1.2.3 From 4959a781bc1bbd0d06092caa94e33fd41033a38d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:01 +0200 Subject: thinkpad_acpi: Use struct dev_pm_ops instead of legacy PM routines Make the thinkpad_acpi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Acked-by: Henrique de Moraes Holschuh Acked-by: Matthew Garrett --- drivers/platform/x86/thinkpad_acpi.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 070e555d4bd8..d5fd4a1193f8 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -922,8 +922,7 @@ static struct input_dev *tpacpi_inputdev; static struct mutex tpacpi_inputdev_send_mutex; static LIST_HEAD(tpacpi_all_drivers); -static int tpacpi_suspend_handler(struct platform_device *pdev, - pm_message_t state) +static int tpacpi_suspend_handler(struct device *dev) { struct ibm_struct *ibm, *itmp; @@ -937,7 +936,7 @@ static int tpacpi_suspend_handler(struct platform_device *pdev, return 0; } -static int tpacpi_resume_handler(struct platform_device *pdev) +static int tpacpi_resume_handler(struct device *dev) { struct ibm_struct *ibm, *itmp; @@ -951,6 +950,9 @@ static int tpacpi_resume_handler(struct platform_device *pdev) return 0; } +static SIMPLE_DEV_PM_OPS(tpacpi_pm, + tpacpi_suspend_handler, tpacpi_resume_handler); + static void tpacpi_shutdown_handler(struct platform_device *pdev) { struct ibm_struct *ibm, *itmp; @@ -967,9 +969,8 @@ static struct platform_driver tpacpi_pdriver = { .driver = { .name = TPACPI_DRVR_NAME, .owner = THIS_MODULE, + .pm = &tpacpi_pm, }, - .suspend = tpacpi_suspend_handler, - .resume = tpacpi_resume_handler, .shutdown = tpacpi_shutdown_handler, }; -- cgit v1.2.3 From 818e14860615634646e1fe176d49e4357a4e1534 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:22 +0200 Subject: intel_ips: Remove empty legacy PM callbacks The legacy PM callbacks provided by the Intel IPS driver are empty routines returning 0, so they can be safely dropped. Signed-off-by: Rafael J. Wysocki Acked-by: Jesse Barnes --- drivers/platform/x86/intel_ips.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c index 0ffdb3cde2bb..bd89f3c8e76e 100644 --- a/drivers/platform/x86/intel_ips.c +++ b/drivers/platform/x86/intel_ips.c @@ -1697,21 +1697,6 @@ static void ips_remove(struct pci_dev *dev) dev_dbg(&dev->dev, "IPS driver removed\n"); } -#ifdef CONFIG_PM -static int ips_suspend(struct pci_dev *dev, pm_message_t state) -{ - return 0; -} - -static int ips_resume(struct pci_dev *dev) -{ - return 0; -} -#else -#define ips_suspend NULL -#define ips_resume NULL -#endif /* CONFIG_PM */ - static void ips_shutdown(struct pci_dev *dev) { } @@ -1721,8 +1706,6 @@ static struct pci_driver ips_pci_driver = { .id_table = ips_id_table, .probe = ips_probe, .remove = ips_remove, - .suspend = ips_suspend, - .resume = ips_resume, .shutdown = ips_shutdown, }; -- cgit v1.2.3 From 3c33be0b0dae390557f41a604ac621d0f3eabf9b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:35 +0200 Subject: acer-wmi: Use struct dev_pm_ops for power management Make the acer-wmi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Tested-by: Lee, Chun-Yi Acked-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index ce875dc365e5..c8f40c9c0428 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1877,8 +1877,7 @@ static int acer_platform_remove(struct platform_device *device) return 0; } -static int acer_platform_suspend(struct platform_device *dev, -pm_message_t state) +static int acer_suspend(struct device *dev) { u32 value; struct acer_data *data = &interface->data; @@ -1900,7 +1899,7 @@ pm_message_t state) return 0; } -static int acer_platform_resume(struct platform_device *device) +static int acer_resume(struct device *dev) { struct acer_data *data = &interface->data; @@ -1916,6 +1915,8 @@ static int acer_platform_resume(struct platform_device *device) return 0; } +static SIMPLE_DEV_PM_OPS(acer_pm, acer_suspend, acer_resume); + static void acer_platform_shutdown(struct platform_device *device) { struct acer_data *data = &interface->data; @@ -1931,11 +1932,10 @@ static struct platform_driver acer_platform_driver = { .driver = { .name = "acer-wmi", .owner = THIS_MODULE, + .pm = &acer_pm, }, .probe = acer_platform_probe, .remove = acer_platform_remove, - .suspend = acer_platform_suspend, - .resume = acer_platform_resume, .shutdown = acer_platform_shutdown, }; -- cgit v1.2.3 From 8ff847fc21e53be0e2ebd476ce6269a201575152 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:19:45 +0200 Subject: intel_mid_thermal: Use struct dev_pm_ops for power management Make the intel_mid_thermal driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Reviewed-by: Durgadoss R --- drivers/platform/x86/intel_mid_thermal.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 5ae9cd9c7e6e..ea7422f6fa03 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -418,23 +418,23 @@ static struct thermal_device_info *initialize_sensor(int index) /** * mid_thermal_resume - resume routine - * @pdev: platform device structure + * @dev: device structure * * mid thermal resume: re-initializes the adc. Can sleep. */ -static int mid_thermal_resume(struct platform_device *pdev) +static int mid_thermal_resume(struct device *dev) { - return mid_initialize_adc(&pdev->dev); + return mid_initialize_adc(dev); } /** * mid_thermal_suspend - suspend routine - * @pdev: platform device structure + * @dev: device structure * * mid thermal suspend implements the suspend functionality * by stopping the ADC. Can sleep. */ -static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) +static int mid_thermal_suspend(struct device *dev) { /* * This just stops the ADC and does not disable it. @@ -444,6 +444,9 @@ static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg) return configure_adc(0); } +static SIMPLE_DEV_PM_OPS(mid_thermal_pm, + mid_thermal_suspend, mid_thermal_resume); + /** * read_curr_temp - reads the current temperature and stores in temp * @temp: holds the current temperature value after reading @@ -557,10 +560,9 @@ static struct platform_driver mid_thermal_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &mid_thermal_pm, }, .probe = mid_thermal_probe, - .suspend = mid_thermal_suspend, - .resume = mid_thermal_resume, .remove = __devexit_p(mid_thermal_remove), .id_table = therm_id_table, }; -- cgit v1.2.3 From 199ac0529eac0341b232b450e43fad71d1513e94 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 27 Jun 2012 23:20:29 +0200 Subject: sonypi: Use struct dev_pm_ops for power management Make the sonypi driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct acpi_device_ops. Signed-off-by: Rafael J. Wysocki Acked-by: Mattia Dongili --- drivers/char/sonypi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 45713f0e7d61..f87780502b41 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1459,7 +1459,7 @@ static int __devexit sonypi_remove(struct platform_device *dev) #ifdef CONFIG_PM static int old_camera_power; -static int sonypi_suspend(struct platform_device *dev, pm_message_t state) +static int sonypi_suspend(struct device *dev) { old_camera_power = sonypi_device.camera_power; sonypi_disable(); @@ -1467,14 +1467,16 @@ static int sonypi_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int sonypi_resume(struct platform_device *dev) +static int sonypi_resume(struct device *dev) { sonypi_enable(old_camera_power); return 0; } + +static SIMPLE_DEV_PM_OPS(sonypi_pm, sonypi_suspend, sonypi_resume); +#define SONYPI_PM (&sonypi_pm) #else -#define sonypi_suspend NULL -#define sonypi_resume NULL +#define SONYPI_PM NULL #endif static void sonypi_shutdown(struct platform_device *dev) @@ -1486,12 +1488,11 @@ static struct platform_driver sonypi_driver = { .driver = { .name = "sonypi", .owner = THIS_MODULE, + .pm = SONYPI_PM, }, .probe = sonypi_probe, .remove = __devexit_p(sonypi_remove), .shutdown = sonypi_shutdown, - .suspend = sonypi_suspend, - .resume = sonypi_resume, }; static struct platform_device *sonypi_platform_device; -- cgit v1.2.3 From 4b7760ba0dd3319f66886ab2335a0fbecdbc808a Mon Sep 17 00:00:00 2001 From: Sameer Nanda Date: Tue, 19 Jun 2012 22:23:33 +0200 Subject: PM / Sleep: add knob for printing device resume times Added a new knob called /sys/power/pm_print_times. Setting it to 1 enables printing of time taken by devices to suspend and resume. Setting it to 0 disables this printing (unless overridden by initcall_debug kernel command line option). Signed-off-by: Sameer Nanda Acked-by: Greg KH Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 4 ++-- drivers/base/power/power.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9cb845e49334..6e4db96958d1 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -166,7 +166,7 @@ static ktime_t initcall_debug_start(struct device *dev) { ktime_t calltime = ktime_set(0, 0); - if (initcall_debug) { + if (pm_print_times) { pr_info("calling %s+ @ %i, parent: %s\n", dev_name(dev), task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); @@ -181,7 +181,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, { ktime_t delta, rettime; - if (initcall_debug) { + if (pm_print_times) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index eeb4bff9505c..12c77b7ff8e8 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -85,3 +85,14 @@ static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} #endif + +#ifdef CONFIG_PM_DEBUG + +extern int pm_print_times_enabled; +#define pm_print_times (initcall_debug || pm_print_times_enabled) + +#else /* CONFIG_PM_DEBUG */ + +#define pm_print_times initcall_debug + +#endif /* CONFIG_PM_DEBUG */ -- cgit v1.2.3 From b2df1d4f8b95d9d1e3f064cef02fc5c5116b05cf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 21 Jun 2012 00:19:33 +0200 Subject: PM / Sleep: Separate printing suspend times from initcall_debug Change the behavior of the newly introduced /sys/power/pm_print_times attribute so that its initial value depends on initcall_debug, but setting it to 0 will cause device suspend/resume times not to be printed, even if initcall_debug has been set. This way, the people who use initcall_debug for reasons other than PM debugging will be able to switch the suspend/resume times printing off, if need be. Signed-off-by: Rafael J. Wysocki Reviewed-by: Srivatsa S. Bhat Acked-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 4 ++-- drivers/base/power/power.h | 11 ----------- 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6e4db96958d1..df5f41d2ec95 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -166,7 +166,7 @@ static ktime_t initcall_debug_start(struct device *dev) { ktime_t calltime = ktime_set(0, 0); - if (pm_print_times) { + if (pm_print_times_enabled) { pr_info("calling %s+ @ %i, parent: %s\n", dev_name(dev), task_pid_nr(current), dev->parent ? dev_name(dev->parent) : "none"); @@ -181,7 +181,7 @@ static void initcall_debug_report(struct device *dev, ktime_t calltime, { ktime_t delta, rettime; - if (pm_print_times) { + if (pm_print_times_enabled) { rettime = ktime_get(); delta = ktime_sub(rettime, calltime); pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 12c77b7ff8e8..eeb4bff9505c 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -85,14 +85,3 @@ static inline int pm_qos_sysfs_add(struct device *dev) { return 0; } static inline void pm_qos_sysfs_remove(struct device *dev) {} #endif - -#ifdef CONFIG_PM_DEBUG - -extern int pm_print_times_enabled; -#define pm_print_times (initcall_debug || pm_print_times_enabled) - -#else /* CONFIG_PM_DEBUG */ - -#define pm_print_times initcall_debug - -#endif /* CONFIG_PM_DEBUG */ -- cgit v1.2.3 From 0b589741b8b83a33e25fb32bb714ea1b972af5aa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 00:02:22 +0200 Subject: PM / Domains: Use subsystem runtime suspend/resume callbacks by default Currently, the default "save state" and "restore state" routines for generic PM domains, pm_genpd_default_save_state() and pm_genpd_default_restore_state(), respectively, only use runtime PM callbacks provided by device drivers, but in general those callbacks need not provide the entire necessary functionality. Namely, in general it may be necessary to execute subsystem (i.e. device type, device class or bus type) callbacks that will carry out all of the necessary operations. For this reason, modify pm_genpd_default_save_state() and pm_genpd_default_restore_state() to execute subsystem callbacks, if they are provided, and fall back to driver callbacks otherwise. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 83aa694a8efe..49fdd4002b9f 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1615,16 +1615,24 @@ EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); static int pm_genpd_default_save_state(struct device *dev) { int (*cb)(struct device *__dev); - struct device_driver *drv = dev->driver; cb = dev_gpd_data(dev)->ops.save_state; if (cb) return cb(dev); - if (drv && drv->pm && drv->pm->runtime_suspend) - return drv->pm->runtime_suspend(dev); + if (dev->type && dev->type->pm) + cb = dev->type->pm->runtime_suspend; + else if (dev->class && dev->class->pm) + cb = dev->class->pm->runtime_suspend; + else if (dev->bus && dev->bus->pm) + cb = dev->bus->pm->runtime_suspend; + else + cb = NULL; - return 0; + if (!cb && dev->driver && dev->driver->pm) + cb = dev->driver->pm->runtime_suspend; + + return cb ? cb(dev) : 0; } /** @@ -1634,16 +1642,24 @@ static int pm_genpd_default_save_state(struct device *dev) static int pm_genpd_default_restore_state(struct device *dev) { int (*cb)(struct device *__dev); - struct device_driver *drv = dev->driver; cb = dev_gpd_data(dev)->ops.restore_state; if (cb) return cb(dev); - if (drv && drv->pm && drv->pm->runtime_resume) - return drv->pm->runtime_resume(dev); + if (dev->type && dev->type->pm) + cb = dev->type->pm->runtime_resume; + else if (dev->class && dev->class->pm) + cb = dev->class->pm->runtime_resume; + else if (dev->bus && dev->bus->pm) + cb = dev->bus->pm->runtime_resume; + else + cb = NULL; - return 0; + if (!cb && dev->driver && dev->driver->pm) + cb = dev->driver->pm->runtime_resume; + + return cb ? cb(dev) : 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3 From 80de3d7f416f1accd03f2e519ead32d6fde4fcf4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 00:02:34 +0200 Subject: PM / Domains: Do not stop devices after restoring their states While resuming a device belonging to a PM domain, pm_genpd_runtime_resume() calls __pm_genpd_restore_device() to restore its state, if necessary. The latter starts the device, using genpd_start_dev(), restores its state, using genpd_restore_dev(), and then stops it, using genpd_stop_dev(). However, this last operation is not necessary, because the device is supposed to be operational after pm_genpd_runtime_resume() has returned and because of it pm_genpd_runtime_resume() has to call genpd_start_dev() once again for the "restored" device, which is inefficient. To make things more efficient, remove the call to genpd_stop_dev() from __pm_genpd_restore_device() and the direct call to genpd_start_dev() from pm_genpd_runtime_resume(). [Of course, genpd_start_dev() still has to be called by it for devices with the power.irq_safe flag set, because __pm_genpd_restore_device() is not executed for them.] This change has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 49fdd4002b9f..da1d52576ec9 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -339,19 +339,16 @@ static void __pm_genpd_restore_device(struct pm_domain_data *pdd, { struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); struct device *dev = pdd->dev; + bool need_restore = gpd_data->need_restore; - if (!gpd_data->need_restore) - return; - + gpd_data->need_restore = false; mutex_unlock(&genpd->lock); genpd_start_dev(genpd, dev); - genpd_restore_dev(genpd, dev); - genpd_stop_dev(genpd, dev); + if (need_restore) + genpd_restore_dev(genpd, dev); mutex_lock(&genpd->lock); - - gpd_data->need_restore = false; } /** @@ -595,7 +592,7 @@ static int pm_genpd_runtime_resume(struct device *dev) /* If power.irq_safe, the PM domain is never powered off. */ if (dev->power.irq_safe) - goto out; + return genpd_start_dev(genpd, dev); mutex_lock(&genpd->lock); ret = __pm_genpd_poweron(genpd); @@ -628,9 +625,6 @@ static int pm_genpd_runtime_resume(struct device *dev) wake_up_all(&genpd->status_wait_queue); mutex_unlock(&genpd->lock); - out: - genpd_start_dev(genpd, dev); - return 0; } -- cgit v1.2.3 From 1ab7ef158dfbaf6314b6660a3e3fb037cba2a1ca Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 13 Jun 2012 09:04:03 +0800 Subject: gpio/mxc: move irq_domain_add_legacy call into gpio driver Move irq_domain_add_legacy call from imx*-dt.c into gpio driver and have the gpio driver adopt irqdomain support for both DT and non-DT boot. With all imx platform code converted from static gpio irq number computation to use run-time gpio_to_irq call, we can now use irq_alloc_descs and irqdomain support to dynamically get irq_base and have the mapping between gpio and irq number available without using virtual_irq_start and MXC_GPIO_IRQ_START. Signed-off-by: Shawn Guo Cc: Grant Likely Acked-by: Sascha Hauer Acked-by: Dong Aisheng --- drivers/gpio/gpio-mxc.c | 56 ++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index c337143b18f8..e5db670d0002 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,8 +34,6 @@ #include #include -#define irq_to_gpio(irq) ((irq) - MXC_GPIO_IRQ_START) - enum mxc_gpio_hwtype { IMX1_GPIO, /* runs on i.mx1 */ IMX21_GPIO, /* runs on i.mx21 and i.mx27 */ @@ -61,7 +60,7 @@ struct mxc_gpio_port { void __iomem *base; int irq; int irq_high; - int virtual_irq_start; + struct irq_domain *domain; struct bgpio_chip bgc; u32 both_edges; }; @@ -144,14 +143,15 @@ static LIST_HEAD(mxc_gpio_ports); static int gpio_set_irq_type(struct irq_data *d, u32 type) { - u32 gpio = irq_to_gpio(d->irq); struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mxc_gpio_port *port = gc->private; u32 bit, val; + u32 gpio_idx = d->hwirq; + u32 gpio = port->bgc.gc.base + gpio_idx; int edge; void __iomem *reg = port->base; - port->both_edges &= ~(1 << (gpio & 31)); + port->both_edges &= ~(1 << gpio_idx); switch (type) { case IRQ_TYPE_EDGE_RISING: edge = GPIO_INT_RISE_EDGE; @@ -168,7 +168,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) edge = GPIO_INT_HIGH_LEV; pr_debug("mxc: set GPIO %d to high trigger\n", gpio); } - port->both_edges |= 1 << (gpio & 31); + port->both_edges |= 1 << gpio_idx; break; case IRQ_TYPE_LEVEL_LOW: edge = GPIO_INT_LOW_LEV; @@ -180,11 +180,11 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type) return -EINVAL; } - reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */ - bit = gpio & 0xf; + reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* ICR1 or ICR2 */ + bit = gpio_idx & 0xf; val = readl(reg) & ~(0x3 << (bit << 1)); writel(val | (edge << (bit << 1)), reg); - writel(1 << (gpio & 0x1f), port->base + GPIO_ISR); + writel(1 << gpio_idx, port->base + GPIO_ISR); return 0; } @@ -217,15 +217,13 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio) /* handle 32 interrupts in one status register */ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat) { - u32 gpio_irq_no_base = port->virtual_irq_start; - while (irq_stat != 0) { int irqoffset = fls(irq_stat) - 1; if (port->both_edges & (1 << irqoffset)) mxc_flip_edge(port, irqoffset); - generic_handle_irq(gpio_irq_no_base + irqoffset); + generic_handle_irq(irq_find_mapping(port->domain, irqoffset)); irq_stat &= ~(1 << irqoffset); } @@ -276,10 +274,9 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) */ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) { - u32 gpio = irq_to_gpio(d->irq); - u32 gpio_idx = gpio & 0x1F; struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mxc_gpio_port *port = gc->private; + u32 gpio_idx = d->hwirq; if (enable) { if (port->irq_high && (gpio_idx >= 16)) @@ -296,12 +293,12 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable) return 0; } -static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port) +static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base) { struct irq_chip_generic *gc; struct irq_chip_type *ct; - gc = irq_alloc_generic_chip("gpio-mxc", 1, port->virtual_irq_start, + gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base, port->base, handle_level_irq); gc->private = port; @@ -352,7 +349,7 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset) struct mxc_gpio_port *port = container_of(bgc, struct mxc_gpio_port, bgc); - return port->virtual_irq_start + offset; + return irq_find_mapping(port->domain, offset); } static int __devinit mxc_gpio_probe(struct platform_device *pdev) @@ -360,6 +357,7 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct mxc_gpio_port *port; struct resource *iores; + int irq_base; int err; mxc_gpio_get_hw(pdev); @@ -430,20 +428,30 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) if (err) goto out_bgpio_remove; - /* - * In dt case, we use gpio number range dynamically - * allocated by gpio core. - */ - port->virtual_irq_start = MXC_GPIO_IRQ_START + (np ? port->bgc.gc.base : - pdev->id * 32); + irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id()); + if (irq_base < 0) { + err = irq_base; + goto out_gpiochip_remove; + } + + port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, + &irq_domain_simple_ops, NULL); + if (!port->domain) { + err = -ENODEV; + goto out_irqdesc_free; + } /* gpio-mxc can be a generic irq chip */ - mxc_gpio_init_gc(port); + mxc_gpio_init_gc(port, irq_base); list_add_tail(&port->node, &mxc_gpio_ports); return 0; +out_irqdesc_free: + irq_free_descs(irq_base, 32); +out_gpiochip_remove: + WARN_ON(gpiochip_remove(&port->bgc.gc) < 0); out_bgpio_remove: bgpio_remove(&port->bgc); out_iounmap: -- cgit v1.2.3 From 88289c80d419897c03f7f43b35e3730d8fb6825b Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 13 Jun 2012 14:07:31 +0800 Subject: dma: ipu: remove the use of ipu_platform_data The struct ipu_platform_data is used by platform code to pass MXC_IPU_IRQ_START to ipu-core driver. We can save it by having ipu-core driver call irq_alloc_descs to get the irq_base. Signed-off-by: Shawn Guo Acked-by: Vinod Koul Acked-by: Sascha Hauer Acked-by: Dong Aisheng --- drivers/dma/ipu/ipu_idmac.c | 8 +++----- drivers/dma/ipu/ipu_irq.c | 14 +++++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index 5ec72044ea4c..c7573e50aa14 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -1663,7 +1663,6 @@ static void __exit ipu_idmac_exit(struct ipu *ipu) static int __init ipu_probe(struct platform_device *pdev) { - struct ipu_platform_data *pdata = pdev->dev.platform_data; struct resource *mem_ipu, *mem_ic; int ret; @@ -1671,7 +1670,7 @@ static int __init ipu_probe(struct platform_device *pdev) mem_ipu = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem_ic = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!pdata || !mem_ipu || !mem_ic) + if (!mem_ipu || !mem_ic) return -EINVAL; ipu_data.dev = &pdev->dev; @@ -1688,10 +1687,9 @@ static int __init ipu_probe(struct platform_device *pdev) goto err_noirq; ipu_data.irq_err = ret; - ipu_data.irq_base = pdata->irq_base; - dev_dbg(&pdev->dev, "fn irq %u, err irq %u, irq-base %u\n", - ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base); + dev_dbg(&pdev->dev, "fn irq %u, err irq %u\n", + ipu_data.irq_fn, ipu_data.irq_err); /* Remap IPU common registers */ ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu)); diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index a71f55e72be9..fa95bcc3de1f 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -354,10 +355,12 @@ static struct irq_chip ipu_irq_chip = { /* Install the IRQ handler */ int __init ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev) { - struct ipu_platform_data *pdata = dev->dev.platform_data; - unsigned int irq, irq_base, i; + unsigned int irq, i; + int irq_base = irq_alloc_descs(-1, 0, CONFIG_MX3_IPU_IRQS, + numa_node_id()); - irq_base = pdata->irq_base; + if (irq_base < 0) + return irq_base; for (i = 0; i < IPU_IRQ_NR_BANKS; i++) irq_bank[i].ipu = ipu; @@ -387,15 +390,16 @@ int __init ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev) irq_set_handler_data(ipu->irq_err, ipu); irq_set_chained_handler(ipu->irq_err, ipu_irq_err); + ipu->irq_base = irq_base; + return 0; } void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev) { - struct ipu_platform_data *pdata = dev->dev.platform_data; unsigned int irq, irq_base; - irq_base = pdata->irq_base; + irq_base = ipu->irq_base; irq_set_chained_handler(ipu->irq_fn, NULL); irq_set_handler_data(ipu->irq_fn, NULL); -- cgit v1.2.3 From 6387f8fcb6974499c3e179d3b0f369e62969300f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 14 Jun 2012 11:21:57 +0800 Subject: i2c: imx: remove unneeded mach/irqs.h inclusion Remove unneeded mach/irq.h inclusion from i2c-imx driver. Signed-off-by: Shawn Guo Cc: linux-i2c@vger.kernel.org Acked-by: Wolfram Sang Acked-by: Sascha Hauer Acked-by: Dong Aisheng --- drivers/i2c/busses/i2c-imx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 8d6b504d65c4..370031ac8200 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -53,7 +53,6 @@ #include #include -#include #include #include -- cgit v1.2.3 From 1ee8f65b5689504b98dacb6641fc8b2cc5618ad4 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 14 Jun 2012 10:58:54 +0800 Subject: tty: serial: imx: remove the use of MXC_INTERNAL_IRQS As the part of the effort to enable SPARE_IRQ for imx platform, the macro MXC_INTERNAL_IRQS will be removed. The imx serial driver has a references to it for a decision on flags of request_irq call based on rtsirq is beyond MXC_INTERNAL_IRQS. However the searching on imx platform code tells that rtsirq will never be beyond MXC_INTERNAL_IRQS. That said, the check, consequently the reference to MXC_INTERNAL_IRQS are not needed, so remove them. Signed-off-by: Shawn Guo Cc: linux-serial@vger.kernel.org Acked-by: Greg Kroah-Hartman Acked-by: Sascha Hauer Acked-by: Dong Aisheng --- drivers/tty/serial/imx.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 4ef747307ecb..d5c689d6217e 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -169,7 +169,6 @@ #define SERIAL_IMX_MAJOR 207 #define MINOR_START 16 #define DEV_NAME "ttymxc" -#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS /* * This determines how often we check the modem status signals @@ -741,10 +740,7 @@ static int imx_startup(struct uart_port *port) /* do not use RTS IRQ on IrDA */ if (!USE_IRDA(sport)) { - retval = request_irq(sport->rtsirq, imx_rtsint, - (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 : - IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING, + retval = request_irq(sport->rtsirq, imx_rtsint, 0, DRIVER_NAME, sport); if (retval) goto error_out3; -- cgit v1.2.3 From 8842a9e2869cae14bbb8184004a42fc3070587fb Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 14 Jun 2012 11:16:14 +0800 Subject: ARM: imx: enable SPARSE_IRQ for imx platform As all irqchips on imx have been changed to allocate their irq_descs, and all unneeded mach/irqs.h inclusions on imx have been cleaned up, now it's time to select SPARSE_IRQ for imx/mxc. The SPARSE_IRQ support forces irqs allocation starting from 16. All those static irq number definition for SoCs need to shift 16 to keep non-DT boot works. With all those static IRQ number and start definitions removed from mach/irqs.h, the header becomes just a container of a couple of mach-imx specific irq/fiq calls. Since mach/irqs.h is not included by asm/irq.h now, the users of mxc_set_irq_fiq needs to explicitly include mach/irqs.h themselves. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer --- drivers/media/video/mx1_camera.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 4296a8350298..d2e6f82ecfac 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -43,6 +43,7 @@ #include #include #include +#include #include /* -- cgit v1.2.3 From d172f319c1094ef22d2a00f43e8a7da4dd02c8f3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 29 Jun 2012 09:45:16 +0800 Subject: regulator: tps65217: Convert LDO1 to use regulator_list_voltage_table Convert tps65217_pmic_ldo1_ops to use regulator_list_voltage_table. We have n_voltages and volt_table settings in regulator_desc, so we don't need the table and table_len fields in struct tps_info. Thus remove them from struct tps_info. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 47 ++++++++++++++++------------------ 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index f5fa05b5bea4..3a2b839276fe 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -26,7 +26,7 @@ #include #include -#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em) \ +#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \ { \ .name = _name, \ .id = _id, \ @@ -38,20 +38,19 @@ .vsel_mask = _vm, \ .enable_reg = TPS65217_REG_ENABLE, \ .enable_mask = _em, \ + .volt_table = _t, \ } \ -#define TPS65217_INFO(_nm, _min, _max, _f1, _f2, _t, _n)\ +#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \ { \ .name = _nm, \ .min_uV = _min, \ .max_uV = _max, \ .vsel_to_uv = _f1, \ .uv_to_vsel = _f2, \ - .table = _t, \ - .table_len = _n, \ } -static const int LDO1_VSEL_table[] = { +static const unsigned int LDO1_VSEL_table[] = { 1000000, 1100000, 1200000, 1250000, 1300000, 1350000, 1400000, 1500000, 1600000, 1800000, 2500000, 2750000, @@ -128,19 +127,18 @@ static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) static struct tps_info tps65217_pmic_regs[] = { TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), - TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL, LDO1_VSEL_table, - 16), + tps65217_uv_to_vsel1), + TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL), TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1, - tps65217_uv_to_vsel1, NULL, 64), + tps65217_uv_to_vsel1), TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32), + tps65217_uv_to_vsel2), TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2, - tps65217_uv_to_vsel2, NULL, 32), + tps65217_uv_to_vsel2), }; static int tps65217_pmic_enable(struct regulator_dev *dev) @@ -230,12 +228,9 @@ static int tps65217_pmic_list_voltage(struct regulator_dev *dev, if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) return -EINVAL; - if (selector >= tps->info[rid]->table_len) + if (selector >= dev->desc->n_voltages) return -EINVAL; - if (tps->info[rid]->table) - return tps->info[rid]->table[selector]; - return tps->info[rid]->vsel_to_uv(selector); } @@ -257,31 +252,33 @@ static struct regulator_ops tps65217_pmic_ldo1_ops = { .disable = tps65217_pmic_disable, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, - .list_voltage = tps65217_pmic_list_voltage, + .list_voltage = regulator_list_voltage_table, }; static const struct regulator_desc regulators[] = { TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC1_EN), + TPS65217_ENABLE_DC1_EN, NULL), TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC2_EN), + TPS65217_ENABLE_DC2_EN, NULL), TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64, TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK, - TPS65217_ENABLE_DC3_EN), + TPS65217_ENABLE_DC3_EN, NULL), TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16, TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK, - TPS65217_ENABLE_LDO1_EN), + TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table), TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64, TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK, - TPS65217_ENABLE_LDO2_EN), + TPS65217_ENABLE_LDO2_EN, NULL), TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK, - TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN), + TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN, + NULL), TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32, TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK, - TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN), + TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN, + NULL), }; static int __devinit tps65217_regulator_probe(struct platform_device *pdev) -- cgit v1.2.3 From f141822b15635daa94610c5527806fa315f59f4d Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 29 Jun 2012 10:33:15 -0600 Subject: regulator: fixed: support deferred probe for DT GPIOs of_get_named_gpio() needs the driver hosting the GPIO that the DT property references to have been probed. Detect this specific failure, and defer the probe of the whole regulator until this API can complete. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 8bda3654ae51..b8c3a910502a 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -61,11 +61,11 @@ of_get_fixed_voltage_config(struct device *dev) config = devm_kzalloc(dev, sizeof(struct fixed_voltage_config), GFP_KERNEL); if (!config) - return NULL; + return ERR_PTR(-ENOMEM); config->init_data = of_get_regulator_init_data(dev, dev->of_node); if (!config->init_data) - return NULL; + return ERR_PTR(-EINVAL); init_data = config->init_data; init_data->constraints.apply_uV = 0; @@ -76,13 +76,26 @@ of_get_fixed_voltage_config(struct device *dev) } else { dev_err(dev, "Fixed regulator specified with variable voltages\n"); - return NULL; + return ERR_PTR(-EINVAL); } if (init_data->constraints.boot_on) config->enabled_at_boot = true; config->gpio = of_get_named_gpio(np, "gpio", 0); + /* + * of_get_named_gpio() currently returns ENODEV rather than + * EPROBE_DEFER. This code attempts to be compatible with both + * for now; the ENODEV check can be removed once the API is fixed. + * of_get_named_gpio() doesn't differentiate between a missing + * property (which would be fine here, since the GPIO is optional) + * and some other error. Patches have been posted for both issues. + * Once they are check in, we should replace this with: + * if (config->gpio < 0 && config->gpio != -ENOENT) + */ + if ((config->gpio == -ENODEV) || (config->gpio == -EPROBE_DEFER)) + return ERR_PTR(-EPROBE_DEFER); + delay = of_get_property(np, "startup-delay-us", NULL); if (delay) config->startup_delay = be32_to_cpu(*delay); @@ -172,10 +185,13 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) struct regulator_config cfg = { }; int ret; - if (pdev->dev.of_node) + if (pdev->dev.of_node) { config = of_get_fixed_voltage_config(&pdev->dev); - else + if (IS_ERR(config)) + return PTR_ERR(config); + } else { config = pdev->dev.platform_data; + } if (!config) return -ENOMEM; -- cgit v1.2.3 From 5130216265f6f924a4ba8214787241be96d93467 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Tue, 19 Jun 2012 15:00:05 +0800 Subject: PINCTRL: SiRF: add GPIO and GPIO irq support in CSR SiRFprimaII In SiRFprimaII, Each GPIO pin can be configured as input or output independently. If a GPIO is configured as input, it can also be enabled as an interrupt source (either edge or level triggered). These pins must be either MUXed as GPIO or other function pads. Signed-off-by: Yuping Luo Signed-off-by: Barry Song Acked-by: Linus Walleij --- drivers/pinctrl/pinctrl-sirf.c | 489 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 488 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index e9f8e7d11001..2aae8a8978e9 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -8,24 +8,61 @@ #include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include #include #include +#include +#include #define DRIVER_NAME "pinmux-sirf" #define SIRFSOC_NUM_PADS 622 -#define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84) #define SIRFSOC_RSC_PIN_MUX 0x4 +#define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84) +#define SIRFSOC_GPIO_CTRL(g, i) ((g)*0x100 + (i)*4) +#define SIRFSOC_GPIO_DSP_EN0 (0x80) +#define SIRFSOC_GPIO_PAD_EN(g) ((g)*0x100 + 0x84) +#define SIRFSOC_GPIO_INT_STATUS(g) ((g)*0x100 + 0x8C) + +#define SIRFSOC_GPIO_CTL_INTR_LOW_MASK 0x1 +#define SIRFSOC_GPIO_CTL_INTR_HIGH_MASK 0x2 +#define SIRFSOC_GPIO_CTL_INTR_TYPE_MASK 0x4 +#define SIRFSOC_GPIO_CTL_INTR_EN_MASK 0x8 +#define SIRFSOC_GPIO_CTL_INTR_STS_MASK 0x10 +#define SIRFSOC_GPIO_CTL_OUT_EN_MASK 0x20 +#define SIRFSOC_GPIO_CTL_DATAOUT_MASK 0x40 +#define SIRFSOC_GPIO_CTL_DATAIN_MASK 0x80 +#define SIRFSOC_GPIO_CTL_PULL_MASK 0x100 +#define SIRFSOC_GPIO_CTL_PULL_HIGH 0x200 +#define SIRFSOC_GPIO_CTL_DSP_INT 0x400 + +#define SIRFSOC_GPIO_NO_OF_BANKS 5 +#define SIRFSOC_GPIO_BANK_SIZE 32 +#define SIRFSOC_GPIO_NUM(bank, index) (((bank)*(32)) + (index)) + +struct sirfsoc_gpio_bank { + struct of_mm_gpio_chip chip; + struct irq_domain *domain; + int id; + int parent_irq; + spinlock_t lock; +}; + +static struct sirfsoc_gpio_bank sgpio_bank[SIRFSOC_GPIO_NO_OF_BANKS]; +static DEFINE_SPINLOCK(sgpio_lock); + /* * pad list for the pinmux subsystem * refer to CS-131858-DC-6A.xls @@ -1204,7 +1241,457 @@ static int __init sirfsoc_pinmux_init(void) } arch_initcall(sirfsoc_pinmux_init); +static inline int sirfsoc_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = container_of(to_of_mm_gpio_chip(chip), + struct sirfsoc_gpio_bank, chip); + + return irq_find_mapping(bank->domain, offset); +} + +static inline int sirfsoc_gpio_to_offset(unsigned int gpio) +{ + return gpio % SIRFSOC_GPIO_BANK_SIZE; +} + +static inline struct sirfsoc_gpio_bank *sirfsoc_gpio_to_bank(unsigned int gpio) +{ + return &sgpio_bank[gpio / SIRFSOC_GPIO_BANK_SIZE]; +} + +void sirfsoc_gpio_set_pull(unsigned gpio, unsigned mode) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_gpio_to_bank(gpio); + int idx = sirfsoc_gpio_to_offset(gpio); + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + + switch (mode) { + case SIRFSOC_GPIO_PULL_NONE: + val &= ~SIRFSOC_GPIO_CTL_PULL_MASK; + break; + case SIRFSOC_GPIO_PULL_UP: + val |= SIRFSOC_GPIO_CTL_PULL_MASK; + val |= SIRFSOC_GPIO_CTL_PULL_HIGH; + break; + case SIRFSOC_GPIO_PULL_DOWN: + val |= SIRFSOC_GPIO_CTL_PULL_MASK; + val &= ~SIRFSOC_GPIO_CTL_PULL_HIGH; + break; + default: + break; + } + + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} +EXPORT_SYMBOL(sirfsoc_gpio_set_pull); + +static inline struct sirfsoc_gpio_bank *sirfsoc_irqchip_to_bank(struct gpio_chip *chip) +{ + return container_of(to_of_mm_gpio_chip(chip), struct sirfsoc_gpio_bank, chip); +} + +static void sirfsoc_gpio_irq_ack(struct irq_data *d) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} + +static void __sirfsoc_gpio_irq_mask(struct sirfsoc_gpio_bank *bank, int idx) +{ + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + val &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; + val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} + +static void sirfsoc_gpio_irq_mask(struct irq_data *d) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + + __sirfsoc_gpio_irq_mask(bank, d->hwirq % SIRFSOC_GPIO_BANK_SIZE); +} + +static void sirfsoc_gpio_irq_unmask(struct irq_data *d) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + val |= SIRFSOC_GPIO_CTL_INTR_EN_MASK; + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); +} + +static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct sirfsoc_gpio_bank *bank = irq_data_get_irq_chip_data(d); + int idx = d->hwirq % SIRFSOC_GPIO_BANK_SIZE; + u32 val, offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + val = readl(bank->chip.regs + offset); + val &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + + switch (type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; + val &= ~SIRFSOC_GPIO_CTL_INTR_LOW_MASK; + break; + case IRQ_TYPE_EDGE_FALLING: + val &= ~SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; + val |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; + break; + case IRQ_TYPE_EDGE_BOTH: + val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_LOW_MASK | + SIRFSOC_GPIO_CTL_INTR_TYPE_MASK; + break; + case IRQ_TYPE_LEVEL_LOW: + val &= ~(SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + val |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK; + break; + case IRQ_TYPE_LEVEL_HIGH: + val |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; + val &= ~(SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + break; + } + + writel(val, bank->chip.regs + offset); + + spin_unlock_irqrestore(&sgpio_lock, flags); + + return 0; +} + +static struct irq_chip sirfsoc_irq_chip = { + .name = "sirf-gpio-irq", + .irq_ack = sirfsoc_gpio_irq_ack, + .irq_mask = sirfsoc_gpio_irq_mask, + .irq_unmask = sirfsoc_gpio_irq_unmask, + .irq_set_type = sirfsoc_gpio_irq_type, +}; + +static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc) +{ + struct sirfsoc_gpio_bank *bank = irq_get_handler_data(irq); + u32 status, ctrl; + int idx = 0; + unsigned int first_irq; + + status = readl(bank->chip.regs + SIRFSOC_GPIO_INT_STATUS(bank->id)); + if (!status) { + printk(KERN_WARNING + "%s: gpio id %d status %#x no interrupt is flaged\n", + __func__, bank->id, status); + handle_bad_irq(irq, desc); + return; + } + + first_irq = bank->domain->revmap_data.legacy.first_irq; + + while (status) { + ctrl = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, idx)); + + /* + * Here we must check whether the corresponding GPIO's interrupt + * has been enabled, otherwise just skip it + */ + if ((status & 0x1) && (ctrl & SIRFSOC_GPIO_CTL_INTR_EN_MASK)) { + pr_debug("%s: gpio id %d idx %d happens\n", + __func__, bank->id, idx); + generic_handle_irq(first_irq + idx); + } + + idx++; + status = status >> 1; + } +} + +static inline void sirfsoc_gpio_set_input(struct sirfsoc_gpio_bank *bank, unsigned ctrl_offset) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + val = readl(bank->chip.regs + ctrl_offset); + val &= ~SIRFSOC_GPIO_CTL_OUT_EN_MASK; + writel(val, bank->chip.regs + ctrl_offset); + + spin_unlock_irqrestore(&bank->lock, flags); +} + +static int sirfsoc_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + unsigned long flags; + + if (pinctrl_request_gpio(chip->base + offset)) + return -ENODEV; + + spin_lock_irqsave(&bank->lock, flags); + + /* + * default status: + * set direction as input and mask irq + */ + sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->id, offset)); + __sirfsoc_gpio_irq_mask(bank, offset); + + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static void sirfsoc_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + __sirfsoc_gpio_irq_mask(bank, offset); + sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->id, offset)); + + spin_unlock_irqrestore(&bank->lock, flags); + + pinctrl_free_gpio(chip->base + offset); +} + +static int sirfsoc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + int idx = sirfsoc_gpio_to_offset(gpio); + unsigned long flags; + unsigned offset; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&bank->lock, flags); + + sirfsoc_gpio_set_input(bank, offset); + + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static inline void sirfsoc_gpio_set_output(struct sirfsoc_gpio_bank *bank, unsigned offset, + int value) +{ + u32 out_ctrl; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + out_ctrl = readl(bank->chip.regs + offset); + if (value) + out_ctrl |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; + else + out_ctrl &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; + + out_ctrl &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; + out_ctrl |= SIRFSOC_GPIO_CTL_OUT_EN_MASK; + writel(out_ctrl, bank->chip.regs + offset); + + spin_unlock_irqrestore(&bank->lock, flags); +} + +static int sirfsoc_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + int idx = sirfsoc_gpio_to_offset(gpio); + u32 offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->id, idx); + + spin_lock_irqsave(&sgpio_lock, flags); + + sirfsoc_gpio_set_output(bank, offset, value); + + spin_unlock_irqrestore(&sgpio_lock, flags); + + return 0; +} + +static int sirfsoc_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + u32 val; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + val = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); + + spin_unlock_irqrestore(&bank->lock, flags); + + return !!(val & SIRFSOC_GPIO_CTL_DATAIN_MASK); +} + +static void sirfsoc_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct sirfsoc_gpio_bank *bank = sirfsoc_irqchip_to_bank(chip); + u32 ctrl; + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + ctrl = readl(bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); + if (value) + ctrl |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; + else + ctrl &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; + writel(ctrl, bank->chip.regs + SIRFSOC_GPIO_CTRL(bank->id, offset)); + + spin_unlock_irqrestore(&bank->lock, flags); +} + +int sirfsoc_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct sirfsoc_gpio_bank *bank = d->host_data; + + if (!bank) + return -EINVAL; + + irq_set_chip(irq, &sirfsoc_irq_chip); + irq_set_handler(irq, handle_level_irq); + irq_set_chip_data(irq, bank); + set_irq_flags(irq, IRQF_VALID); + + return 0; +} + +const struct irq_domain_ops sirfsoc_gpio_irq_simple_ops = { + .map = sirfsoc_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int __devinit sirfsoc_gpio_probe(struct device_node *np) +{ + int i, err = 0; + struct sirfsoc_gpio_bank *bank; + void *regs; + struct platform_device *pdev; + + pdev = of_find_device_by_node(np); + if (!pdev) + return -ENODEV; + + regs = of_iomap(np, 0); + if (!regs) + return -ENOMEM; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + bank = &sgpio_bank[i]; + spin_lock_init(&bank->lock); + bank->chip.gc.request = sirfsoc_gpio_request; + bank->chip.gc.free = sirfsoc_gpio_free; + bank->chip.gc.direction_input = sirfsoc_gpio_direction_input; + bank->chip.gc.get = sirfsoc_gpio_get_value; + bank->chip.gc.direction_output = sirfsoc_gpio_direction_output; + bank->chip.gc.set = sirfsoc_gpio_set_value; + bank->chip.gc.to_irq = sirfsoc_gpio_to_irq; + bank->chip.gc.base = i * SIRFSOC_GPIO_BANK_SIZE; + bank->chip.gc.ngpio = SIRFSOC_GPIO_BANK_SIZE; + bank->chip.gc.label = kstrdup(np->full_name, GFP_KERNEL); + bank->chip.gc.of_node = np; + bank->chip.regs = regs; + bank->id = i; + bank->parent_irq = platform_get_irq(pdev, i); + if (bank->parent_irq < 0) { + err = bank->parent_irq; + goto out; + } + + err = gpiochip_add(&bank->chip.gc); + if (err) { + pr_err("%s: error in probe function with status %d\n", + np->full_name, err); + goto out; + } + + bank->domain = irq_domain_add_legacy(np, SIRFSOC_GPIO_BANK_SIZE, + SIRFSOC_GPIO_IRQ_START + i * SIRFSOC_GPIO_BANK_SIZE, 0, + &sirfsoc_gpio_irq_simple_ops, bank); + + if (!bank->domain) { + pr_err("%s: Failed to create irqdomain\n", np->full_name); + err = -ENOSYS; + goto out; + } + + irq_set_chained_handler(bank->parent_irq, sirfsoc_gpio_handle_irq); + irq_set_handler_data(bank->parent_irq, bank); + } + +out: + iounmap(regs); + return err; +} + +static int __init sirfsoc_gpio_init(void) +{ + + struct device_node *np; + + np = of_find_matching_node(NULL, pinmux_ids); + + if (!np) + return -ENODEV; + + return sirfsoc_gpio_probe(np); +} +subsys_initcall(sirfsoc_gpio_init); + MODULE_AUTHOR("Rongjun Ying , " + "Yuping Luo , " "Barry Song "); MODULE_DESCRIPTION("SIRFSOC pin control driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ea9f07197cae739fb22fd208a68425fea26a763b Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 6 Feb 2012 22:24:57 +0100 Subject: can: cc770: fix sparse warning for cc770_interrupt Make cc770_interrupt static to fix the following sparse warning: drivers/net/can/cc770/cc770.c:699:13: warning: symbol 'cc770_interrupt' was not declared. Should it be static? Signed-off-by: Marc Kleine-Budde --- drivers/net/can/cc770/cc770.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c index d42a6a7396f2..a138db11cbf0 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c @@ -695,7 +695,7 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o) netif_wake_queue(dev); } -irqreturn_t cc770_interrupt(int irq, void *dev_id) +static irqreturn_t cc770_interrupt(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct cc770_priv *priv = netdev_priv(dev); -- cgit v1.2.3 From 77fc95a356cd07d247bbe42a66596ba7b8486920 Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Mon, 6 Feb 2012 22:21:13 +0100 Subject: can: dev: fix sparse warning for can_restart Make can_restart static to fix the following sparse warning: drivers/net/can/dev.c:371:6: warning: symbol 'can_restart' was not declared. Should it be static? Signed-off-by: Marc Kleine-Budde --- drivers/net/can/dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 239e4dd92ca1..963e2ccd10db 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -401,7 +401,7 @@ EXPORT_SYMBOL_GPL(can_free_echo_skb); /* * CAN device restart for bus-off recovery */ -void can_restart(unsigned long data) +static void can_restart(unsigned long data) { struct net_device *dev = (struct net_device *)data; struct can_priv *priv = netdev_priv(dev); -- cgit v1.2.3 From afc016d8360ceb19a1f37bf6579d5850d47d582d Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Thu, 28 Jun 2012 16:21:34 +0800 Subject: can: flexcan: use of_property_read_u32 to get DT entry value of_property_read_u32() can auto handle endian problems, use this function can make code clean and simple. No need to check return value here since the following got value check will handle this. Cc: linux-can@vger.kernel.org Cc: Marc Kleine-Budde Cc: Wolfgang Grandegger Cc: Shawn Guo Signed-off-by: Hui Wang Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 0f88cd3bb928..b429b3f3fa7f 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -938,14 +938,9 @@ static int __devinit flexcan_probe(struct platform_device *pdev) if (IS_ERR(pinctrl)) return PTR_ERR(pinctrl); - if (pdev->dev.of_node) { - const __be32 *clock_freq_p; - - clock_freq_p = of_get_property(pdev->dev.of_node, - "clock-frequency", NULL); - if (clock_freq_p) - clock_freq = be32_to_cpup(clock_freq_p); - } + if (pdev->dev.of_node) + of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &clock_freq); if (!clock_freq) { clk = clk_get(&pdev->dev, NULL); -- cgit v1.2.3 From 8f53dc724a83a0082184fa27df80c25c7df47340 Mon Sep 17 00:00:00 2001 From: Hiroshi DOYU Date: Wed, 27 Jun 2012 12:54:01 +0300 Subject: iommu/tegra: smmu: Fix unsleepable memory allocation allo_pdir() is called in smmu_iommu_domain_init() with spin_lock held. memory allocations in it have to be atomic/unsleepable. Signed-off-by: Hiroshi DOYU Reported-by: Chris Wright Acked-by: Chris Wright Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index ecd679043d77..3f3d09d560ea 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -550,13 +550,13 @@ static int alloc_pdir(struct smmu_as *as) return 0; as->pte_count = devm_kzalloc(smmu->dev, - sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_KERNEL); + sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_ATOMIC); if (!as->pte_count) { dev_err(smmu->dev, "failed to allocate smmu_device PTE cunters\n"); return -ENOMEM; } - as->pdir_page = alloc_page(GFP_KERNEL | __GFP_DMA); + as->pdir_page = alloc_page(GFP_ATOMIC | __GFP_DMA); if (!as->pdir_page) { dev_err(smmu->dev, "failed to allocate smmu_device page directory\n"); -- cgit v1.2.3 From 3775d4818d72081e2afa2aed2442a2b9ecfc5eab Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 Jun 2012 12:09:18 +0300 Subject: iommu/amd: fix type bug in flush code write_file_bool() modifies 32 bits of data, so "amd_iommu_unmap_flush" needs to be 32 bits as well or we'll corrupt memory. Fortunately it looks like the data is aligned with a gap after the declaration so this is harmless in production. Signed-off-by: Dan Carpenter Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 2 +- drivers/iommu/amd_iommu_init.c | 2 +- drivers/iommu/amd_iommu_types.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index dfe7d37c82c5..625626391f2d 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -404,7 +404,7 @@ static void amd_iommu_stats_init(void) return; de_fflush = debugfs_create_bool("fullflush", 0444, stats_dir, - (u32 *)&amd_iommu_unmap_flush); + &amd_iommu_unmap_flush); amd_iommu_stats_add(&compl_wait); amd_iommu_stats_add(&cnt_map_single); diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index c04ddca7f12f..a33612f3206f 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -129,7 +129,7 @@ u16 amd_iommu_last_bdf; /* largest PCI device id we have to handle */ LIST_HEAD(amd_iommu_unity_map); /* a list of required unity mappings we find in ACPI */ -bool amd_iommu_unmap_flush; /* if true, flush on every unmap */ +u32 amd_iommu_unmap_flush; /* if true, flush on every unmap */ LIST_HEAD(amd_iommu_list); /* list of all AMD IOMMUs in the system */ diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index 24355559a2ad..c1b1d489817e 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -652,7 +652,7 @@ extern unsigned long *amd_iommu_pd_alloc_bitmap; * If true, the addresses will be flushed on unmap time, not when * they are reused */ -extern bool amd_iommu_unmap_flush; +extern u32 amd_iommu_unmap_flush; /* Smallest number of PASIDs supported by any IOMMU in the system */ extern u32 amd_iommu_max_pasids; -- cgit v1.2.3 From e2fb50521c3811eddd60d911bc6d4d191f5d6e61 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Wed, 23 May 2012 15:08:10 -0500 Subject: omap2+: add drm device Register OMAP DRM/KMS platform device. DMM is split into a separate device using hwmod. Signed-off-by: Andy Gross Signed-off-by: Rob Clark Signed-off-by: Tony Lindgren --- drivers/staging/omapdrm/omap_drv.h | 2 +- drivers/staging/omapdrm/omap_priv.h | 55 ------------------------------------- 2 files changed, 1 insertion(+), 56 deletions(-) delete mode 100644 drivers/staging/omapdrm/omap_priv.h (limited to 'drivers') diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h index f238d574da0c..2092a9167d29 100644 --- a/drivers/staging/omapdrm/omap_drv.h +++ b/drivers/staging/omapdrm/omap_drv.h @@ -25,8 +25,8 @@ #include #include #include +#include #include "omap_drm.h" -#include "omap_priv.h" #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ diff --git a/drivers/staging/omapdrm/omap_priv.h b/drivers/staging/omapdrm/omap_priv.h deleted file mode 100644 index ef6441447147..000000000000 --- a/drivers/staging/omapdrm/omap_priv.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * include/drm/omap_priv.h - * - * Copyright (C) 2011 Texas Instruments - * Author: Rob Clark - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see . - */ - -#ifndef __OMAP_PRIV_H__ -#define __OMAP_PRIV_H__ - -/* Non-userspace facing APIs - */ - -/* optional platform data to configure the default configuration of which - * pipes/overlays/CRTCs are used.. if this is not provided, then instead the - * first CONFIG_DRM_OMAP_NUM_CRTCS are used, and they are each connected to - * one manager, with priority given to managers that are connected to - * detected devices. Remaining overlays are used as video planes. This - * should be a good default behavior for most cases, but yet there still - * might be times when you wish to do something different. - */ -struct omap_kms_platform_data { - /* overlays to use as CRTCs: */ - int ovl_cnt; - const int *ovl_ids; - - /* overlays to use as video planes: */ - int pln_cnt; - const int *pln_ids; - - int mgr_cnt; - const int *mgr_ids; - - int dev_cnt; - const char **dev_names; -}; - -struct omap_drm_platform_data { - struct omap_kms_platform_data *kms_pdata; - struct omap_dmm_platform_data *dmm_pdata; -}; - -#endif /* __OMAP_DRM_H__ */ -- cgit v1.2.3 From 3a0c52a6d82cc41da965284412608c74aece34e4 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 2 Jul 2012 09:32:32 +0300 Subject: cfg80211: add 802.11ad (60gHz band) support Add enumerations for both cfg80211 and nl80211. This expands wiphy.bands etc. arrays. Extend channel <-> frequency translation to cover 60g band and modify the rate check logic since there are no legacy mandatory rates (only MCS is used.) Signed-off-by: Vladimir Kondratiev Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlegacy/3945-rs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index 4b10157d8686..d4fd29ad90dc 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -946,7 +946,7 @@ il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) case IEEE80211_BAND_5GHZ: rs_sta->expected_tpt = il3945_expected_tpt_a; break; - case IEEE80211_NUM_BANDS: + default: BUG(); break; } -- cgit v1.2.3 From cac87fd34ebd79a954321f78acf38abcc69e1308 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 20 Jun 2012 22:30:15 +0800 Subject: regulator: tps6524x: Convert to regulator_list_voltage_table() This patch adds fixed_5000000_voltage table for fixed voltage, so we can convert regulator_ops to regulator_list_voltage_table. We can differentiate fixed voltage by checking rdev->desc->n_voltages == 1, thus remove the FIXED_VOLTAGE flag usage. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6524x-regulator.c | 52 +++++++++++++--------------------- 1 file changed, 19 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 1b299aacf22f..48e37bd95a27 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -111,7 +111,6 @@ #define N_REGULATORS (N_DCDC + N_LDO + N_SWITCH) #define FIXED_ILIMSEL BIT(0) -#define FIXED_VOLTAGE BIT(1) #define CMD_READ(reg) ((reg) << 6) #define CMD_WRITE(reg) (BIT(5) | (reg) << 6) @@ -129,8 +128,7 @@ struct field { struct supply_info { const char *name; int n_voltages; - const int *voltages; - int fixed_voltage; + const unsigned int *voltages; int n_ilimsels; const int *ilimsels; int fixed_ilimsel; @@ -307,7 +305,7 @@ static int write_field(struct tps6524x *hw, const struct field *field, val << field->shift); } -static const int dcdc1_voltages[] = { +static const unsigned int dcdc1_voltages[] = { 800000, 825000, 850000, 875000, 900000, 925000, 950000, 975000, 1000000, 1025000, 1050000, 1075000, @@ -318,7 +316,7 @@ static const int dcdc1_voltages[] = { 1500000, 1525000, 1550000, 1575000, }; -static const int dcdc2_voltages[] = { +static const unsigned int dcdc2_voltages[] = { 1400000, 1450000, 1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, 1900000, 1950000, @@ -329,7 +327,7 @@ static const int dcdc2_voltages[] = { 2800000, 2850000, 2900000, 2950000, }; -static const int dcdc3_voltages[] = { +static const unsigned int dcdc3_voltages[] = { 2400000, 2450000, 2500000, 2550000, 2600000, 2650000, 2700000, 2750000, 2800000, 2850000, 2900000, 2950000, 3000000, 3050000, 3100000, @@ -337,20 +335,24 @@ static const int dcdc3_voltages[] = { 3400000, 3450000, 3500000, 3550000, 3600000, }; -static const int ldo1_voltages[] = { +static const unsigned int ldo1_voltages[] = { 4300000, 4350000, 4400000, 4450000, 4500000, 4550000, 4600000, 4650000, 4700000, 4750000, 4800000, 4850000, 4900000, 4950000, 5000000, 5050000, }; -static const int ldo2_voltages[] = { +static const unsigned int ldo2_voltages[] = { 1100000, 1150000, 1200000, 1250000, 1300000, 1700000, 1750000, 1800000, 1850000, 1900000, 3150000, 3200000, 3250000, 3300000, 3350000, 3400000, }; +static const unsigned int fixed_5000000_voltage[] = { + 5000000 +}; + static const int ldo_ilimsel[] = { 400000, 1500000 }; @@ -424,8 +426,8 @@ static const struct supply_info supply_info[N_REGULATORS] = { }, { .name = "USB", - .flags = FIXED_VOLTAGE, - .fixed_voltage = 5000000, + .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), + .voltages = fixed_5000000_voltage, .n_ilimsels = ARRAY_SIZE(usb_ilimsel), .ilimsels = usb_ilimsel, .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, @@ -435,29 +437,15 @@ static const struct supply_info supply_info[N_REGULATORS] = { }, { .name = "LCD", - .flags = FIXED_VOLTAGE | FIXED_ILIMSEL, - .fixed_voltage = 5000000, + .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), + .voltages = fixed_5000000_voltage, + .flags = FIXED_ILIMSEL, .fixed_ilimsel = 400000, .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, BLOCK_LCD_SHIFT), }, }; -static int list_voltage(struct regulator_dev *rdev, unsigned selector) -{ - const struct supply_info *info; - struct tps6524x *hw; - - hw = rdev_get_drvdata(rdev); - info = &supply_info[rdev_get_id(rdev)]; - - if (info->flags & FIXED_VOLTAGE) - return selector ? -EINVAL : info->fixed_voltage; - - return ((selector < info->n_voltages) ? - info->voltages[selector] : -EINVAL); -} - static int set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { const struct supply_info *info; @@ -466,7 +454,7 @@ static int set_voltage_sel(struct regulator_dev *rdev, unsigned selector) hw = rdev_get_drvdata(rdev); info = &supply_info[rdev_get_id(rdev)]; - if (info->flags & FIXED_VOLTAGE) + if (rdev->desc->n_voltages == 1) return -EINVAL; return write_field(hw, &info->voltage, selector); @@ -481,7 +469,7 @@ static int get_voltage_sel(struct regulator_dev *rdev) hw = rdev_get_drvdata(rdev); info = &supply_info[rdev_get_id(rdev)]; - if (info->flags & FIXED_VOLTAGE) + if (rdev->desc->n_voltages == 1) return 0; ret = read_field(hw, &info->voltage); @@ -577,7 +565,7 @@ static struct regulator_ops regulator_ops = { .disable = disable_supply, .get_voltage_sel = get_voltage_sel, .set_voltage_sel = set_voltage_sel, - .list_voltage = list_voltage, + .list_voltage = regulator_list_voltage_table, .set_current_limit = set_current_limit, .get_current_limit = get_current_limit, }; @@ -629,13 +617,11 @@ static int __devinit pmic_probe(struct spi_device *spi) hw->desc[i].name = info->name; hw->desc[i].id = i; hw->desc[i].n_voltages = info->n_voltages; + hw->desc[i].volt_table = info->voltages; hw->desc[i].ops = ®ulator_ops; hw->desc[i].type = REGULATOR_VOLTAGE; hw->desc[i].owner = THIS_MODULE; - if (info->flags & FIXED_VOLTAGE) - hw->desc[i].n_voltages = 1; - config.dev = dev; config.init_data = init_data; config.driver_data = hw; -- cgit v1.2.3 From 1e12dfc9681fed46fbc9fffc6aba9613908c1213 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 20 Jun 2012 22:32:08 +0800 Subject: regulator: tps6524x: Convert fixed ilimsel to table based This patch refactors the code to get rid of the fixed_ilimsel and FIXED_ILIMSEL flag usage, and convert all the fixed ilimsel to table based (with one entry in the table). We can differentiate fixed ilimsel by checking info->n_ilimsels == 1, thus FIXED_ILIMSEL flag can be removed. This change makes the logic of the code simpler as all the ilimsels are table based now. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6524x-regulator.c | 44 ++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 48e37bd95a27..947ece933d90 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -110,8 +110,6 @@ #define N_SWITCH 2 #define N_REGULATORS (N_DCDC + N_LDO + N_SWITCH) -#define FIXED_ILIMSEL BIT(0) - #define CMD_READ(reg) ((reg) << 6) #define CMD_WRITE(reg) (BIT(5) | (reg) << 6) #define STAT_CLK BIT(3) @@ -130,9 +128,7 @@ struct supply_info { int n_voltages; const unsigned int *voltages; int n_ilimsels; - const int *ilimsels; - int fixed_ilimsel; - int flags; + const unsigned int *ilimsels; struct field enable, voltage, ilimsel; }; @@ -353,24 +349,36 @@ static const unsigned int fixed_5000000_voltage[] = { 5000000 }; -static const int ldo_ilimsel[] = { +static const unsigned int ldo_ilimsel[] = { 400000, 1500000 }; -static const int usb_ilimsel[] = { +static const unsigned int usb_ilimsel[] = { 200000, 400000, 800000, 1000000 }; +static const unsigned int fixed_2400000_ilimsel[] = { + 2400000 +}; + +static const unsigned int fixed_1200000_ilimsel[] = { + 1200000 +}; + +static const unsigned int fixed_400000_ilimsel[] = { + 400000 +}; + #define __MK_FIELD(_reg, _mask, _shift) \ { .reg = (_reg), .mask = (_mask), .shift = (_shift), } static const struct supply_info supply_info[N_REGULATORS] = { { .name = "DCDC1", - .flags = FIXED_ILIMSEL, .n_voltages = ARRAY_SIZE(dcdc1_voltages), .voltages = dcdc1_voltages, - .fixed_ilimsel = 2400000, + .n_ilimsels = ARRAY_SIZE(fixed_2400000_ilimsel), + .ilimsels = fixed_2400000_ilimsel, .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, DCDCDCDC1_EN_SHIFT), .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, @@ -378,10 +386,10 @@ static const struct supply_info supply_info[N_REGULATORS] = { }, { .name = "DCDC2", - .flags = FIXED_ILIMSEL, .n_voltages = ARRAY_SIZE(dcdc2_voltages), .voltages = dcdc2_voltages, - .fixed_ilimsel = 1200000, + .n_ilimsels = ARRAY_SIZE(fixed_1200000_ilimsel), + .ilimsels = fixed_1200000_ilimsel, .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, DCDCDCDC2_EN_SHIFT), .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, @@ -389,10 +397,10 @@ static const struct supply_info supply_info[N_REGULATORS] = { }, { .name = "DCDC3", - .flags = FIXED_ILIMSEL, .n_voltages = ARRAY_SIZE(dcdc3_voltages), .voltages = dcdc3_voltages, - .fixed_ilimsel = 1200000, + .n_ilimsels = ARRAY_SIZE(fixed_1200000_ilimsel), + .ilimsels = fixed_1200000_ilimsel, .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, DCDCDCDC3_EN_SHIFT), .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, @@ -439,8 +447,8 @@ static const struct supply_info supply_info[N_REGULATORS] = { .name = "LCD", .n_voltages = ARRAY_SIZE(fixed_5000000_voltage), .voltages = fixed_5000000_voltage, - .flags = FIXED_ILIMSEL, - .fixed_ilimsel = 400000, + .n_ilimsels = ARRAY_SIZE(fixed_400000_ilimsel), + .ilimsels = fixed_400000_ilimsel, .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, BLOCK_LCD_SHIFT), }, @@ -491,7 +499,7 @@ static int set_current_limit(struct regulator_dev *rdev, int min_uA, hw = rdev_get_drvdata(rdev); info = &supply_info[rdev_get_id(rdev)]; - if (info->flags & FIXED_ILIMSEL) + if (info->n_ilimsels == 1) return -EINVAL; for (i = 0; i < info->n_ilimsels; i++) @@ -514,8 +522,8 @@ static int get_current_limit(struct regulator_dev *rdev) hw = rdev_get_drvdata(rdev); info = &supply_info[rdev_get_id(rdev)]; - if (info->flags & FIXED_ILIMSEL) - return info->fixed_ilimsel; + if (info->n_ilimsels == 1) + return info->ilimsels[0]; ret = read_field(hw, &info->ilimsel); if (ret < 0) -- cgit v1.2.3 From 1a5d39d0644fa01e659ee42b5ec47abbd7db3afb Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Mon, 2 Jul 2012 18:54:17 +0100 Subject: regulator: tps65217: invalid if check This permits the setting of bogus values because the invalidity check is itself invalid. Reported-by: dcb314@hotmail.com Signed-off-by: Alan Cox Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 3a2b839276fe..0a3df5b7c904 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -78,7 +78,7 @@ static int tps65217_vsel_to_uv1(unsigned int vsel) static int tps65217_uv_to_vsel1(int uV, unsigned int *vsel) { - if ((uV < 0) && (uV > 3300000)) + if (uV < 0 || uV > 3300000) return -EINVAL; if (uV <= 1500000) @@ -112,7 +112,7 @@ static int tps65217_vsel_to_uv2(unsigned int vsel) static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel) { - if ((uV < 0) && (uV > 3300000)) + if (uV < 0 || uV > 3300000) return -EINVAL; if (uV <= 1900000) -- cgit v1.2.3 From 0072f0a82ce78ceb634f00a57d80c9b421b061d4 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 20 Jun 2012 22:55:50 +0800 Subject: regulator: tps62360: Convert to regulator_set_voltage_time_sel() Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps62360-regulator.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index bcea4e1eea1e..103bb62aaef8 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -74,7 +74,6 @@ struct tps62360_chip { int lru_index[4]; int curr_vset_vsel[4]; int curr_vset_id; - int change_uv_per_us; }; /* @@ -173,16 +172,6 @@ static int tps62360_dcdc_set_voltage_sel(struct regulator_dev *dev, return 0; } -static int tps62360_set_voltage_time_sel(struct regulator_dev *rdev, - unsigned int old_selector, unsigned int new_selector) -{ - struct tps62360_chip *tps = rdev_get_drvdata(rdev); - - return DIV_ROUND_UP(abs(new_selector - old_selector) * - rdev->desc->uV_step, - tps->change_uv_per_us); -} - static int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode) { struct tps62360_chip *tps = rdev_get_drvdata(rdev); @@ -249,7 +238,7 @@ static struct regulator_ops tps62360_dcdc_ops = { .set_voltage_sel = tps62360_dcdc_set_voltage_sel, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, - .set_voltage_time_sel = tps62360_set_voltage_time_sel, + .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_mode = tps62360_set_mode, .get_mode = tps62360_get_mode, }; @@ -292,7 +281,7 @@ static int __devinit tps62360_init_dcdc(struct tps62360_chip *tps, ramp_ctrl = (ramp_ctrl >> 4) & 0x7; /* ramp mV/us = 32/(2^ramp_ctrl) */ - tps->change_uv_per_us = DIV_ROUND_UP(32000, BIT(ramp_ctrl)); + tps->desc.ramp_delay = DIV_ROUND_UP(32000, BIT(ramp_ctrl)); return ret; } -- cgit v1.2.3 From 8bea2bd37df08aaa599aa361a9f8b836ba98e554 Mon Sep 17 00:00:00 2001 From: Stanislaw Ledwon Date: Mon, 18 Jun 2012 15:20:00 +0200 Subject: usb: Add support for root hub port status CAS The host controller port status register supports CAS (Cold Attach Status) bit. This bit could be set when USB3.0 device is connected when system is in Sx state. When the system wakes to S0 this port status with CAS bit is reported and this port can't be used by any device. When CAS bit is set the port should be reset by warm reset. This was not supported by xhci driver. The issue was found when pendrive was connected to suspended platform. The link state of "Compliance Mode" was reported together with CAS bit. This link state was also not supported by xhci and core/hub.c. The CAS bit is defined only for xhci root hub port and it is not supported on regular hubs. The link status is used to force warm reset on port. Make the USB core issue a warm reset when port is in ether the 'inactive' or 'compliance mode'. Change the xHCI driver to report 'compliance mode' when the CAS is set. This force warm reset on the root hub port. This patch should be backported to stable kernels as old as 3.2, that contain the commit 10d674a82e553cb8a1f41027bb3c3e309b3f6804 "USB: When hot reset for USB3 fails, try warm reset." Signed-off-by: Stanislaw Ledwon Signed-off-by: Sarah Sharp Acked-by: Andiry Xu Cc: stable@vger.kernel.org --- drivers/usb/core/hub.c | 18 ++++++++++-------- drivers/usb/host/xhci-hub.c | 44 ++++++++++++++++++++++++++++++++++++++------ drivers/usb/host/xhci.h | 6 +++++- 3 files changed, 53 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 25a7422ee657..8fb484984c86 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2324,12 +2324,16 @@ static unsigned hub_is_wusb(struct usb_hub *hub) static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm); -/* Is a USB 3.0 port in the Inactive state? */ -static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) +/* Is a USB 3.0 port in the Inactive or Complinance Mode state? + * Port worm reset is required to recover + */ +static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) { return hub_is_superspeed(hub->hdev) && - (portstatus & USB_PORT_STAT_LINK_STATE) == - USB_SS_PORT_LS_SS_INACTIVE; + (((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_INACTIVE) || + ((portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_COMP_MOD)) ; } static int hub_port_wait_reset(struct usb_hub *hub, int port1, @@ -2365,7 +2369,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, * * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 */ - if (hub_port_inactive(hub, portstatus)) { + if (hub_port_warm_reset_required(hub, portstatus)) { int ret; if ((portchange & USB_PORT_STAT_C_CONNECTION)) @@ -4408,9 +4412,7 @@ static void hub_events(void) /* Warm reset a USB3 protocol port if it's in * SS.Inactive state. */ - if (hub_is_superspeed(hub->hdev) && - (portstatus & USB_PORT_STAT_LINK_STATE) - == USB_SS_PORT_LS_SS_INACTIVE) { + if (hub_port_warm_reset_required(hub, portstatus)) { dev_dbg(hub_dev, "warm reset port %d\n", i); hub_port_reset(hub, i, NULL, HUB_BH_RESET_TIME, true); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 2732ef660c5c..7b01094d7993 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -462,6 +462,42 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, } } +/* Updates Link Status for super Speed port */ +static void xhci_hub_report_link_state(u32 *status, u32 status_reg) +{ + u32 pls = status_reg & PORT_PLS_MASK; + + /* resume state is a xHCI internal state. + * Do not report it to usb core. + */ + if (pls == XDEV_RESUME) + return; + + /* When the CAS bit is set then warm reset + * should be performed on port + */ + if (status_reg & PORT_CAS) { + /* The CAS bit can be set while the port is + * in any link state. + * Only roothubs have CAS bit, so we + * pretend to be in compliance mode + * unless we're already in compliance + * or the inactive state. + */ + if (pls != USB_SS_PORT_LS_COMP_MOD && + pls != USB_SS_PORT_LS_SS_INACTIVE) { + pls = USB_SS_PORT_LS_COMP_MOD; + } + /* Return also connection bit - + * hub state machine resets port + * when this bit is set. + */ + pls |= USB_PORT_STAT_CONNECTION; + } + /* update status field */ + *status |= pls; +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { @@ -606,13 +642,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, else status |= USB_PORT_STAT_POWER; } - /* Port Link State */ + /* Update Port Link State for super speed ports*/ if (hcd->speed == HCD_USB3) { - /* resume state is a xHCI internal state. - * Do not report it to usb core. - */ - if ((temp & PORT_PLS_MASK) != XDEV_RESUME) - status |= (temp & PORT_PLS_MASK); + xhci_hub_report_link_state(&status, temp); } if (bus_state->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index de3d6e3e57be..55c0785810c9 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -341,7 +341,11 @@ struct xhci_op_regs { #define PORT_PLC (1 << 22) /* port configure error change - port failed to configure its link partner */ #define PORT_CEC (1 << 23) -/* bit 24 reserved */ +/* Cold Attach Status - xHC can set this bit to report device attached during + * Sx state. Warm port reset should be perfomed to clear this bit and move port + * to connected state. + */ +#define PORT_CAS (1 << 24) /* wake on connect (enable) */ #define PORT_WKCONN_E (1 << 25) /* wake on disconnect (enable) */ -- cgit v1.2.3 From 0d9f78a92ef5e97d9fe51d9215ebe22f6f0d289d Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 21 Jun 2012 16:28:30 -0700 Subject: xhci: Fix hang on back-to-back Set TR Deq Ptr commands. The Microsoft LifeChat 3000 USB headset was causing a very reproducible hang whenever it was plugged in. At first, I thought the host controller was producing bad transfer events, because the log was filled with errors like: xhci_hcd 0000:00:14.0: ERROR Transfer event TRB DMA ptr not part of current TD However, it turned out to be an xHCI driver bug in the ring expansion patches. The bug is triggered When there are two ring segments, and a TD that ends just before a link TRB, like so: ______________ _____________ | | ---> | setup TRB B | ______________ | _____________ | | | | data TRB B | ______________ | _____________ | setup TRB A | <-- deq | | data TRB B | ______________ | _____________ | data TRB A | | | | <-- enq, deq'' ______________ | _____________ | status TRB A | | | | ______________ | _____________ | link TRB |--------------- | link TRB | _____________ <--- deq' _____________ TD A (the first control transfer) stalls on the data phase. That halts the ring. The xHCI driver moves the hardware dequeue pointer to the first TRB after the stalled transfer, which happens to be the link TRB. Once the Set TR dequeue pointer command completes, the function update_ring_for_set_deq_completion runs. That function is supposed to update the xHCI driver's dequeue pointer to match the internal hardware dequeue pointer. On the first call this would work fine, and the software dequeue pointer would move to deq'. However, if the transfer immediately after that stalled (TD B in this case), another Set TR Dequeue command would be issued. That would move the hardware dequeue pointer to deq''. Once that command completed, update_ring_for_set_deq_completion would run again. The original code would unconditionally increment the software dequeue pointer, which moved the pointer off the ring segment into la-la-land. The while loop would happy increment the dequeue pointer (possibly wrapping it) until it matched the hardware pointer value. The while loop would also access all the memory in between the first ring segment and the second ring segment to determine if it was a link TRB. This could cause general protection faults, although it was unlikely because the ring segments came from a DMA pool, and would often have consecutive memory addresses. If nothing in that space looked like a link TRB, the deq_seg pointer for the ring would remain on the first segment. Thus, the deq_seg and the software dequeue pointer would get out of sync. When the next transfer event came in after the stalled transfer, the xHCI driver code would attempt to convert the software dequeue pointer into a DMA address in order to compare the DMA address for the completed transfer. Since the deq_seg and the dequeue pointer were out of sync, xhci_trb_virt_to_dma would return NULL. The transfer event would get ignored, the transfer would eventually timeout, and we would mistakenly convert the finished transfer to no-op TRBs. Some kernel driver (maybe xHCI?) would then get stuck in an infinite loop in interrupt context, and the whole machine would hang. This patch should be backported to kernels as old as 3.4, that contain the commit b008df60c6369ba0290fa7daa177375407a12e07 "xHCI: count free TRBs on transfer ring" Signed-off-by: Sarah Sharp Cc: Andiry Xu Cc: stable@vger.kernel.org --- drivers/usb/host/xhci-ring.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 23b4aefd1036..8275645889da 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -885,6 +885,17 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, num_trbs_free_temp = ep_ring->num_trbs_free; dequeue_temp = ep_ring->dequeue; + /* If we get two back-to-back stalls, and the first stalled transfer + * ends just before a link TRB, the dequeue pointer will be left on + * the link TRB by the code in the while loop. So we have to update + * the dequeue pointer one segment further, or we'll jump off + * the segment into la-la-land. + */ + if (last_trb(xhci, ep_ring, ep_ring->deq_seg, ep_ring->dequeue)) { + ep_ring->deq_seg = ep_ring->deq_seg->next; + ep_ring->dequeue = ep_ring->deq_seg->trbs; + } + while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) { /* We have more usable TRBs */ ep_ring->num_trbs_free++; -- cgit v1.2.3 From fc448a18ae6219af9a73257b1fbcd009efab4a81 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 10:37:30 +1000 Subject: md/raid10: Don't try to recovery unmatched (and unused) chunks. If a RAID10 has an odd number of chunks - as might happen when there are an odd number of devices - the last chunk has no pair and so is not mirrored. We don't store data there, but when recovering the last device in an array we retry to recover that last chunk from a non-existent location. This results in an error, and the recovery aborts. When we get to that last chunk we should just stop - there is nothing more to do anyway. This bug has been present since the introduction of RAID10, so the patch is appropriate for any -stable kernel. Cc: stable@vger.kernel.org Reported-by: Christian Balzer Tested-by: Christian Balzer Signed-off-by: NeilBrown --- drivers/md/raid10.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 99ae6068e456..bcf6ea8acc9f 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2890,6 +2890,12 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, /* want to reconstruct this device */ rb2 = r10_bio; sect = raid10_find_virt(conf, sector_nr, i); + if (sect >= mddev->resync_max_sectors) { + /* last stripe is not complete - don't + * try to recover this sector. + */ + continue; + } /* Unless we are doing a full sync, or a replacement * we only need to recover the block if it is set in * the bitmap -- cgit v1.2.3 From 5cfb22a1f83e4f04c0a4df89b60053a077222e2b Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 11:46:53 +1000 Subject: md/raid5: prefer replacing failed devices over want-replacement devices. If a RAID5 has both a failed device and a device marked as 'WantReplacement', then we should preferentially replace the failed device. However the current code replaces whichever is found first. So split into 2 loops, check fail failed/missing first, and only check for WantReplacement if nothing is failed or missing. Reported-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/raid5.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index d26767246d26..95fcbbf3d6c9 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5465,10 +5465,9 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev) if (rdev->saved_raid_disk >= 0 && rdev->saved_raid_disk >= first && conf->disks[rdev->saved_raid_disk].rdev == NULL) - disk = rdev->saved_raid_disk; - else - disk = first; - for ( ; disk <= last ; disk++) { + first = rdev->saved_raid_disk; + + for (disk = first; disk <= last; disk++) { p = conf->disks + disk; if (p->rdev == NULL) { clear_bit(In_sync, &rdev->flags); @@ -5477,8 +5476,11 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev) if (rdev->saved_raid_disk != disk) conf->fullsync = 1; rcu_assign_pointer(p->rdev, rdev); - break; + goto out; } + } + for (disk = first; disk <= last; disk++) { + p = conf->disks + disk; if (test_bit(WantReplacement, &p->rdev->flags) && p->replacement == NULL) { clear_bit(In_sync, &rdev->flags); @@ -5490,6 +5492,7 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev) break; } } +out: print_raid5_conf(conf); return err; } -- cgit v1.2.3 From 6c0544e255dd6582a9899572e120fb55d9f672a4 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 12 Jun 2012 08:31:10 +0800 Subject: md/raid5: Do not add data_offset before call to is_badblock In chunk_aligned_read() we are adding data_offset before calling is_badblock. But is_badblock also adds data_offset, so that is bad. So move the addition of data_offset to after the call to is_badblock. This bug was introduced by commit 31c176ecdf3563140e639 md/raid5: avoid reading from known bad blocks. which first appeared in 3.0. So that patch is suitable for any -stable kernel from 3.0.y onwards. However it will need minor revision for most of those (as the comment didn't appear until recently). Cc: stable@vger.kernel.org Signed-off-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/raid5.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 95fcbbf3d6c9..9567a9c83a1f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3881,8 +3881,6 @@ static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio) raid_bio->bi_next = (void*)rdev; align_bi->bi_bdev = rdev->bdev; align_bi->bi_flags &= ~(1 << BIO_SEG_VALID); - /* No reshape active, so we can trust rdev->data_offset */ - align_bi->bi_sector += rdev->data_offset; if (!bio_fits_rdev(align_bi) || is_badblock(rdev, align_bi->bi_sector, align_bi->bi_size>>9, @@ -3893,6 +3891,9 @@ static int chunk_aligned_read(struct mddev *mddev, struct bio * raid_bio) return 0; } + /* No reshape active, so we can trust rdev->data_offset */ + align_bi->bi_sector += rdev->data_offset; + spin_lock_irq(&conf->device_lock); wait_event_lock_irq(conf->wait_for_stripe, conf->quiesce == 0, -- cgit v1.2.3 From 1850753d2e6d9ca7856581ca5d3cf09521e6a5d7 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 3 Jul 2012 12:11:54 +1000 Subject: md/raid5: In ops_run_io, inc nr_pending before calling md_wait_for_blocked_rdev In ops_run_io(), the call to md_wait_for_blocked_rdev will decrement nr_pending so we lose the reference we hold on the rdev. So atomic_inc it first to maintain the reference. This bug was introduced by commit 73e92e51b7969ef5477d md/raid5. Don't write to known bad block on doubtful devices. which appeared in 3.0, so patch is suitable for stable kernels since then. Cc: stable@vger.kernel.org Signed-off-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/raid5.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 9567a9c83a1f..befadb41a11f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -606,6 +606,12 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) * a chance*/ md_check_recovery(conf->mddev); } + /* + * Because md_wait_for_blocked_rdev + * will dec nr_pending, we must + * increment it first. + */ + atomic_inc(&rdev->nr_pending); md_wait_for_blocked_rdev(rdev, conf->mddev); } else { /* Acknowledged bad block - skip the write */ -- cgit v1.2.3 From 7c2c57c9a98bf5961e438a376486f95346f6b0c5 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 3 Jul 2012 12:12:26 +1000 Subject: md:Add blk_plug in sync_thread. Add blk_plug in sync_thread will increase the performance of sync. Because sync_thread did not blk_plug,so when raid sync, the bio merge not well. Testing environment: SATA controller: Intel Corporation 82801JI (ICH10 Family) SATA AHCI Controller. OS:Linux xxx 3.5.0-rc2+ #340 SMP Tue Jun 12 09:00:25 CST 2012 x86_64 x86_64 x86_64 GNU/Linux. RAID5: four ST31000524NS disk. Without blk_plug:recovery speed about 63M/Sec; Add blk_plug:recovery speed about 120M/Sec. Using blktrace: blktrace -d /dev/sdb -w 60 -o -|blkparse -i - without blk_plug: Total (8,16): Reads Queued: 309811, 1239MiB Writes Queued: 0, 0KiB Read Dispatches: 283583, 1189MiB Write Dispatches: 0, 0KiB Reads Requeued: 0 Writes Requeued: 0 Reads Completed: 273351, 1149MiB Writes Completed: 0, 0KiB Read Merges: 23533, 94132KiB Write Merges: 0, 0KiB IO unplugs: 0 Timer unplugs: 0 add blk_plug: Total (8,16): Reads Queued: 428697, 1714MiB Writes Queued: 0, 0KiB Read Dispatches: 3954, 1714MiB Write Dispatches: 0, 0KiB Reads Requeued: 0 Writes Requeued: 0 Reads Completed: 3956, 1715MiB Writes Completed: 0, 0KiB Read Merges: 424743, 1698MiB Write Merges: 0, 0KiB IO unplugs: 0 Timer unplugs: 3384 The ratio of merge will be markedly increased. Signed-off-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/md.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 1c2f9048e1ae..973aa8459e98 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -7298,6 +7298,7 @@ void md_do_sync(struct mddev *mddev) int skipped = 0; struct md_rdev *rdev; char *desc; + struct blk_plug plug; /* just incase thread restarts... */ if (test_bit(MD_RECOVERY_DONE, &mddev->recovery)) @@ -7447,6 +7448,7 @@ void md_do_sync(struct mddev *mddev) } mddev->curr_resync_completed = j; + blk_start_plug(&plug); while (j < max_sectors) { sector_t sectors; @@ -7552,6 +7554,7 @@ void md_do_sync(struct mddev *mddev) * this also signals 'finished resyncing' to md_stop */ out: + blk_finish_plug(&plug); wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)); /* tell personality that we are finished */ -- cgit v1.2.3 From 5f066c632fcfd2a33f2eb7077c15c630e9f5ea5b Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 12:13:29 +1000 Subject: md/raid5: fix refcount problem when blocked_rdev is set. commit 43220aa0f22cd3ce5b30246d50ccd696d119edea md/raid5: fix a hang on device failure. fixed a hang, but introduced a refcounting in-balance so that if the presence of bad-blocks ever caused an rdev to be 'blocked' we would increment the refcount on the rdev and never decrement it. So added the needed rdev_dec_pending when md_wait_for_blocked_rdev is not called. Reported-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/raid5.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index befadb41a11f..62b6b3a83abf 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3588,8 +3588,18 @@ static void handle_stripe(struct stripe_head *sh) finish: /* wait for this device to become unblocked */ - if (conf->mddev->external && unlikely(s.blocked_rdev)) - md_wait_for_blocked_rdev(s.blocked_rdev, conf->mddev); + if (unlikely(s.blocked_rdev)) { + if (conf->mddev->external) + md_wait_for_blocked_rdev(s.blocked_rdev, + conf->mddev); + else + /* Internal metadata will immediately + * be written by raid5d, so we don't + * need to wait here. + */ + rdev_dec_pending(s.blocked_rdev, + conf->mddev); + } if (s.handle_bad_blocks) for (i = disks; i--; ) { -- cgit v1.2.3 From 1ea6607d4cdc917987e7e6cfaafc4a630d97a297 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 18 Jun 2012 10:06:09 -0300 Subject: serial: mxs-auart: Allow device tree probing Allow device tree probing. Cc: Grant Likely Cc: Rob Herring Cc: Alan Cox Cc: Signed-off-by: Fabio Estevam Signed-off-by: Subodh Nijsure Acked-by: Greg Kroah-Hartman Signed-off-by: Shawn Guo --- drivers/tty/serial/mxs-auart.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index ec56d8397aae..2e341b81ff89 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -675,6 +676,30 @@ static struct uart_driver auart_driver = { #endif }; +/* + * This function returns 1 if pdev isn't a device instatiated by dt, 0 if it + * could successfully get all information from dt or a negative errno. + */ +static int serial_mxs_probe_dt(struct mxs_auart_port *s, + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + int ret; + + if (!np) + /* no device tree device */ + return 1; + + ret = of_alias_get_id(np, "serial"); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get alias id: %d\n", ret); + return ret; + } + s->port.line = ret; + + return 0; +} + static int __devinit mxs_auart_probe(struct platform_device *pdev) { struct mxs_auart_port *s; @@ -689,6 +714,12 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) goto out; } + ret = serial_mxs_probe_dt(s, pdev); + if (ret > 0) + s->port.line = pdev->id < 0 ? 0 : pdev->id; + else if (ret < 0) + goto out_free; + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(pinctrl)) { ret = PTR_ERR(pinctrl); @@ -711,7 +742,6 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) s->port.membase = ioremap(r->start, resource_size(r)); s->port.ops = &mxs_auart_ops; s->port.iotype = UPIO_MEM; - s->port.line = pdev->id < 0 ? 0 : pdev->id; s->port.fifosize = 16; s->port.uartclk = clk_get_rate(s->clk); s->port.type = PORT_IMX; @@ -728,7 +758,7 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); - auart_port[pdev->id] = s; + auart_port[s->port.line] = s; mxs_auart_reset(&s->port); @@ -769,12 +799,19 @@ static int __devexit mxs_auart_remove(struct platform_device *pdev) return 0; } +static struct of_device_id mxs_auart_dt_ids[] = { + { .compatible = "fsl,imx23-auart", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxs_auart_dt_ids); + static struct platform_driver mxs_auart_driver = { .probe = mxs_auart_probe, .remove = __devexit_p(mxs_auart_remove), .driver = { .name = "mxs-auart", .owner = THIS_MODULE, + .of_match_table = mxs_auart_dt_ids, }, }; @@ -807,3 +844,4 @@ module_init(mxs_auart_init); module_exit(mxs_auart_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Freescale MXS application uart driver"); +MODULE_ALIAS("platform:mxs-auart"); -- cgit v1.2.3 From dd8d20a3f32a7ba37526f5b4dfd4d35a93e5342f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 26 Jun 2012 16:29:27 +0800 Subject: rtc: stmp3xxx: Add simple binding for the stmp3xxx-rtc Signed-off-by: Marek Vasut Cc: Alessandro Zummo Cc: devicetree-discuss@lists.ozlabs.org Cc: Grant Likely Cc: rtc-linux@googlegroups.com Acked-by: Rob Herring Signed-off-by: Shawn Guo --- drivers/rtc/rtc-stmp3xxx.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 10287865e330..739ef55694f4 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -265,6 +266,12 @@ static int stmp3xxx_rtc_resume(struct platform_device *dev) #define stmp3xxx_rtc_resume NULL #endif +static const struct of_device_id rtc_dt_ids[] = { + { .compatible = "fsl,stmp3xxx-rtc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rtc_dt_ids); + static struct platform_driver stmp3xxx_rtcdrv = { .probe = stmp3xxx_rtc_probe, .remove = stmp3xxx_rtc_remove, @@ -273,6 +280,7 @@ static struct platform_driver stmp3xxx_rtcdrv = { .driver = { .name = "stmp3xxx-rtc", .owner = THIS_MODULE, + .of_match_table = rtc_dt_ids, }, }; -- cgit v1.2.3 From ce4409b5821f170c018b94bacb36df1ffadb751f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 Jun 2012 19:22:09 +0800 Subject: video: mxsfb: move mxsfb.h into include/linux Move mxsfb.h into include/linux, so that mxsfb driver does not have to include header. Signed-off-by: Shawn Guo --- drivers/video/mxsfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index abbe691047bd..08dad8d8abba 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #define REG_SET 4 #define REG_CLR 8 -- cgit v1.2.3 From 73fc610f9a891fd777040bbb1854ae0b58edee9c Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 25 Jun 2012 19:54:35 +0800 Subject: video: mxsfb: add simple device tree probe Add a simple device tree probe support for mxsfb driver. Before a common binding for struct fb_videomode is available, the driver will keep using struct mxsfb_platform_data. That said, platform code will use auxdata to attach mxsfb_platform_data for device tree boot. Signed-off-by: Shawn Guo --- drivers/video/mxsfb.c | 60 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 08dad8d8abba..49619b441500 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -41,6 +41,8 @@ #include #include +#include +#include #include #include #include @@ -750,16 +752,43 @@ static void __devexit mxsfb_free_videomem(struct mxsfb_info *host) } } +static struct platform_device_id mxsfb_devtype[] = { + { + .name = "imx23-fb", + .driver_data = MXSFB_V3, + }, { + .name = "imx28-fb", + .driver_data = MXSFB_V4, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, mxsfb_devtype); + +static const struct of_device_id mxsfb_dt_ids[] = { + { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], }, + { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxsfb_dt_ids); + static int __devinit mxsfb_probe(struct platform_device *pdev) { + const struct of_device_id *of_id = + of_match_device(mxsfb_dt_ids, &pdev->dev); struct mxsfb_platform_data *pdata = pdev->dev.platform_data; struct resource *res; struct mxsfb_info *host; struct fb_info *fb_info; struct fb_modelist *modelist; struct pinctrl *pinctrl; + int panel_enable; + enum of_gpio_flags flags; int i, ret; + if (of_id) + pdev->id_entry = of_id->data; + if (!pdata) { dev_err(&pdev->dev, "No platformdata. Giving up\n"); return -ENODEV; @@ -807,6 +836,22 @@ static int __devinit mxsfb_probe(struct platform_device *pdev) goto error_getclock; } + panel_enable = of_get_named_gpio_flags(pdev->dev.of_node, + "panel-enable-gpios", 0, &flags); + if (gpio_is_valid(panel_enable)) { + unsigned long f = GPIOF_OUT_INIT_HIGH; + if (flags == OF_GPIO_ACTIVE_LOW) + f = GPIOF_OUT_INIT_LOW; + ret = devm_gpio_request_one(&pdev->dev, panel_enable, + f, "panel-enable"); + if (ret) { + dev_err(&pdev->dev, + "failed to request gpio %d: %d\n", + panel_enable, ret); + goto error_panel_enable; + } + } + fb_info->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); if (!fb_info->pseudo_palette) { ret = -ENOMEM; @@ -854,6 +899,7 @@ error_register: error_init_fb: kfree(fb_info->pseudo_palette); error_pseudo_pallette: +error_panel_enable: clk_put(host->clk); error_getclock: error_getpin: @@ -901,19 +947,6 @@ static void mxsfb_shutdown(struct platform_device *pdev) writel(CTRL_RUN, host->base + LCDC_CTRL + REG_CLR); } -static struct platform_device_id mxsfb_devtype[] = { - { - .name = "imx23-fb", - .driver_data = MXSFB_V3, - }, { - .name = "imx28-fb", - .driver_data = MXSFB_V4, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mxsfb_devtype); - static struct platform_driver mxsfb_driver = { .probe = mxsfb_probe, .remove = __devexit_p(mxsfb_remove), @@ -921,6 +954,7 @@ static struct platform_driver mxsfb_driver = { .id_table = mxsfb_devtype, .driver = { .name = DRIVER_NAME, + .of_match_table = mxsfb_dt_ids, }, }; -- cgit v1.2.3 From 055d3747dbf00ce85c6872ecca4d466638e80c22 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 15:55:33 +1000 Subject: md/raid10: fix failure when trying to repair a read error. commit 58c54fcca3bac5bf9290cfed31c76e4c4bfbabaf md/raid10: handle further errors during fix_read_error better. in 3.1 added "r10_sync_page_io" which takes an IO size in sectors. But we were passing the IO size in bytes!!! This resulting in bio_add_page failing, and empty request being sent down, and a consequent BUG_ON in scsi_lib. [fix missing space in error message at same time] This fix is suitable for 3.1.y and later. Cc: stable@vger.kernel.org Reported-by: Christian Balzer Signed-off-by: NeilBrown --- drivers/md/raid10.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index bcf6ea8acc9f..ae73e29298b2 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2310,7 +2310,7 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10 if (r10_sync_page_io(rdev, r10_bio->devs[sl].addr + sect, - s<<9, conf->tmppage, WRITE) + s, conf->tmppage, WRITE) == 0) { /* Well, this device is dead */ printk(KERN_NOTICE @@ -2349,7 +2349,7 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10 switch (r10_sync_page_io(rdev, r10_bio->devs[sl].addr + sect, - s<<9, conf->tmppage, + s, conf->tmppage, READ)) { case 0: /* Well, this device is dead */ @@ -2512,7 +2512,7 @@ read_more: slot = r10_bio->read_slot; printk_ratelimited( KERN_ERR - "md/raid10:%s: %s: redirecting" + "md/raid10:%s: %s: redirecting " "sector %llu to another mirror\n", mdname(mddev), bdevname(rdev->bdev, b), -- cgit v1.2.3 From 0232605d987d8230b254aa139805bbb56a7ca30c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 15:56:52 +1000 Subject: md: make 'name' arg to md_register_thread non-optional. Having the 'name' arg optional and defaulting to the current personality name is no necessary and leads to errors, as when changing the level of an array we can end up using the name of the old level instead of the new one. So make it non-optional and always explicitly pass the name of the level that the array will be. Reported-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/md.c | 2 +- drivers/md/multipath.c | 3 ++- drivers/md/raid1.c | 2 +- drivers/md/raid10.c | 2 +- drivers/md/raid5.c | 4 +++- 5 files changed, 8 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 973aa8459e98..c601c4be77c7 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6751,7 +6751,7 @@ struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev thread->tsk = kthread_run(md_thread, thread, "%s_%s", mdname(thread->mddev), - name ?: mddev->pers->name); + name); if (IS_ERR(thread->tsk)) { kfree(thread); return NULL; diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 9339e67fcc79..61a1833ebaf3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -474,7 +474,8 @@ static int multipath_run (struct mddev *mddev) } { - mddev->thread = md_register_thread(multipathd, mddev, NULL); + mddev->thread = md_register_thread(multipathd, mddev, + "multipath"); if (!mddev->thread) { printk(KERN_ERR "multipath: couldn't allocate thread" " for %s\n", mdname(mddev)); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a9c7981ddd24..39b2a8aa3b23 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2621,7 +2621,7 @@ static struct r1conf *setup_conf(struct mddev *mddev) goto abort; } err = -ENOMEM; - conf->thread = md_register_thread(raid1d, mddev, NULL); + conf->thread = md_register_thread(raid1d, mddev, "raid1"); if (!conf->thread) { printk(KERN_ERR "md/raid1:%s: couldn't allocate thread\n", diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ae73e29298b2..edc1088a1320 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -3427,7 +3427,7 @@ static struct r10conf *setup_conf(struct mddev *mddev) spin_lock_init(&conf->resync_lock); init_waitqueue_head(&conf->wait_barrier); - conf->thread = md_register_thread(raid10d, mddev, NULL); + conf->thread = md_register_thread(raid10d, mddev, "raid10"); if (!conf->thread) goto out; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 62b6b3a83abf..a5135e595866 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -4840,6 +4840,7 @@ static struct r5conf *setup_conf(struct mddev *mddev) int raid_disk, memory, max_disks; struct md_rdev *rdev; struct disk_info *disk; + char pers_name[6]; if (mddev->new_level != 5 && mddev->new_level != 4 @@ -4963,7 +4964,8 @@ static struct r5conf *setup_conf(struct mddev *mddev) printk(KERN_INFO "md/raid:%s: allocated %dkB\n", mdname(mddev), memory); - conf->thread = md_register_thread(raid5d, mddev, NULL); + sprintf(pers_name, "raid%d", mddev->new_level); + conf->thread = md_register_thread(raid5d, mddev, pers_name); if (!conf->thread) { printk(KERN_ERR "md/raid:%s: couldn't allocate thread.\n", -- cgit v1.2.3 From 2e8ac30312973dd20e6807365349ecb1c7e0ea45 Mon Sep 17 00:00:00 2001 From: majianpeng Date: Tue, 3 Jul 2012 15:57:02 +1000 Subject: md/raid456: When read error cannot be recovered, record bad block We may not be able to fix a bad block if: - the array is degraded - the over-write fails. In these cases we currently eject the device, but we should record a bad block if possible. Signed-off-by: majianpeng Signed-off-by: NeilBrown --- drivers/md/raid5.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index a5135e595866..51169ecd7787 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1743,6 +1743,7 @@ static void raid5_end_read_request(struct bio * bi, int error) } else { const char *bdn = bdevname(rdev->bdev, b); int retry = 0; + int set_bad = 0; clear_bit(R5_UPTODATE, &sh->dev[i].flags); atomic_inc(&rdev->read_errors); @@ -1754,7 +1755,8 @@ static void raid5_end_read_request(struct bio * bi, int error) mdname(conf->mddev), (unsigned long long)s, bdn); - else if (conf->mddev->degraded >= conf->max_degraded) + else if (conf->mddev->degraded >= conf->max_degraded) { + set_bad = 1; printk_ratelimited( KERN_WARNING "md/raid:%s: read error not correctable " @@ -1762,8 +1764,9 @@ static void raid5_end_read_request(struct bio * bi, int error) mdname(conf->mddev), (unsigned long long)s, bdn); - else if (test_bit(R5_ReWrite, &sh->dev[i].flags)) + } else if (test_bit(R5_ReWrite, &sh->dev[i].flags)) { /* Oh, no!!! */ + set_bad = 1; printk_ratelimited( KERN_WARNING "md/raid:%s: read error NOT corrected!! " @@ -1771,7 +1774,7 @@ static void raid5_end_read_request(struct bio * bi, int error) mdname(conf->mddev), (unsigned long long)s, bdn); - else if (atomic_read(&rdev->read_errors) + } else if (atomic_read(&rdev->read_errors) > conf->max_nr_stripes) printk(KERN_WARNING "md/raid:%s: Too many read errors, failing device %s.\n", @@ -1783,7 +1786,11 @@ static void raid5_end_read_request(struct bio * bi, int error) else { clear_bit(R5_ReadError, &sh->dev[i].flags); clear_bit(R5_ReWrite, &sh->dev[i].flags); - md_error(conf->mddev, rdev); + if (!(set_bad + && test_bit(In_sync, &rdev->flags) + && rdev_set_badblocks( + rdev, sh->sector, STRIPE_SECTORS, 0))) + md_error(conf->mddev, rdev); } } rdev_dec_pending(rdev, conf->mddev); -- cgit v1.2.3 From fab363b5ff502d1b39ddcfec04271f5858d9f26e Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 3 Jul 2012 15:57:19 +1000 Subject: raid5: delayed stripe fix There isn't locking setting STRIPE_DELAYED and STRIPE_PREREAD_ACTIVE bits, but the two bits have relationship. A delayed stripe can be moved to hold list only when preread active stripe count is below IO_THRESHOLD. If a stripe has both the bits set, such stripe will be in delayed list and preread count not 0, which will make such stripe never leave delayed list. Signed-off-by: Shaohua Li Signed-off-by: NeilBrown --- drivers/md/raid5.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 51169ecd7787..7245a9df35a9 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -196,12 +196,14 @@ static void __release_stripe(struct r5conf *conf, struct stripe_head *sh) BUG_ON(!list_empty(&sh->lru)); BUG_ON(atomic_read(&conf->active_stripes)==0); if (test_bit(STRIPE_HANDLE, &sh->state)) { - if (test_bit(STRIPE_DELAYED, &sh->state)) + if (test_bit(STRIPE_DELAYED, &sh->state) && + !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) list_add_tail(&sh->lru, &conf->delayed_list); else if (test_bit(STRIPE_BIT_DELAY, &sh->state) && sh->bm_seq - conf->seq_write > 0) list_add_tail(&sh->lru, &conf->bitmap_list); else { + clear_bit(STRIPE_DELAYED, &sh->state); clear_bit(STRIPE_BIT_DELAY, &sh->state); list_add_tail(&sh->lru, &conf->handle_list); } -- cgit v1.2.3 From 32644afd8975d19174bcb9ba34687c32dd810a09 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 15:58:42 +1000 Subject: md/raid1: fix bug in read_balance introduced by hot-replace When we added hot_replace we doubled the number of devices that could be in a RAID1 array. So we doubled how far read_balance would search. Unfortunately we didn't double the point at which it looped back to the beginning - so it effectively loops over all non-replacement disks twice. This doesn't cause bad behaviour, but it pointless and means we never read from replacement devices. Signed-off-by: NeilBrown --- drivers/md/raid1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 39b2a8aa3b23..34b4665cb0b6 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -517,8 +517,8 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect int bad_sectors; int disk = start_disk + i; - if (disk >= conf->raid_disks) - disk -= conf->raid_disks; + if (disk >= conf->raid_disks * 2) + disk -= conf->raid_disks * 2; rdev = rcu_dereference(conf->mirrors[disk].rdev); if (r1_bio->bios[disk] == IO_BLOCKED -- cgit v1.2.3 From f456309106e9657645c81bce1a6bb3230393564e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 15:59:06 +1000 Subject: md: support re-add of recovering devices. We currently only allow a device to be re-added if it appear to be in-sync. This is overly restrictive as it may be desirable to re-add a device that is in the middle of recovery. So remove the test for "InSync" - the test on rdev->raid_disk is sufficient to ensure that the re-add will succeed. Reported-by: Alexander Lyakas Tested-by: Alexander Lyakas Signed-off-by: NeilBrown --- drivers/md/md.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index c601c4be77c7..a4c219e3c859 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5784,8 +5784,7 @@ static int add_new_disk(struct mddev * mddev, mdu_disk_info_t *info) super_types[mddev->major_version]. validate_super(mddev, rdev); if ((info->state & (1<flags) || - rdev->raid_disk != info->raid_disk)) { + rdev->raid_disk != info->raid_disk) { /* This was a hot-add request, but events doesn't * match, so reject it. */ -- cgit v1.2.3 From 30c1e672044d98e5c4cff5fcbdb34b55a2df0c0f Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Thu, 28 Jun 2012 16:21:35 +0800 Subject: can: flexcan: add hardware controller version support At least in the i.MX series, the flexcan contrller divides into ver_3 and ver_10, current driver is for ver_3 controller. i.MX6 has ver_10 controller, it has more reigsters than ver_3 has. The rxfgmask (Rx FIFO Global Mask) register is one of the new added. Its reset value is 0xffffffff, this means ID Filter Table must be checked when receive a packet, but the driver is designed to accept everything during the chip start, we need to clear this register to follow this design. Use the data entry of the struct of_device_id to point chip specific info, we can set hardware version for each platform. Cc: linux-can@vger.kernel.org Cc: Marc Kleine-Budde Cc: Wolfgang Grandegger Cc: Shawn Guo Signed-off-by: Hui Wang [mkl: add id_table support] Tested-by: Hui Wang Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 60 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index b429b3f3fa7f..81324a11a50f 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -165,10 +166,21 @@ struct flexcan_regs { u32 imask1; /* 0x28 */ u32 iflag2; /* 0x2c */ u32 iflag1; /* 0x30 */ - u32 _reserved2[19]; + u32 crl2; /* 0x34 */ + u32 esr2; /* 0x38 */ + u32 imeur; /* 0x3c */ + u32 lrfr; /* 0x40 */ + u32 crcr; /* 0x44 */ + u32 rxfgmask; /* 0x48 */ + u32 rxfir; /* 0x4c */ + u32 _reserved3[12]; struct flexcan_mb cantxfg[64]; }; +struct flexcan_devtype_data { + u32 hw_ver; /* hardware controller version */ +}; + struct flexcan_priv { struct can_priv can; struct net_device *dev; @@ -180,6 +192,15 @@ struct flexcan_priv { struct clk *clk; struct flexcan_platform_data *pdata; + struct flexcan_devtype_data *devtype_data; +}; + +static struct flexcan_devtype_data fsl_p1010_devtype_data = { + .hw_ver = 3, +}; + +static struct flexcan_devtype_data fsl_imx6q_devtype_data = { + .hw_ver = 10, }; static struct can_bittiming_const flexcan_bittiming_const = { @@ -750,6 +771,9 @@ static int flexcan_chip_start(struct net_device *dev) flexcan_write(0x0, ®s->rx14mask); flexcan_write(0x0, ®s->rx15mask); + if (priv->devtype_data->hw_ver >= 10) + flexcan_write(0x0, ®s->rxfgmask); + flexcan_transceiver_switch(priv, 1); /* synchronize with the can bus */ @@ -922,8 +946,21 @@ static void __devexit unregister_flexcandev(struct net_device *dev) unregister_candev(dev); } +static const struct of_device_id flexcan_of_match[] = { + { .compatible = "fsl,p1010-flexcan", .data = &fsl_p1010_devtype_data, }, + { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, + { /* sentinel */ }, +}; + +static const struct platform_device_id flexcan_id_table[] = { + { .name = "flexcan", .driver_data = (kernel_ulong_t)&fsl_p1010_devtype_data, }, + { /* sentinel */ }, +}; + static int __devinit flexcan_probe(struct platform_device *pdev) { + const struct of_device_id *of_id; + struct flexcan_devtype_data *devtype_data; struct net_device *dev; struct flexcan_priv *priv; struct resource *mem; @@ -977,6 +1014,17 @@ static int __devinit flexcan_probe(struct platform_device *pdev) goto failed_alloc; } + of_id = of_match_device(flexcan_of_match, &pdev->dev); + if (of_id) { + devtype_data = of_id->data; + } else if (pdev->id_entry->driver_data) { + devtype_data = (struct flexcan_devtype_data *) + pdev->id_entry->driver_data; + } else { + err = -ENODEV; + goto failed_devtype; + } + dev->netdev_ops = &flexcan_netdev_ops; dev->irq = irq; dev->flags |= IFF_ECHO; @@ -993,6 +1041,7 @@ static int __devinit flexcan_probe(struct platform_device *pdev) priv->dev = dev; priv->clk = clk; priv->pdata = pdev->dev.platform_data; + priv->devtype_data = devtype_data; netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT); @@ -1011,6 +1060,7 @@ static int __devinit flexcan_probe(struct platform_device *pdev) return 0; failed_register: + failed_devtype: free_candev(dev); failed_alloc: iounmap(base); @@ -1044,13 +1094,6 @@ static int __devexit flexcan_remove(struct platform_device *pdev) return 0; } -static struct of_device_id flexcan_of_match[] = { - { - .compatible = "fsl,p1010-flexcan", - }, - {}, -}; - #ifdef CONFIG_PM static int flexcan_suspend(struct platform_device *pdev, pm_message_t state) { @@ -1097,6 +1140,7 @@ static struct platform_driver flexcan_driver = { .remove = __devexit_p(flexcan_remove), .suspend = flexcan_suspend, .resume = flexcan_resume, + .id_table = flexcan_id_table, }; module_platform_driver(flexcan_driver); -- cgit v1.2.3 From b357f04a67c2aeee828b240863cd3f21d6cb3179 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 3 Jul 2012 17:45:31 +1000 Subject: md: fix up plugging (again). The value returned by "mddev_check_plug" is only valid until the next 'schedule' as that will unplug things. This could happen at any call to mempool_alloc. So just calling mddev_check_plug at the start doesn't really make sense. So call it just before, or just after, queuing things for the thread. As the action that happens at unplug is to wake the thread, this makes lots of sense. If we cannot add a plug (which requires a small GFP_ATOMIC alloc) we wake thread immediately. RAID5 is a bit different. Requests are queued for the thread and the thread is woken by release_stripe. So we don't need to wake the thread on failure. However the thread doesn't perform certain actions when there is any active plug, so it is important to install a plug before waking the thread. So for RAID5 we install the plug *before* queuing the request and waking the thread. Without this patch it is possible for raid1 or raid10 to queue a request without then waking the thread, resulting in the array locking up. Also change raid10 to only flush_pending_write when there are not active plugs, just like raid1. This patch is suitable for 3.0 or later. I plan to submit it to -stable, but I'll like to let it spend a few weeks in mainline first to be sure it is completely safe. Signed-off-by: NeilBrown --- drivers/md/raid1.c | 7 ++----- drivers/md/raid10.c | 12 ++++++------ drivers/md/raid5.c | 6 +----- 3 files changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 34b4665cb0b6..8c2754f835ef 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -883,7 +883,6 @@ static void make_request(struct mddev *mddev, struct bio * bio) const unsigned long do_sync = (bio->bi_rw & REQ_SYNC); const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA)); struct md_rdev *blocked_rdev; - int plugged; int first_clone; int sectors_handled; int max_sectors; @@ -1034,7 +1033,6 @@ read_again: * the bad blocks. Each set of writes gets it's own r1bio * with a set of bios attached. */ - plugged = mddev_check_plugged(mddev); disks = conf->raid_disks * 2; retry_write: @@ -1191,6 +1189,8 @@ read_again: bio_list_add(&conf->pending_bio_list, mbio); conf->pending_count++; spin_unlock_irqrestore(&conf->device_lock, flags); + if (!mddev_check_plugged(mddev)) + md_wakeup_thread(mddev->thread); } /* Mustn't call r1_bio_write_done before this next test, * as it could result in the bio being freed. @@ -1213,9 +1213,6 @@ read_again: /* In case raid1d snuck in to freeze_array */ wake_up(&conf->wait_barrier); - - if (do_sync || !bitmap || !plugged) - md_wakeup_thread(mddev->thread); } static void status(struct seq_file *seq, struct mddev *mddev) diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index edc1088a1320..acf5a828c7e1 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1039,7 +1039,6 @@ static void make_request(struct mddev *mddev, struct bio * bio) const unsigned long do_fua = (bio->bi_rw & REQ_FUA); unsigned long flags; struct md_rdev *blocked_rdev; - int plugged; int sectors_handled; int max_sectors; int sectors; @@ -1239,7 +1238,6 @@ read_again: * of r10_bios is recored in bio->bi_phys_segments just as with * the read case. */ - plugged = mddev_check_plugged(mddev); r10_bio->read_slot = -1; /* make sure repl_bio gets freed */ raid10_find_phys(conf, r10_bio); @@ -1396,6 +1394,8 @@ retry_write: bio_list_add(&conf->pending_bio_list, mbio); conf->pending_count++; spin_unlock_irqrestore(&conf->device_lock, flags); + if (!mddev_check_plugged(mddev, 0, 0)) + md_wakeup_thread(mddev->thread); if (!r10_bio->devs[i].repl_bio) continue; @@ -1423,6 +1423,8 @@ retry_write: bio_list_add(&conf->pending_bio_list, mbio); conf->pending_count++; spin_unlock_irqrestore(&conf->device_lock, flags); + if (!mddev_check_plugged(mddev)) + md_wakeup_thread(mddev->thread); } /* Don't remove the bias on 'remaining' (one_write_done) until @@ -1448,9 +1450,6 @@ retry_write: /* In case raid10d snuck in to freeze_array */ wake_up(&conf->wait_barrier); - - if (do_sync || !mddev->bitmap || !plugged) - md_wakeup_thread(mddev->thread); } static void status(struct seq_file *seq, struct mddev *mddev) @@ -2661,7 +2660,8 @@ static void raid10d(struct mddev *mddev) blk_start_plug(&plug); for (;;) { - flush_pending_writes(conf); + if (atomic_read(&mddev->plug_cnt) == 0) + flush_pending_writes(conf); spin_lock_irqsave(&conf->device_lock, flags); if (list_empty(head)) { diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 7245a9df35a9..04348d76bb30 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3997,7 +3997,6 @@ static void make_request(struct mddev *mddev, struct bio * bi) struct stripe_head *sh; const int rw = bio_data_dir(bi); int remaining; - int plugged; if (unlikely(bi->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bi); @@ -4016,7 +4015,6 @@ static void make_request(struct mddev *mddev, struct bio * bi) bi->bi_next = NULL; bi->bi_phys_segments = 1; /* over-loaded to count active stripes */ - plugged = mddev_check_plugged(mddev); for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { DEFINE_WAIT(w); int previous; @@ -4118,6 +4116,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) if ((bi->bi_rw & REQ_SYNC) && !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) atomic_inc(&conf->preread_active_stripes); + mddev_check_plugged(mddev); release_stripe(sh); } else { /* cannot get stripe for read-ahead, just give-up */ @@ -4125,10 +4124,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) finish_wait(&conf->wait_for_overlap, &w); break; } - } - if (!plugged) - md_wakeup_thread(mddev->thread); spin_lock_irq(&conf->device_lock); remaining = raid5_dec_bi_phys_segments(bi); -- cgit v1.2.3 From 1ef5325b238676c7a16bcd374250b07e77682736 Mon Sep 17 00:00:00 2001 From: Jerome Glisse Date: Mon, 2 Jul 2012 12:40:54 -0400 Subject: drm/radeon: fix rare segfault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In gem idle/busy ioctl the radeon object was derefenced after drm_gem_object_unreference_unlocked which in case the object have been destroyed lead to use of a possibly free pointer with possibly wrong data. Signed-off-by: Jerome Glisse Reviewed-by: Alex Deucher Reviewed-by: Christian König Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_gem.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index f28bd4b7ef98..21ec9f5653ce 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -292,6 +292,7 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { + struct radeon_device *rdev = dev->dev_private; struct drm_radeon_gem_busy *args = data; struct drm_gem_object *gobj; struct radeon_bo *robj; @@ -317,13 +318,14 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, break; } drm_gem_object_unreference_unlocked(gobj); - r = radeon_gem_handle_lockup(robj->rdev, r); + r = radeon_gem_handle_lockup(rdev, r); return r; } int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { + struct radeon_device *rdev = dev->dev_private; struct drm_radeon_gem_wait_idle *args = data; struct drm_gem_object *gobj; struct radeon_bo *robj; @@ -336,10 +338,10 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, robj = gem_to_radeon_bo(gobj); r = radeon_bo_wait(robj, NULL, false); /* callback hw specific functions if any */ - if (robj->rdev->asic->ioctl_wait_idle) - robj->rdev->asic->ioctl_wait_idle(robj->rdev, robj); + if (rdev->asic->ioctl_wait_idle) + robj->rdev->asic->ioctl_wait_idle(rdev, robj); drm_gem_object_unreference_unlocked(gobj); - r = radeon_gem_handle_lockup(robj->rdev, r); + r = radeon_gem_handle_lockup(rdev, r); return r; } -- cgit v1.2.3 From 7b668ebe2fce517873b0c28dd70c10fef1d3dc2f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 3 Jul 2012 11:22:11 +0200 Subject: drm: edid: Don't add inferred modes with higher resolution When a monitor EDID doesn't give the preferred bit, driver assumes that the mode with the higest resolution and rate is the preferred mode. Meanwhile the recent changes for allowing more modes in the GFT/CVT ranges give actually more modes, and some modes may be over the native size. Thus such a mode would be picked up as the preferred mode although it's no native resolution. For avoiding such a problem, this patch limits the addition of inferred modes by checking not to be greater than other modes. Also, it checks the duplicated mode entry at the same time. Reviewed-by: Adam Jackson Signed-off-by: Takashi Iwai Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5873e481e5d2..a8743c399e83 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1039,6 +1039,24 @@ mode_in_range(const struct drm_display_mode *mode, struct edid *edid, return true; } +static bool valid_inferred_mode(const struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + struct drm_display_mode *m; + bool ok = false; + + list_for_each_entry(m, &connector->probed_modes, head) { + if (mode->hdisplay == m->hdisplay && + mode->vdisplay == m->vdisplay && + drm_mode_vrefresh(mode) == drm_mode_vrefresh(m)) + return false; /* duplicated */ + if (mode->hdisplay <= m->hdisplay && + mode->vdisplay <= m->vdisplay) + ok = true; + } + return ok; +} + static int drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, struct detailed_timing *timing) @@ -1048,7 +1066,8 @@ drm_dmt_modes_for_range(struct drm_connector *connector, struct edid *edid, struct drm_device *dev = connector->dev; for (i = 0; i < drm_num_dmt_modes; i++) { - if (mode_in_range(drm_dmt_modes + i, edid, timing)) { + if (mode_in_range(drm_dmt_modes + i, edid, timing) && + valid_inferred_mode(connector, drm_dmt_modes + i)) { newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); if (newmode) { drm_mode_probed_add(connector, newmode); @@ -1088,7 +1107,8 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, return modes; fixup_mode_1366x768(newmode); - if (!mode_in_range(newmode, edid, timing)) { + if (!mode_in_range(newmode, edid, timing) || + !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); continue; } @@ -1116,7 +1136,8 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid, return modes; fixup_mode_1366x768(newmode); - if (!mode_in_range(newmode, edid, timing)) { + if (!mode_in_range(newmode, edid, timing) || + !valid_inferred_mode(connector, newmode)) { drm_mode_destroy(dev, newmode); continue; } -- cgit v1.2.3 From 9f846a16d213523fbe6daea17e20df6b8ac5a1e5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 1 Jul 2012 17:09:42 +0200 Subject: drm/i915: kick any firmware framebuffers before claiming the gtt Especially vesafb likes to map everything as uc- (yikes), and if that mapping hangs around still while we try to map the gtt as wc the kernel will downgrade our request to uc-, resulting in abyssal performance. Unfortunately we can't do this as early as readon does (i.e. as the first thing we do when initializing the hw) because our fb/mmio space region moves around on a per-gen basis. So I've had to move it below the gtt initialization, but that seems to work, too. The important thing is that we do this before we set up the gtt wc mapping. Now an altogether different question is why people compile their kernels with vesafb enabled, but I guess making things just work isn't bad per se ... v2: - s/radeondrmfb/inteldrmfb/ - fix up error handling v3: Kill #ifdef X86, this is Intel after all. Noticed by Ben Widawsky. v4: Jani Nikula complained about the pointless bool primary initialization. v5: Don't oops if we can't allocate, noticed by Chris Wilson. v6: Resolve conflicts with agp rework and fixup whitespace. This is commit e188719a2891f01b3100d in drm-next. Backport to 3.5 -fixes queue requested by Dave Airlie - due to grub using vesa on fedora their initrd seems to load vesafb before loading the real kms driver. So tons more people actually experience a dead-slow gpu. Hence also the Cc: stable. Cc: stable@vger.kernel.org Reported-and-tested-by: "Kilarski, Bernard R" Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index f94792626b94..36822b924eb1 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1401,6 +1401,27 @@ i915_mtrr_setup(struct drm_i915_private *dev_priv, unsigned long base, } } +static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) +{ + struct apertures_struct *ap; + struct pci_dev *pdev = dev_priv->dev->pdev; + bool primary; + + ap = alloc_apertures(1); + if (!ap) + return; + + ap->ranges[0].base = dev_priv->dev->agp->base; + ap->ranges[0].size = + dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; + primary = + pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; + + remove_conflicting_framebuffers(ap, "inteldrmfb", primary); + + kfree(ap); +} + /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device @@ -1446,6 +1467,15 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto free_priv; } + dev_priv->mm.gtt = intel_gtt_get(); + if (!dev_priv->mm.gtt) { + DRM_ERROR("Failed to initialize GTT\n"); + ret = -ENODEV; + goto put_bridge; + } + + i915_kick_out_firmware_fb(dev_priv); + pci_set_master(dev->pdev); /* overlay on gen2 is broken and can't address above 1G */ @@ -1471,13 +1501,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) goto put_bridge; } - dev_priv->mm.gtt = intel_gtt_get(); - if (!dev_priv->mm.gtt) { - DRM_ERROR("Failed to initialize GTT\n"); - ret = -ENODEV; - goto out_rmmap; - } - aperture_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; dev_priv->mm.gtt_mapping = -- cgit v1.2.3 From e3e1a0bcb3f192fe2f95f86a74bd4e7967341e74 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Mon, 2 Jul 2012 19:46:16 +0200 Subject: mac80211: reduce IEEE80211_TX_MAX_RATES IEEE80211_TX_MAX_RATES can be reduced from 5 to 4 as there is no current hardware supporting a rate chain with 5 multi rate stages (mrr), so 4 mrr stages are sufficient. The memory that is freed within the ieee80211_tx_info struct will be used in the upcoming Transmission Power Control (TPC) implementation. Suggested-by: Felix Fietkau Signed-off-by: Thomas Huehn [reword commit message] Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/carl9170/tx.c | 6 +++--- drivers/net/wireless/p54/txrx.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index aed305177af6..ede0b572cebc 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -277,11 +277,11 @@ static void carl9170_tx_release(struct kref *ref) return; BUILD_BUG_ON( - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len) != 23); + offsetof(struct ieee80211_tx_info, status.ack_signal) != 20); - memset(&txinfo->status.ampdu_ack_len, 0, + memset(&txinfo->status.ack_signal, 0, sizeof(struct ieee80211_tx_info) - - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); + offsetof(struct ieee80211_tx_info, status.ack_signal)); if (atomic_read(&ar->tx_total_queued)) ar->tx_schedule = true; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 82a1cac920bd..f38786e02623 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -422,11 +422,11 @@ static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) * Clear manually, ieee80211_tx_info_clear_status would * clear the counts too and we need them. */ - memset(&info->status.ampdu_ack_len, 0, + memset(&info->status.ack_signal, 0, sizeof(struct ieee80211_tx_info) - - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); + offsetof(struct ieee80211_tx_info, status.ack_signal)); BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, - status.ampdu_ack_len) != 23); + status.ack_signal) != 20); if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) pad = entry_data->align[0]; -- cgit v1.2.3 From 1b083ea4c072e49a6b0d4a1a6d5d9a6e4b3606b1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 3 Jul 2012 13:14:49 +0200 Subject: mac80211_hwsim: fix NUM_BANDS usage Due to the recent change of NUM_BANDS from 2 to 3 hwsim broke. Fix the code by using the right constant but don't support bands other than 2.4 and 5 GHz. Reported-by: Dan Carpenter Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5bddf53ece1d..c68728c6c6fa 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -292,7 +292,7 @@ struct mac80211_hwsim_data { struct list_head list; struct ieee80211_hw *hw; struct device *dev; - struct ieee80211_supported_band bands[2]; + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; @@ -1855,7 +1855,7 @@ static int __init init_mac80211_hwsim(void) sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; break; default: - break; + continue; } sband->ht_cap.ht_supported = true; -- cgit v1.2.3 From 0d200aefd4ac51787b6b80de1bb7ce93bccd59f6 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 3 Jul 2012 12:55:31 +0100 Subject: dm thin: commit metadata before creating metadata snapshot Userland sometimes sees a corrupt metadata block if metadata is changing rapidly when a metadata snapshot is reserved for userland, To make the problem go away, commit before we take the metadata snapshot (which is a sensible thing to do anyway). The checksums mean userland spots this corruption immediately so there's no risk of acting on incorrect data. No corruption exists from the kernel's point of view, and thin_check passes after pool shutdown. I believe this is to do with shared blocks at the first level of the {device, mapping} btree. Prior to the metadata-snap support no sharing at this level was possible, so this patch is only required after commit cc8394d86f045b86ff303d3c9e4ce47d97148951 ("dm thin: provide userspace access to pool metadata"). Signed-off-by: Joe Thornber Signed-off-by: Mike Snitzer Signed-off-by: Alasdair G Kergon --- drivers/md/dm-thin.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 37fdaf81bd1f..ce59824fb414 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -2292,6 +2292,13 @@ static int process_reserve_metadata_snap_mesg(unsigned argc, char **argv, struct if (r) return r; + r = dm_pool_commit_metadata(pool->pmd); + if (r) { + DMERR("%s: dm_pool_commit_metadata() failed, error = %d", + __func__, r); + return r; + } + r = dm_pool_reserve_metadata_snap(pool->pmd); if (r) DMWARN("reserve_metadata_snap message failed."); -- cgit v1.2.3 From 25d7cd6faa7ae6ed2565617c3ee2500ccb8a9f7f Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 3 Jul 2012 12:55:33 +0100 Subject: dm persistent data: fix shadow_info_leak on dm_tm_destroy Cleanup the shadow table before destroying the transaction manager. Reference: leak was identified with kmemleak when running test_discard_random_sectors in the thinp-test-suite. Signed-off-by: Mike Snitzer Cc: stable@vger.kernel.org Signed-off-by: Alasdair G Kergon --- drivers/md/persistent-data/dm-transaction-manager.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index 400fe144c0cd..02bf78e9d10d 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c @@ -138,6 +138,9 @@ EXPORT_SYMBOL_GPL(dm_tm_create_non_blocking_clone); void dm_tm_destroy(struct dm_transaction_manager *tm) { + if (!tm->is_clone) + wipe_shadow_table(tm); + kfree(tm); } EXPORT_SYMBOL_GPL(dm_tm_destroy); -- cgit v1.2.3 From 62662303e7f590fdfbb0070ab820a0ad4267c119 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 3 Jul 2012 12:55:35 +0100 Subject: dm persistent data: handle space map checker creation failure If CONFIG_DM_DEBUG_SPACE_MAPS is enabled and dm_sm_checker_create() fails, dm_tm_create_internal() would still return success even though it cleaned up all resources it was supposed to have created. This will lead to a kernel crash: general protection fault: 0000 [#1] SMP DEBUG_PAGEALLOC ... RIP: 0010:[] [] dm_bufio_get_block_size+0x9/0x20 Call Trace: [] dm_bm_block_size+0xe/0x10 [] sm_ll_init+0x78/0xd0 [] sm_ll_new_disk+0x16/0xa0 [] dm_sm_disk_create+0xfe/0x160 [] dm_pool_metadata_open+0x16e/0x6a0 [] pool_ctr+0x3f0/0x900 [] dm_table_add_target+0x195/0x450 [] table_load+0xe4/0x330 [] ctl_ioctl+0x15a/0x2c0 [] dm_ctl_ioctl+0x13/0x20 [] do_vfs_ioctl+0x98/0x560 [] sys_ioctl+0x91/0xa0 [] system_call_fastpath+0x16/0x1b Fix the space map checker code to return an appropriate ERR_PTR and have dm_sm_disk_create() and dm_tm_create_internal() check for it with IS_ERR. Reported-by: Vivek Goyal Signed-off-by: Mike Snitzer Cc: stable@vger.kernel.org Signed-off-by: Alasdair G Kergon --- drivers/md/persistent-data/dm-space-map-checker.c | 24 +++++++++++----------- drivers/md/persistent-data/dm-space-map-disk.c | 11 +++++++++- .../md/persistent-data/dm-transaction-manager.c | 8 ++++++-- 3 files changed, 28 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c index 50ed53bf4aa2..6d7c8329250f 100644 --- a/drivers/md/persistent-data/dm-space-map-checker.c +++ b/drivers/md/persistent-data/dm-space-map-checker.c @@ -343,25 +343,25 @@ struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) int r; struct sm_checker *smc; - if (!sm) - return NULL; + if (IS_ERR_OR_NULL(sm)) + return ERR_PTR(-EINVAL); smc = kmalloc(sizeof(*smc), GFP_KERNEL); if (!smc) - return NULL; + return ERR_PTR(-ENOMEM); memcpy(&smc->sm, &ops_, sizeof(smc->sm)); r = ca_create(&smc->old_counts, sm); if (r) { kfree(smc); - return NULL; + return ERR_PTR(r); } r = ca_create(&smc->counts, sm); if (r) { ca_destroy(&smc->old_counts); kfree(smc); - return NULL; + return ERR_PTR(r); } smc->real_sm = sm; @@ -371,7 +371,7 @@ struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) ca_destroy(&smc->counts); ca_destroy(&smc->old_counts); kfree(smc); - return NULL; + return ERR_PTR(r); } r = ca_commit(&smc->old_counts, &smc->counts); @@ -379,7 +379,7 @@ struct dm_space_map *dm_sm_checker_create(struct dm_space_map *sm) ca_destroy(&smc->counts); ca_destroy(&smc->old_counts); kfree(smc); - return NULL; + return ERR_PTR(r); } return &smc->sm; @@ -391,25 +391,25 @@ struct dm_space_map *dm_sm_checker_create_fresh(struct dm_space_map *sm) int r; struct sm_checker *smc; - if (!sm) - return NULL; + if (IS_ERR_OR_NULL(sm)) + return ERR_PTR(-EINVAL); smc = kmalloc(sizeof(*smc), GFP_KERNEL); if (!smc) - return NULL; + return ERR_PTR(-ENOMEM); memcpy(&smc->sm, &ops_, sizeof(smc->sm)); r = ca_create(&smc->old_counts, sm); if (r) { kfree(smc); - return NULL; + return ERR_PTR(r); } r = ca_create(&smc->counts, sm); if (r) { ca_destroy(&smc->old_counts); kfree(smc); - return NULL; + return ERR_PTR(r); } smc->real_sm = sm; diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c index fc469ba9f627..3d0ed5332883 100644 --- a/drivers/md/persistent-data/dm-space-map-disk.c +++ b/drivers/md/persistent-data/dm-space-map-disk.c @@ -290,7 +290,16 @@ struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm, dm_block_t nr_blocks) { struct dm_space_map *sm = dm_sm_disk_create_real(tm, nr_blocks); - return dm_sm_checker_create_fresh(sm); + struct dm_space_map *smc; + + if (IS_ERR_OR_NULL(sm)) + return sm; + + smc = dm_sm_checker_create_fresh(sm); + if (IS_ERR(smc)) + dm_sm_destroy(sm); + + return smc; } EXPORT_SYMBOL_GPL(dm_sm_disk_create); diff --git a/drivers/md/persistent-data/dm-transaction-manager.c b/drivers/md/persistent-data/dm-transaction-manager.c index 02bf78e9d10d..e5604b32d91f 100644 --- a/drivers/md/persistent-data/dm-transaction-manager.c +++ b/drivers/md/persistent-data/dm-transaction-manager.c @@ -347,8 +347,10 @@ static int dm_tm_create_internal(struct dm_block_manager *bm, } *sm = dm_sm_checker_create(inner); - if (!*sm) + if (IS_ERR(*sm)) { + r = PTR_ERR(*sm); goto bad2; + } } else { r = dm_bm_write_lock(dm_tm_get_bm(*tm), sb_location, @@ -367,8 +369,10 @@ static int dm_tm_create_internal(struct dm_block_manager *bm, } *sm = dm_sm_checker_create(inner); - if (!*sm) + if (IS_ERR(*sm)) { + r = PTR_ERR(*sm); goto bad2; + } } return 0; -- cgit v1.2.3 From b0239faaf87c38bb419c9264bf20817438ddc3a9 Mon Sep 17 00:00:00 2001 From: Mike Snitzer Date: Tue, 3 Jul 2012 12:55:37 +0100 Subject: dm persistent data: fix allocation failure in space map checker init If CONFIG_DM_DEBUG_SPACE_MAPS is enabled and memory is fragmented and a sufficiently-large metadata device is used in a thin pool then the space map checker will fail to allocate the memory it requires. Switch from kmalloc to vmalloc to allow larger virtually contiguous allocations for the space map checker's internal count arrays. Reported-by: Vivek Goyal Cc: stable@kernel.org Signed-off-by: Mike Snitzer Signed-off-by: Alasdair G Kergon --- drivers/md/persistent-data/dm-space-map-checker.c | 30 ++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/md/persistent-data/dm-space-map-checker.c b/drivers/md/persistent-data/dm-space-map-checker.c index 6d7c8329250f..fc90c11620ad 100644 --- a/drivers/md/persistent-data/dm-space-map-checker.c +++ b/drivers/md/persistent-data/dm-space-map-checker.c @@ -8,6 +8,7 @@ #include #include +#include #ifdef CONFIG_DM_DEBUG_SPACE_MAPS @@ -89,13 +90,23 @@ static int ca_create(struct count_array *ca, struct dm_space_map *sm) ca->nr = nr_blocks; ca->nr_free = nr_blocks; - ca->counts = kzalloc(sizeof(*ca->counts) * nr_blocks, GFP_KERNEL); - if (!ca->counts) - return -ENOMEM; + + if (!nr_blocks) + ca->counts = NULL; + else { + ca->counts = vzalloc(sizeof(*ca->counts) * nr_blocks); + if (!ca->counts) + return -ENOMEM; + } return 0; } +static void ca_destroy(struct count_array *ca) +{ + vfree(ca->counts); +} + static int ca_load(struct count_array *ca, struct dm_space_map *sm) { int r; @@ -126,12 +137,14 @@ static int ca_load(struct count_array *ca, struct dm_space_map *sm) static int ca_extend(struct count_array *ca, dm_block_t extra_blocks) { dm_block_t nr_blocks = ca->nr + extra_blocks; - uint32_t *counts = kzalloc(sizeof(*counts) * nr_blocks, GFP_KERNEL); + uint32_t *counts = vzalloc(sizeof(*counts) * nr_blocks); if (!counts) return -ENOMEM; - memcpy(counts, ca->counts, sizeof(*counts) * ca->nr); - kfree(ca->counts); + if (ca->counts) { + memcpy(counts, ca->counts, sizeof(*counts) * ca->nr); + ca_destroy(ca); + } ca->nr = nr_blocks; ca->nr_free += extra_blocks; ca->counts = counts; @@ -151,11 +164,6 @@ static int ca_commit(struct count_array *old, struct count_array *new) return 0; } -static void ca_destroy(struct count_array *ca) -{ - kfree(ca->counts); -} - /*----------------------------------------------------------------*/ struct sm_checker { -- cgit v1.2.3 From bd408b33363035275403cfaadf2683d8c8e22dd7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 Jun 2012 14:24:12 +0200 Subject: iwlwifi: remove unneeded NULL check There's no need to check trans for non-null here as it has already been checked in the caller. This fixes an smatch warning that we check after having dereferenced it. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/pcie/rx.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index be143eb4aa4f..39a6ca1f009c 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -879,9 +879,6 @@ static irqreturn_t iwl_isr(int irq, void *data) lockdep_assert_held(&trans_pcie->irq_lock); - if (!trans) - return IRQ_NONE; - trace_iwlwifi_dev_irq(trans->dev); /* Disable (but don't clear!) interrupts here to avoid -- cgit v1.2.3 From 0ff1bd35f5aece42879b04cbfe6b4040cf50a697 Mon Sep 17 00:00:00 2001 From: "Richard A. Griffiths" Date: Thu, 28 Jun 2012 13:14:11 -0700 Subject: iwlwifi: disallow log_event access if interface down 'echo 1 > log_event' generates the bogus "MAC is in deep sleep" or "Timeout waiting for hardware access" log messages when the interface is down, we should just disallow accessing the device through debugfs when it is down. Signed-off-by: Richard A. Griffiths Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/debugfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index 8a2d9e643b14..b0f125c985cd 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -83,7 +83,7 @@ static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ + .read = iwl_dbgfs_##name##_read, \ .open = simple_open, \ .llseek = generic_file_llseek, \ }; @@ -2254,6 +2254,10 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, char buf[8]; int buf_size; + /* check that the interface is up */ + if (!iwl_is_ready(priv)) + return -EAGAIN; + memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) -- cgit v1.2.3 From e25d5c11eeeec610af2e0ff51e60d7cdadefe0f2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 30 Jun 2012 23:50:47 +0200 Subject: hdaps: Use struct dev_pm_ops for power management Make the HDAPS driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki --- drivers/platform/x86/hdaps.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 24a3ae065f1b..d9ab6f64dcec 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -305,17 +305,19 @@ static int hdaps_probe(struct platform_device *dev) return 0; } -static int hdaps_resume(struct platform_device *dev) +static int hdaps_resume(struct device *dev) { return hdaps_device_init(); } +static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume); + static struct platform_driver hdaps_driver = { .probe = hdaps_probe, - .resume = hdaps_resume, .driver = { .name = "hdaps", .owner = THIS_MODULE, + .pm = &hdaps_pm, }, }; -- cgit v1.2.3 From 7b86cef34afceeecf57d4d07f3cfab06f8b60b13 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 3 Jul 2012 11:05:50 -0500 Subject: gpio/omap: fix invalid context restore of gpio bank-0 Currently the gpio _runtime_resume/suspend functions are calling the get_context_loss_count() platform function if the function is populated for a gpio bank. This function is used to determine if the gpio bank logic state needs to be restored due to a power transition. This function will be populated for all banks, but it should only be called for banks that have the "loses_context" variable set. It is pointless to call this if loses_context is false as we know the context will never be lost and will not need restoring. For all OMAP2+ devices gpio bank-0 is in an always-on power domain and so will never lose context. We found that the get_context_loss_count() was being called for bank-0 during the probe and returning 1 instead of 0 indicating that the context had been lost. This was causing the context restore function to be called at probe time for this bank and because the context had never been saved, was restoring an invalid state. This ultimately resulted in a crash [1]. This issue is a regression that was exposed by commit 1b1287032 (gpio/omap: fix missing check in *_runtime_suspend()). There are multiple bugs here that need to be addressed ... 1. Why the always-on power domain returns a context loss count of 1? This needs to be fixed in the power domain code [2]. However, the gpio driver should not assume the loss count is 0 to begin with. 2. The omap gpio driver should never be calling get_context_loss_count for a gpio bank in a always-on domain. This is pointless and adds unneccessary overhead. 3. The OMAP gpio driver assumes that the initial power domain context loss count will be 0 at the time the gpio driver is probed. However, it could be possible that this is not the case and an invalid context restore could be performed during the probe. To avoid this only populate the get_context_loss_count() function pointer after the initial call to pm_runtime_get() has occurred. This will ensure that the first pm_runtime_put() initialised the loss count correctly. This patch addresses issues 2 and 3 above. [1] http://marc.info/?l=linux-omap&m=134065775323775&w=2 [2] http://marc.info/?l=linux-omap&m=134100413303810&w=2 Cc: Kevin Hilman Cc: Grant Likely Cc: Linus Walleij Cc: Tarun Kanti DebBarma Cc: Franky Lin Cc: Santosh Shilimkar Cc: NeilBrown Reported-by: Franky Lin Reviewed-by: Santosh Shilimkar Tested-by: Franky Lin Acked-by: Kevin Hilman Tested-by: NeilBrown Signed-off-by: Jon Hunter Signed-off-by: Kevin Hilman --- drivers/gpio/gpio-omap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index ff213e70fa5a..4fbc208c32cf 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1091,7 +1091,6 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) bank->is_mpuio = pdata->is_mpuio; bank->non_wakeup_gpios = pdata->non_wakeup_gpios; bank->loses_context = pdata->loses_context; - bank->get_context_loss_count = pdata->get_context_loss_count; bank->regs = pdata->regs; #ifdef CONFIG_OF_GPIO bank->chip.of_node = of_node_get(node); @@ -1145,6 +1144,9 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev) omap_gpio_chip_init(bank); omap_gpio_show_rev(bank); + if (bank->loses_context) + bank->get_context_loss_count = pdata->get_context_loss_count; + pm_runtime_put(bank->dev); list_add_tail(&bank->node, &omap_gpio_list); -- cgit v1.2.3 From dc7fd275ae60ef8edf952aff2a62462f5d892fd4 Mon Sep 17 00:00:00 2001 From: ShuoX Liu Date: Tue, 3 Jul 2012 19:05:31 +0200 Subject: cpuidle: move field disable from per-driver to per-cpu Andrew J.Schorr raises a question. When he changes the disable setting on a single CPU, it affects all the other CPUs. Basically, currently, the disable field is per-driver instead of per-cpu. All the C states of the same driver are shared by all CPU in the same machine. The patch changes the `disable' field to per-cpu, so we could set this separately for each cpu. Signed-off-by: ShuoX Liu Reported-by: Andrew J.Schorr Reviewed-by: Yanmin Zhang Signed-off-by: Andrew Morton Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/cpuidle.c | 1 - drivers/cpuidle/governors/menu.c | 5 +++-- drivers/cpuidle/sysfs.c | 21 ++++++++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index d90519cec880..04e4b7674a47 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -265,7 +265,6 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->power_usage = -1; state->flags = 0; state->enter = poll_idle; - state->disable = 0; } #else static void poll_idle_init(struct cpuidle_driver *drv) {} diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 06335756ea14..8391d93f57d5 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -281,7 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * unless the timer is happening really really soon. */ if (data->expected_us > 5 && - drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0) + dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; /* @@ -290,8 +290,9 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) */ for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) { struct cpuidle_state *s = &drv->states[i]; + struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (s->disable) + if (su->disable) continue; if (s->target_residency > data->predicted_us) continue; diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index 88032b4dc6d2..5f809e337b89 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -217,7 +217,8 @@ struct cpuidle_state_attr { struct attribute attr; ssize_t (*show)(struct cpuidle_state *, \ struct cpuidle_state_usage *, char *); - ssize_t (*store)(struct cpuidle_state *, const char *, size_t); + ssize_t (*store)(struct cpuidle_state *, \ + struct cpuidle_state_usage *, const char *, size_t); }; #define define_one_state_ro(_name, show) \ @@ -233,21 +234,22 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ return sprintf(buf, "%u\n", state->_name);\ } -#define define_store_state_function(_name) \ +#define define_store_state_ull_function(_name) \ static ssize_t store_state_##_name(struct cpuidle_state *state, \ + struct cpuidle_state_usage *state_usage, \ const char *buf, size_t size) \ { \ - long value; \ + unsigned long long value; \ int err; \ if (!capable(CAP_SYS_ADMIN)) \ return -EPERM; \ - err = kstrtol(buf, 0, &value); \ + err = kstrtoull(buf, 0, &value); \ if (err) \ return err; \ if (value) \ - state->disable = 1; \ + state_usage->_name = 1; \ else \ - state->disable = 0; \ + state_usage->_name = 0; \ return size; \ } @@ -273,8 +275,8 @@ define_show_state_ull_function(usage) define_show_state_ull_function(time) define_show_state_str_function(name) define_show_state_str_function(desc) -define_show_state_function(disable) -define_store_state_function(disable) +define_show_state_ull_function(disable) +define_store_state_ull_function(disable) define_one_state_ro(name, show_state_name); define_one_state_ro(desc, show_state_desc); @@ -318,10 +320,11 @@ static ssize_t cpuidle_state_store(struct kobject *kobj, { int ret = -EIO; struct cpuidle_state *state = kobj_to_state(kobj); + struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj); struct cpuidle_state_attr *cattr = attr_to_stateattr(attr); if (cattr->store) - ret = cattr->store(state, buf, size); + ret = cattr->store(state, state_usage, buf, size); return ret; } -- cgit v1.2.3 From 6e797a078824b30afbfae6cc4b1c2b21c51761ef Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 16 Jun 2012 15:20:11 +0200 Subject: PM / cpuidle: Add driver reference counter Add a reference counter for the cpuidle driver, so that it can't be unregistered when it is in use. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/driver.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 40cd3f3024df..58bf3b1ac9c4 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -16,6 +16,7 @@ static struct cpuidle_driver *cpuidle_curr_driver; DEFINE_SPINLOCK(cpuidle_driver_lock); +int cpuidle_driver_refcount; static void __cpuidle_register_driver(struct cpuidle_driver *drv) { @@ -89,8 +90,34 @@ void cpuidle_unregister_driver(struct cpuidle_driver *drv) } spin_lock(&cpuidle_driver_lock); - cpuidle_curr_driver = NULL; + + if (!WARN_ON(cpuidle_driver_refcount > 0)) + cpuidle_curr_driver = NULL; + spin_unlock(&cpuidle_driver_lock); } EXPORT_SYMBOL_GPL(cpuidle_unregister_driver); + +struct cpuidle_driver *cpuidle_driver_ref(void) +{ + struct cpuidle_driver *drv; + + spin_lock(&cpuidle_driver_lock); + + drv = cpuidle_curr_driver; + cpuidle_driver_refcount++; + + spin_unlock(&cpuidle_driver_lock); + return drv; +} + +void cpuidle_driver_unref(void) +{ + spin_lock(&cpuidle_driver_lock); + + if (!WARN_ON(cpuidle_driver_refcount <= 0)) + cpuidle_driver_refcount--; + + spin_unlock(&cpuidle_driver_lock); +} -- cgit v1.2.3 From cbc9ef0287ab764d3da0129efa673808df641fe3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 3 Jul 2012 19:07:42 +0200 Subject: PM / Domains: Add preliminary support for cpuidle, v2 On some systems there are CPU cores located in the same power domains as I/O devices. Then, power can only be removed from the domain if all I/O devices in it are not in use and the CPU core is idle. Add preliminary support for that to the generic PM domains framework. First, the platform is expected to provide a cpuidle driver with one extra state designated for use with the generic PM domains code. This state should be initially disabled and its exit_latency value should be set to whatever time is needed to bring up the CPU core itself after restoring power to it, not including the domain's power on latency. Its .enter() callback should point to a procedure that will remove power from the domain containing the CPU core at the end of the CPU power transition. The remaining characteristics of the extra cpuidle state, referred to as the "domain" cpuidle state below, (e.g. power usage, target residency) should be populated in accordance with the properties of the hardware. Next, the platform should execute genpd_attach_cpuidle() on the PM domain containing the CPU core. That will cause the generic PM domains framework to treat that domain in a special way such that: * When all devices in the domain have been suspended and it is about to be turned off, the states of the devices will be saved, but power will not be removed from the domain. Instead, the "domain" cpuidle state will be enabled so that power can be removed from the domain when the CPU core is idle and the state has been chosen as the target by the cpuidle governor. * When the first I/O device in the domain is resumed and __pm_genpd_poweron(() is called for the first time after power has been removed from the domain, the "domain" cpuidle state will be disabled to avoid subsequent surprise power removals via cpuidle. The effective exit_latency value of the "domain" cpuidle state depends on the time needed to bring up the CPU core itself after restoring power to it as well as on the power on latency of the domain containing the CPU core. Thus the "domain" cpuidle state's exit_latency has to be recomputed every time the domain's power on latency is updated, which may happen every time power is restored to the domain, if the measured power on latency is greater than the latency stored in the corresponding generic_pm_domain structure. Signed-off-by: Rafael J. Wysocki Reviewed-by: Kevin Hilman --- drivers/base/power/domain.c | 117 +++++++++++++++++++++++++++++++++++++++ drivers/cpuidle/cpuidle.c | 1 + drivers/cpuidle/governors/menu.c | 3 +- 3 files changed, 120 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index da1d52576ec9..4b5f090fccb6 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -139,6 +139,19 @@ static void genpd_set_active(struct generic_pm_domain *genpd) genpd->status = GPD_STATE_ACTIVE; } +static void genpd_recalc_cpu_exit_latency(struct generic_pm_domain *genpd) +{ + s64 usecs64; + + if (!genpd->cpu_data) + return; + + usecs64 = genpd->power_on_latency_ns; + do_div(usecs64, NSEC_PER_USEC); + usecs64 += genpd->cpu_data->saved_exit_latency; + genpd->cpu_data->idle_state->exit_latency = usecs64; +} + /** * __pm_genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. @@ -176,6 +189,13 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) return 0; } + if (genpd->cpu_data) { + cpuidle_pause_and_lock(); + genpd->cpu_data->idle_state->disabled = true; + cpuidle_resume_and_unlock(); + goto out; + } + /* * The list is guaranteed not to change while the loop below is being * executed, unless one of the masters' .power_on() callbacks fiddles @@ -215,6 +235,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) if (elapsed_ns > genpd->power_on_latency_ns) { genpd->power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true; + genpd_recalc_cpu_exit_latency(genpd); if (genpd->name) pr_warning("%s: Power-on latency exceeded, " "new value %lld ns\n", genpd->name, @@ -222,6 +243,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd) } } + out: genpd_set_active(genpd); return 0; @@ -455,6 +477,21 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd) } } + if (genpd->cpu_data) { + /* + * If cpu_data is set, cpuidle should turn the domain off when + * the CPU in it is idle. In that case we don't decrement the + * subdomain counts of the master domains, so that power is not + * removed from the current domain prematurely as a result of + * cutting off the masters' power. + */ + genpd->status = GPD_STATE_POWER_OFF; + cpuidle_pause_and_lock(); + genpd->cpu_data->idle_state->disabled = false; + cpuidle_resume_and_unlock(); + goto out; + } + if (genpd->power_off) { ktime_t time_start; s64 elapsed_ns; @@ -1600,6 +1637,86 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) } EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); +int genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state) +{ + struct cpuidle_driver *cpuidle_drv; + struct gpd_cpu_data *cpu_data; + struct cpuidle_state *idle_state; + int ret = 0; + + if (IS_ERR_OR_NULL(genpd) || state < 0) + return -EINVAL; + + genpd_acquire_lock(genpd); + + if (genpd->cpu_data) { + ret = -EEXIST; + goto out; + } + cpu_data = kzalloc(sizeof(*cpu_data), GFP_KERNEL); + if (!cpu_data) { + ret = -ENOMEM; + goto out; + } + cpuidle_drv = cpuidle_driver_ref(); + if (!cpuidle_drv) { + ret = -ENODEV; + goto out; + } + if (cpuidle_drv->state_count <= state) { + ret = -EINVAL; + goto err; + } + idle_state = &cpuidle_drv->states[state]; + if (!idle_state->disabled) { + ret = -EAGAIN; + goto err; + } + cpu_data->idle_state = idle_state; + cpu_data->saved_exit_latency = idle_state->exit_latency; + genpd->cpu_data = cpu_data; + genpd_recalc_cpu_exit_latency(genpd); + + out: + genpd_release_lock(genpd); + return ret; + + err: + cpuidle_driver_unref(); + goto out; +} + +int genpd_detach_cpuidle(struct generic_pm_domain *genpd) +{ + struct gpd_cpu_data *cpu_data; + struct cpuidle_state *idle_state; + int ret = 0; + + if (IS_ERR_OR_NULL(genpd)) + return -EINVAL; + + genpd_acquire_lock(genpd); + + cpu_data = genpd->cpu_data; + if (!cpu_data) { + ret = -ENODEV; + goto out; + } + idle_state = cpu_data->idle_state; + if (!idle_state->disabled) { + ret = -EAGAIN; + goto out; + } + idle_state->exit_latency = cpu_data->saved_exit_latency; + cpuidle_driver_unref(); + genpd->cpu_data = NULL; + kfree(cpu_data); + + out: + genpd_release_lock(genpd); + return ret; +} + /* Default device callbacks for generic PM domains. */ /** diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 04e4b7674a47..0132706251df 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -265,6 +265,7 @@ static void poll_idle_init(struct cpuidle_driver *drv) state->power_usage = -1; state->flags = 0; state->enter = poll_idle; + state->disabled = false; } #else static void poll_idle_init(struct cpuidle_driver *drv) {} diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 8391d93f57d5..5b1f2c372c1f 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -281,6 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) * unless the timer is happening really really soon. */ if (data->expected_us > 5 && + !drv->states[CPUIDLE_DRIVER_STATE_START].disabled && dev->states_usage[CPUIDLE_DRIVER_STATE_START].disable == 0) data->last_state_idx = CPUIDLE_DRIVER_STATE_START; @@ -292,7 +293,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) struct cpuidle_state *s = &drv->states[i]; struct cpuidle_state_usage *su = &dev->states_usage[i]; - if (su->disable) + if (s->disabled || su->disable) continue; if (s->target_residency > data->predicted_us) continue; -- cgit v1.2.3 From 863b13271f1608ab3af6f7a371047d9a66693e38 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Tue, 3 Jul 2012 12:11:41 +0530 Subject: clk: fix parent validation in __clk_set_parent() The below commit introduced a bug in __clk_set_parent() which could cause it to *skip* the parent validation which makes sure the parent passed to the api is a valid one. commit 7975059db572eb47f0fb272a62afeae272a4b209 Author: Rajendra Nayak Date: Wed Jun 6 14:41:31 2012 +0530 clk: Allow late cache allocation for clk->parents This was identified by the following compiler warning.. drivers/clk/clk.c: In function '__clk_set_parent': drivers/clk/clk.c:1083:5: warning: 'i' may be used uninitialized in this function [-Wuninitialized] .. as reported by Marc Kleine-Budde. There were various options discussed on how to fix this, one being initing 'i' to clk->num_parents, but the below approach was found to be more appropriate as it also makes the 'parent validation' code simpler to read. Reported-by: Marc Kleine-Budde Signed-off-by: Rajendra Nayak Signed-off-by: Mike Turquette Cc: stable@kernel.org --- drivers/clk/clk.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index dcbe05616090..9a1eb0cfa95f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1067,26 +1067,24 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) old_parent = clk->parent; - /* find index of new parent clock using cached parent ptrs */ - if (clk->parents) - for (i = 0; i < clk->num_parents; i++) - if (clk->parents[i] == parent) - break; - else + if (!clk->parents) clk->parents = kzalloc((sizeof(struct clk*) * clk->num_parents), GFP_KERNEL); /* - * find index of new parent clock using string name comparison - * also try to cache the parent to avoid future calls to __clk_lookup + * find index of new parent clock using cached parent ptrs, + * or if not yet cached, use string name comparison and cache + * them now to avoid future calls to __clk_lookup. */ - if (i == clk->num_parents) - for (i = 0; i < clk->num_parents; i++) - if (!strcmp(clk->parent_names[i], parent->name)) { - if (clk->parents) - clk->parents[i] = __clk_lookup(parent->name); - break; - } + for (i = 0; i < clk->num_parents; i++) { + if (clk->parents && clk->parents[i] == parent) + break; + else if (!strcmp(clk->parent_names[i], parent->name)) { + if (clk->parents) + clk->parents[i] = __clk_lookup(parent->name); + break; + } + } if (i == clk->num_parents) { pr_debug("%s: clock %s is not a possible parent of clock %s\n", -- cgit v1.2.3 From 844e6901df45d2021d1f81b25e61159b5af2687f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 3 Jul 2012 15:19:33 +0800 Subject: regulator: tps65217: Fix voltage boundary checking in tps65217_pmic_map_voltage It is ok to request voltage with min_uV < tps->info[rid]->min_uV and max_uV > tps->info[rid]->max_uV. The equation we used in uv_to_vsel() does not allow min_uV < tps->info[rid]->min_uV, otherwise it returns negative selector. So we need to set min_uV = tps->info[rid]->min_uV if min_uV < tps->info[rid]->min_uV. Signed-off-by: Axel Lin Acked-by: AnilKumar Ch Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 0a3df5b7c904..f7d0495b003e 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -206,10 +206,10 @@ static int tps65217_pmic_map_voltage(struct regulator_dev *dev, if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4) return -EINVAL; - if (min_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV) - return -EINVAL; + if (min_uV < tps->info[rid]->min_uV) + min_uV = tps->info[rid]->min_uV; - if (max_uV < tps->info[rid]->min_uV || max_uV > tps->info[rid]->max_uV) + if (max_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV) return -EINVAL; ret = tps->info[rid]->uv_to_vsel(min_uV, &sel); -- cgit v1.2.3 From 5260cd2bc97318b8477b1d1e99c5a2c53764135b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 20 Jun 2012 17:53:06 +0530 Subject: regulator: dt: regulator match by regulator-compatible Match the device's regulators with the property of "regulator-compatible" of each regulator node. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 68dc3d43dd37..3e4106f2bda9 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -92,15 +92,17 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev, EXPORT_SYMBOL_GPL(of_get_regulator_init_data); /** - * of_regulator_match - extract regulator init data + * of_regulator_match - extract regulator init data when node + * property "regulator-compatible" matches with the regulator name. * @dev: device requesting the data * @node: parent device node of the regulators * @matches: match table for the regulators * @num_matches: number of entries in match table * * This function uses a match table specified by the regulator driver and - * looks up the corresponding init data in the device tree. Note that the - * match table is modified in place. + * looks up the corresponding init data in the device tree if + * regulator-compatible matches. Note that the match table is modified + * in place. * * Returns the number of matches found or a negative error code on failure. */ @@ -110,27 +112,40 @@ int of_regulator_match(struct device *dev, struct device_node *node, { unsigned int count = 0; unsigned int i; + const char *regulator_comp; + struct device_node *child; if (!dev || !node) return -EINVAL; - for (i = 0; i < num_matches; i++) { - struct of_regulator_match *match = &matches[i]; - struct device_node *child; - - child = of_find_node_by_name(node, match->name); - if (!child) - continue; - - match->init_data = of_get_regulator_init_data(dev, child); - if (!match->init_data) { - dev_err(dev, "failed to parse DT for regulator %s\n", + for_each_child_of_node(node, child) { + regulator_comp = of_get_property(child, + "regulator-compatible", NULL); + if (!regulator_comp) { + dev_err(dev, "regulator-compatible is missing for node %s\n", child->name); - return -EINVAL; + continue; + } + for (i = 0; i < num_matches; i++) { + struct of_regulator_match *match = &matches[i]; + if (match->of_node) + continue; + + if (strcmp(match->name, regulator_comp)) + continue; + + match->init_data = + of_get_regulator_init_data(dev, child); + if (!match->init_data) { + dev_err(dev, + "failed to parse DT for regulator %s\n", + child->name); + return -EINVAL; + } + match->of_node = child; + count++; + break; } - - match->of_node = child; - count++; } return count; -- cgit v1.2.3 From d92d95b6bf2722ffa0fefa7651c51bf336743dd7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 2 Jul 2012 19:21:06 -0700 Subject: regulator: Fix recursive mutex lockdep warning A recursive lockdep warning occurs if you call regulator_set_optimum_mode() on a regulator with a supply because there is no nesting annotation for the rdev->mutex. To avoid this warning, get the supply's load before locking the regulator's mutex to avoid grabbing the same class of lock twice. ============================================= [ INFO: possible recursive locking detected ] 3.4.0 #3257 Tainted: G W --------------------------------------------- swapper/0/1 is trying to acquire lock: (&rdev->mutex){+.+.+.}, at: [] regulator_get_voltage+0x18/0x38 but task is already holding lock: (&rdev->mutex){+.+.+.}, at: [] regulator_set_optimum_mode+0x24/0x224 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&rdev->mutex); lock(&rdev->mutex); *** DEADLOCK *** May be due to missing lock nesting notation 3 locks held by swapper/0/1: #0: (&__lockdep_no_validate__){......}, at: [] __driver_attach+0x40/0x8c #1: (&__lockdep_no_validate__){......}, at: [] __driver_attach+0x50/0x8c #2: (&rdev->mutex){+.+.+.}, at: [] regulator_set_optimum_mode+0x24/0x224 stack backtrace: [] (unwind_backtrace+0x0/0x12c) from [] (validate_chain+0x760/0x1080) [] (validate_chain+0x760/0x1080) from [] (__lock_acquire+0x950/0xa10) [] (__lock_acquire+0x950/0xa10) from [] (lock_acquire+0x18c/0x1e8) [] (lock_acquire+0x18c/0x1e8) from [] (mutex_lock_nested+0x68/0x3c4) [] (mutex_lock_nested+0x68/0x3c4) from [] (regulator_get_voltage+0x18/0x38) [] (regulator_get_voltage+0x18/0x38) from [] (regulator_set_optimum_mode+0xa4/0x224) ... Signed-off-by: Stephen Boyd Signed-off-by: Mark Brown --- drivers/regulator/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 09a737c868b5..8b4b3829d9e7 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2519,9 +2519,12 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) { struct regulator_dev *rdev = regulator->rdev; struct regulator *consumer; - int ret, output_uV, input_uV, total_uA_load = 0; + int ret, output_uV, input_uV = 0, total_uA_load = 0; unsigned int mode; + if (rdev->supply) + input_uV = regulator_get_voltage(rdev->supply); + mutex_lock(&rdev->mutex); /* @@ -2554,10 +2557,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) goto out; } - /* get input voltage */ - input_uV = 0; - if (rdev->supply) - input_uV = regulator_get_voltage(rdev->supply); + /* No supply? Use constraint voltage */ if (input_uV <= 0) input_uV = rdev->constraints->input_uV; if (input_uV <= 0) { -- cgit v1.2.3 From c5f3939b8fe0c358d35397982e5afdef112afc81 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 2 Jul 2012 15:00:18 +0100 Subject: regulator: core: Support fixed voltages in regulator_is_supported_voltage() Currently regulator_is_supported_voltage() works by enumerating the set of voltages which can be set by the regulator but the checks we're doing to impose constraints mean that if we can't vary the voltage we'll not report any voltages as supported even though the regulator is actually set at that voltage. We could fix the voltage listing but this would mean that list_voltage() could end up going to the hardware to get the current voltage which isn't expected (it's supposed to be very cheap) so instead special case things when we can't change the voltage and compare the requested range against the current voltage. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d886ee265e47..a80886626230 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1890,8 +1890,18 @@ EXPORT_SYMBOL_GPL(regulator_list_voltage); int regulator_is_supported_voltage(struct regulator *regulator, int min_uV, int max_uV) { + struct regulator_dev *rdev = regulator->rdev; int i, voltages, ret; + /* If we can't change voltage check the current voltage */ + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + ret = regulator_get_voltage(regulator); + if (ret >= 0) + return (min_uV >= ret && ret <= max_uV); + else + return ret; + } + ret = regulator_count_voltages(regulator); if (ret < 0) return ret; -- cgit v1.2.3 From e113d792d56d4b720b3d84c122b6af84c3bfa094 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Jun 2012 10:57:51 +0100 Subject: regulator: core: Check that the selector from map_voltage() is valid Lots of regulator drivers have checks in their map_voltage() functions to verify that the result of the mapping is in the range originally specified. Factor these out in the core and provide a bit of extra defensiveness for other drivers by doing the check in the core. Since we're now doing a list_voltage() earlier move the current mapping back to a voltage out into the set_voltage() call to save redoing it. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a80886626230..f98a8ee29933 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2040,7 +2040,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, { int ret; int delay = 0; - int best_val; + int best_val = 0; unsigned int selector; int old_selector = -1; @@ -2064,6 +2064,15 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, if (rdev->desc->ops->set_voltage) { ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV, &selector); + + if (ret >= 0) { + if (rdev->desc->ops->list_voltage) + best_val = rdev->desc->ops->list_voltage(rdev, + selector); + else + best_val = _regulator_get_voltage(rdev); + } + } else if (rdev->desc->ops->set_voltage_sel) { if (rdev->desc->ops->map_voltage) { ret = rdev->desc->ops->map_voltage(rdev, min_uV, @@ -2079,18 +2088,19 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, } if (ret >= 0) { - selector = ret; - ret = rdev->desc->ops->set_voltage_sel(rdev, ret); + best_val = rdev->desc->ops->list_voltage(rdev, ret); + if (min_uV <= best_val && max_uV >= best_val) { + selector = ret; + ret = rdev->desc->ops->set_voltage_sel(rdev, + ret); + } else { + ret = -EINVAL; + } } } else { ret = -EINVAL; } - if (rdev->desc->ops->list_voltage) - best_val = rdev->desc->ops->list_voltage(rdev, selector); - else - best_val = _regulator_get_voltage(rdev); - /* Call set_voltage_time_sel if successfully obtained old_selector */ if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 && rdev->desc->ops->set_voltage_time_sel) { -- cgit v1.2.3 From 79511ed3225a64f6b7fc749f4f9c1ed82f24f729 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:23:10 +0100 Subject: regulator: core: Allow fixed enable_time to be set in the regulator_desc Many regulators have a fixed specification for their enable time. Allow this to be set in the regulator_desc as a number to save them having to implement an explicit operation. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index aa82c0465f4f..6e488aa6f1e8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1189,7 +1189,7 @@ overflow_err: static int _regulator_get_enable_time(struct regulator_dev *rdev) { if (!rdev->desc->ops->enable_time) - return 0; + return rdev->desc->enable_time; return rdev->desc->ops->enable_time(rdev); } -- cgit v1.2.3 From 3d0f267fcd9197d72551baf60165b40a00dfb66a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:27:22 +0100 Subject: regulator: fixed: Set enable enable_time in regulator_desc Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index b8c3a910502a..5adc35aa6cc7 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -36,7 +36,6 @@ struct fixed_voltage_data { struct regulator_dev *dev; int microvolts; int gpio; - unsigned startup_delay; bool enable_high; bool is_enabled; }; @@ -136,13 +135,6 @@ static int fixed_voltage_disable(struct regulator_dev *dev) return 0; } -static int fixed_voltage_enable_time(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - return data->startup_delay; -} - static int fixed_voltage_get_voltage(struct regulator_dev *dev) { struct fixed_voltage_data *data = rdev_get_drvdata(dev); @@ -168,7 +160,6 @@ static struct regulator_ops fixed_voltage_gpio_ops = { .is_enabled = fixed_voltage_is_enabled, .enable = fixed_voltage_enable, .disable = fixed_voltage_disable, - .enable_time = fixed_voltage_enable_time, .get_voltage = fixed_voltage_get_voltage, .list_voltage = fixed_voltage_list_voltage, }; @@ -213,12 +204,13 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; + drvdata->desc.enable_time = config->startup_delay; + if (config->microvolts) drvdata->desc.n_voltages = 1; drvdata->microvolts = config->microvolts; drvdata->gpio = config->gpio; - drvdata->startup_delay = config->startup_delay; if (gpio_is_valid(config->gpio)) { int gpio_flag; -- cgit v1.2.3 From 00581b30ae75ea0813207803e2b1d3f9abc55b9a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:29:27 +0100 Subject: regulator: wm8994: Set enable_time in descriptor Signed-off-by: Mark Brown --- drivers/regulator/wm8994-regulator.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 0e2028b74d1a..a056cee7ed46 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -70,18 +70,10 @@ static int wm8994_ldo_is_enabled(struct regulator_dev *rdev) return ldo->is_enabled; } -static int wm8994_ldo_enable_time(struct regulator_dev *rdev) -{ - /* 3ms is fairly conservative but this shouldn't be too performance - * critical; can be tweaked per-system if required. */ - return 3000; -} - static struct regulator_ops wm8994_ldo1_ops = { .enable = wm8994_ldo_enable, .disable = wm8994_ldo_disable, .is_enabled = wm8994_ldo_is_enabled, - .enable_time = wm8994_ldo_enable_time, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -119,7 +111,6 @@ static struct regulator_ops wm8994_ldo2_ops = { .enable = wm8994_ldo_enable, .disable = wm8994_ldo_disable, .is_enabled = wm8994_ldo_is_enabled, - .enable_time = wm8994_ldo_enable_time, .list_voltage = wm8994_ldo2_list_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -137,6 +128,7 @@ static const struct regulator_desc wm8994_ldo_desc[] = { .ops = &wm8994_ldo1_ops, .min_uV = 2400000, .uV_step = 100000, + .enable_time = 3000, .owner = THIS_MODULE, }, { @@ -147,6 +139,7 @@ static const struct regulator_desc wm8994_ldo_desc[] = { .vsel_reg = WM8994_LDO_2, .vsel_mask = WM8994_LDO2_VSEL_MASK, .ops = &wm8994_ldo2_ops, + .enable_time = 3000, .owner = THIS_MODULE, }, }; -- cgit v1.2.3 From eefaa3c6280f3d523348e1d200e5a8215ed66fbc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 15:00:02 +0100 Subject: regulator: wm831x-ldo: Specify enable time for alive LDO Signed-off-by: Mark Brown --- drivers/regulator/wm831x-ldo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 04d8a413c260..5cb70ca1e98d 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -701,6 +701,7 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev) ldo->desc.enable_mask = 1 << id; ldo->desc.min_uV = 800000; ldo->desc.uV_step = 50000; + ldo->desc.enable_time = 1000; config.dev = pdev->dev.parent; if (pdata) -- cgit v1.2.3 From 5c5659d0a22ec4f947ef4faa3055767572f15e74 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 13:21:26 +0100 Subject: regulator: core: Factor out enable and disable operations some more Create new _regulator_do_enable() and _regulator_do_disable() operations which deal with the mechanics of performing the enable and disable, partly to cut down on the levels of indentation and partly to support some future work. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 110 +++++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6e488aa6f1e8..82650a16a975 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1476,10 +1476,50 @@ void devm_regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(devm_regulator_put); +static int _regulator_do_enable(struct regulator_dev *rdev) +{ + int ret, delay; + + /* Query before enabling in case configuration dependent. */ + ret = _regulator_get_enable_time(rdev); + if (ret >= 0) { + delay = ret; + } else { + rdev_warn(rdev, "enable_time() failed: %d\n", ret); + delay = 0; + } + + trace_regulator_enable(rdev_get_name(rdev)); + + if (rdev->desc->ops->enable) { + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + } else { + return -EINVAL; + } + + /* Allow the regulator to ramp; it would be useful to extend + * this for bulk operations so that the regulators can ramp + * together. */ + trace_regulator_enable_delay(rdev_get_name(rdev)); + + if (delay >= 1000) { + mdelay(delay / 1000); + udelay(delay % 1000); + } else if (delay) { + udelay(delay); + } + + trace_regulator_enable_complete(rdev_get_name(rdev)); + + return 0; +} + /* locks held by regulator_enable() */ static int _regulator_enable(struct regulator_dev *rdev) { - int ret, delay; + int ret; /* check voltage and requested load before enabling */ if (rdev->constraints && @@ -1493,40 +1533,10 @@ static int _regulator_enable(struct regulator_dev *rdev) if (!_regulator_can_change_status(rdev)) return -EPERM; - if (!rdev->desc->ops->enable) - return -EINVAL; - - /* Query before enabling in case configuration - * dependent. */ - ret = _regulator_get_enable_time(rdev); - if (ret >= 0) { - delay = ret; - } else { - rdev_warn(rdev, "enable_time() failed: %d\n", - ret); - delay = 0; - } - - trace_regulator_enable(rdev_get_name(rdev)); - - /* Allow the regulator to ramp; it would be useful - * to extend this for bulk operations so that the - * regulators can ramp together. */ - ret = rdev->desc->ops->enable(rdev); + ret = _regulator_do_enable(rdev); if (ret < 0) return ret; - trace_regulator_enable_delay(rdev_get_name(rdev)); - - if (delay >= 1000) { - mdelay(delay / 1000); - udelay(delay % 1000); - } else if (delay) { - udelay(delay); - } - - trace_regulator_enable_complete(rdev_get_name(rdev)); - } else if (ret < 0) { rdev_err(rdev, "is_enabled() failed: %d\n", ret); return ret; @@ -1575,6 +1585,30 @@ int regulator_enable(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_enable); +static int _regulator_do_disable(struct regulator_dev *rdev) +{ + int ret; + + trace_regulator_disable(rdev_get_name(rdev)); + + if (rdev->ena_gpio) { + gpio_set_value_cansleep(rdev->ena_gpio, + rdev->ena_gpio_invert); + rdev->ena_gpio_state = 0; + + } else if (rdev->desc->ops->disable) { + ret = rdev->desc->ops->disable(rdev); + if (ret != 0) + return ret; + } + + trace_regulator_disable_complete(rdev_get_name(rdev)); + + _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, + NULL); + return 0; +} + /* locks held by regulator_disable() */ static int _regulator_disable(struct regulator_dev *rdev) { @@ -1589,20 +1623,12 @@ static int _regulator_disable(struct regulator_dev *rdev) (rdev->constraints && !rdev->constraints->always_on)) { /* we are last user */ - if (_regulator_can_change_status(rdev) && - rdev->desc->ops->disable) { - trace_regulator_disable(rdev_get_name(rdev)); - - ret = rdev->desc->ops->disable(rdev); + if (_regulator_can_change_status(rdev)) { + ret = _regulator_do_disable(rdev); if (ret < 0) { rdev_err(rdev, "failed to disable\n"); return ret; } - - trace_regulator_disable_complete(rdev_get_name(rdev)); - - _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, - NULL); } rdev->use_count = 0; -- cgit v1.2.3 From 65f735082de35aa4d44e8d0afe862798d0e09e29 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:14:38 +0100 Subject: regulator: core: Add core support for GPIO controlled enable lines It is very common for regulators to support having their enable signal controlled by a GPIO. Since there are a bunch of fiddly things to get right like handling the operations when the enable signal is tied to a rail and it's just replicated code add support for this to the core. Drivers should set ena_gpio in their config if they have a GPIO control, using ena_gpio_flags to specify any flags (including GPIOF_OUT_INIT_ for the initial state) and ena_gpio_invert if the GPIO is active low. The core will then override any enable and disable operations the driver has and instead control the specified GPIO. This will in the future also allow us to further extend the core by identifying when several enable signals have been tied together and handling this properly. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 82650a16a975..8d81bafcb721 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1491,7 +1492,11 @@ static int _regulator_do_enable(struct regulator_dev *rdev) trace_regulator_enable(rdev_get_name(rdev)); - if (rdev->desc->ops->enable) { + if (rdev->ena_gpio) { + gpio_set_value_cansleep(rdev->ena_gpio, + !rdev->ena_gpio_invert); + rdev->ena_gpio_state = 1; + } else if (rdev->desc->ops->enable) { ret = rdev->desc->ops->enable(rdev); if (ret < 0) return ret; @@ -1846,6 +1851,10 @@ EXPORT_SYMBOL_GPL(regulator_disable_regmap); static int _regulator_is_enabled(struct regulator_dev *rdev) { + /* A GPIO control always takes precedence */ + if (rdev->ena_gpio) + return rdev->ena_gpio_state; + /* If we don't know then assume that the regulator is always on */ if (!rdev->desc->ops->is_enabled) return 1; @@ -3243,6 +3252,26 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); + if (config->ena_gpio) { + ret = gpio_request_one(config->ena_gpio, + GPIOF_DIR_OUT | config->ena_gpio_flags, + rdev_get_name(rdev)); + if (ret != 0) { + rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", + config->ena_gpio, ret); + goto clean; + } + + rdev->ena_gpio = config->ena_gpio; + rdev->ena_gpio_invert = config->ena_gpio_invert; + + if (config->ena_gpio_flags & GPIOF_OUT_INIT_HIGH) + rdev->ena_gpio_state = 1; + + if (rdev->ena_gpio_invert) + rdev->ena_gpio_state = !rdev->ena_gpio_state; + } + /* set regulator constraints */ if (init_data) constraints = &init_data->constraints; @@ -3311,6 +3340,8 @@ unset_supplies: scrub: if (rdev->supply) regulator_put(rdev->supply); + if (rdev->ena_gpio) + gpio_free(rdev->ena_gpio); kfree(rdev->constraints); device_unregister(&rdev->dev); /* device core frees rdev */ @@ -3344,6 +3375,8 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); kfree(rdev->constraints); + if (rdev->ena_gpio) + gpio_free(rdev->ena_gpio); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } -- cgit v1.2.3 From 25a53dfbfbfd10c52109ada40313e058b07b4b21 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:30:33 +0100 Subject: regulator: fixed: Use core GPIO enable support This is essentially the code that was factored out into the core when the feature was implemented. Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 106 ++++++++-------------------------------------- 1 file changed, 18 insertions(+), 88 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 5adc35aa6cc7..1a0b17e8d8a9 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -35,9 +35,6 @@ struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; int microvolts; - int gpio; - bool enable_high; - bool is_enabled; }; @@ -108,33 +105,6 @@ of_get_fixed_voltage_config(struct device *dev) return config; } -static int fixed_voltage_is_enabled(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - return data->is_enabled; -} - -static int fixed_voltage_enable(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - gpio_set_value_cansleep(data->gpio, data->enable_high); - data->is_enabled = true; - - return 0; -} - -static int fixed_voltage_disable(struct regulator_dev *dev) -{ - struct fixed_voltage_data *data = rdev_get_drvdata(dev); - - gpio_set_value_cansleep(data->gpio, !data->enable_high); - data->is_enabled = false; - - return 0; -} - static int fixed_voltage_get_voltage(struct regulator_dev *dev) { struct fixed_voltage_data *data = rdev_get_drvdata(dev); @@ -156,14 +126,6 @@ static int fixed_voltage_list_voltage(struct regulator_dev *dev, return data->microvolts; } -static struct regulator_ops fixed_voltage_gpio_ops = { - .is_enabled = fixed_voltage_is_enabled, - .enable = fixed_voltage_enable, - .disable = fixed_voltage_disable, - .get_voltage = fixed_voltage_get_voltage, - .list_voltage = fixed_voltage_list_voltage, -}; - static struct regulator_ops fixed_voltage_ops = { .get_voltage = fixed_voltage_get_voltage, .list_voltage = fixed_voltage_list_voltage, @@ -203,6 +165,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) } drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; + drvdata->desc.ops = &fixed_voltage_ops; drvdata->desc.enable_time = config->startup_delay; @@ -210,53 +173,25 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.n_voltages = 1; drvdata->microvolts = config->microvolts; - drvdata->gpio = config->gpio; - - if (gpio_is_valid(config->gpio)) { - int gpio_flag; - drvdata->enable_high = config->enable_high; - - /* FIXME: Remove below print warning - * - * config->gpio must be set to -EINVAL by platform code if - * GPIO control is not required. However, early adopters - * not requiring GPIO control may forget to initialize - * config->gpio to -EINVAL. This will cause GPIO 0 to be used - * for GPIO control. - * - * This warning will be removed once there are a couple of users - * for this driver. - */ - if (!config->gpio) - dev_warn(&pdev->dev, - "using GPIO 0 for regulator enable control\n"); - - /* - * set output direction without changing state - * to prevent glitch - */ - drvdata->is_enabled = config->enabled_at_boot; - ret = drvdata->is_enabled ? - config->enable_high : !config->enable_high; - gpio_flag = ret ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; - - if (config->gpio_is_open_drain) - gpio_flag |= GPIOF_OPEN_DRAIN; - - ret = gpio_request_one(config->gpio, gpio_flag, - config->supply_name); - if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator enable GPIO %d: %d\n", - config->gpio, ret); - goto err_name; - } - - drvdata->desc.ops = &fixed_voltage_gpio_ops; + if (config->gpio >= 0) + cfg.ena_gpio = config->gpio; + cfg.ena_gpio_invert = !config->enable_high; + if (config->enabled_at_boot) { + if (config->enable_high) { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + } else { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + } } else { - drvdata->desc.ops = &fixed_voltage_ops; + if (config->enable_high) { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + } else { + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + } } + if (config->gpio_is_open_drain) + cfg.ena_gpio_flags |= GPIOF_OPEN_DRAIN; cfg.dev = &pdev->dev; cfg.init_data = config->init_data; @@ -267,7 +202,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); - goto err_gpio; + goto err_name; } platform_set_drvdata(pdev, drvdata); @@ -277,9 +212,6 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) return 0; -err_gpio: - if (gpio_is_valid(config->gpio)) - gpio_free(config->gpio); err_name: kfree(drvdata->desc.name); err: @@ -291,8 +223,6 @@ static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); regulator_unregister(drvdata->dev); - if (gpio_is_valid(drvdata->gpio)) - gpio_free(drvdata->gpio); kfree(drvdata->desc.name); return 0; -- cgit v1.2.3 From 495fd8c8fdefebf545f8ea01d10ae7b812931c27 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:31:17 +0100 Subject: regulator: wm8994: Use core GPIO enable support Signed-off-by: Mark Brown --- drivers/regulator/wm8994-regulator.c | 68 +++--------------------------------- 1 file changed, 4 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index a056cee7ed46..86bb48db149e 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -26,8 +26,6 @@ #include struct wm8994_ldo { - int enable; - bool is_enabled; struct regulator_dev *regulator; struct wm8994 *wm8994; }; @@ -35,46 +33,7 @@ struct wm8994_ldo { #define WM8994_LDO1_MAX_SELECTOR 0x7 #define WM8994_LDO2_MAX_SELECTOR 0x3 -static int wm8994_ldo_enable(struct regulator_dev *rdev) -{ - struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); - - /* If we have no soft control assume that the LDO is always enabled. */ - if (!ldo->enable) - return 0; - - gpio_set_value_cansleep(ldo->enable, 1); - ldo->is_enabled = true; - - return 0; -} - -static int wm8994_ldo_disable(struct regulator_dev *rdev) -{ - struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); - - /* If we have no soft control assume that the LDO is always enabled. */ - if (!ldo->enable) - return -EINVAL; - - gpio_set_value_cansleep(ldo->enable, 0); - ldo->is_enabled = false; - - return 0; -} - -static int wm8994_ldo_is_enabled(struct regulator_dev *rdev) -{ - struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); - - return ldo->is_enabled; -} - static struct regulator_ops wm8994_ldo1_ops = { - .enable = wm8994_ldo_enable, - .disable = wm8994_ldo_disable, - .is_enabled = wm8994_ldo_is_enabled, - .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .get_voltage_sel = regulator_get_voltage_sel_regmap, @@ -108,10 +67,6 @@ static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, } static struct regulator_ops wm8994_ldo2_ops = { - .enable = wm8994_ldo_enable, - .disable = wm8994_ldo_disable, - .is_enabled = wm8994_ldo_is_enabled, - .list_voltage = wm8994_ldo2_list_voltage, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, @@ -163,39 +118,26 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) ldo->wm8994 = wm8994; - if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) { - ldo->enable = pdata->ldo[id].enable; - - ret = gpio_request_one(ldo->enable, 0, "WM8994 LDO enable"); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n", - ret); - goto err; - } - } else - ldo->is_enabled = true; - config.dev = wm8994->dev; config.driver_data = ldo; config.regmap = wm8994->regmap; - if (pdata) + if (pdata) { config.init_data = pdata->ldo[id].init_data; + config.ena_gpio = pdata->ldo[id].enable; + } ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &config); if (IS_ERR(ldo->regulator)) { ret = PTR_ERR(ldo->regulator); dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", id + 1, ret); - goto err_gpio; + goto err; } platform_set_drvdata(pdev, ldo); return 0; -err_gpio: - if (gpio_is_valid(ldo->enable)) - gpio_free(ldo->enable); err: return ret; } @@ -207,8 +149,6 @@ static __devexit int wm8994_ldo_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); - if (gpio_is_valid(ldo->enable)) - gpio_free(ldo->enable); return 0; } -- cgit v1.2.3 From a9905b1d6fa80a07997520c7f554aedf3e1b47e7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 27 Jun 2012 14:32:06 +0100 Subject: regulator: arizona-ldo1: Implement GPIO enable support Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 51f02cac443f..c8f95c07adb6 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -57,6 +57,9 @@ static const struct regulator_desc arizona_ldo1 = { }; static const struct regulator_init_data arizona_ldo1_default = { + .constraints = { + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, .num_consumer_supplies = 1, }; @@ -88,6 +91,7 @@ static __devinit int arizona_ldo1_probe(struct platform_device *pdev) config.dev = arizona->dev; config.driver_data = ldo1; config.regmap = arizona->regmap; + config.ena_gpio = arizona->pdata.ldoena; if (arizona->pdata.ldo1) config.init_data = arizona->pdata.ldo1; -- cgit v1.2.3 From 5a511cd33f55e78de7b26eec16874535446bd6c0 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Sat, 16 Jun 2012 23:15:37 +0530 Subject: pinctrl/pinctrl-u300: remove devm_kfree at driver unload the memory allocated by devm_kzalloc is automatically freed at the driver detach side, so no neeed of calling devm_kfree Signed-off-by: Devendra Naga Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-u300.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 05d029911be6..13e7a3eb82e3 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1183,7 +1183,6 @@ static int __devexit u300_pmx_remove(struct platform_device *pdev) iounmap(upmx->virtbase); release_mem_region(upmx->phybase, upmx->physize); platform_set_drvdata(pdev, NULL); - devm_kfree(&pdev->dev, upmx); return 0; } -- cgit v1.2.3 From 7e1dfe5319de9ab8c73f3934da4debd0fd88c71c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jun 2012 14:02:25 +0200 Subject: pinctrl/u300: delete pointless debug print This is some leftover from earlier development, let's get rid of it. Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-u300.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 13e7a3eb82e3..6cd697a079f9 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1113,8 +1113,6 @@ static int __devinit u300_pmx_probe(struct platform_device *pdev) int ret; int i; - pr_err("U300 PMX PROBE\n"); - /* Create state holders etc for this driver */ upmx = devm_kzalloc(&pdev->dev, sizeof(*upmx), GFP_KERNEL); if (!upmx) -- cgit v1.2.3 From da9aecb02d8763ef17130dfd7c678747ad84b070 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Sat, 16 Jun 2012 23:43:16 +0530 Subject: pinctrl/pinctrl-core: cleanup pinctrl_register lots of places the ret is used just for non zero cases with out that also we can check the status of the function calls. Signed-off-by: Devendra Naga Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 0cc053af70bd..50d9c289cffd 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1395,9 +1395,9 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct pinctrl_dev *pctldev; int ret; - if (pctldesc == NULL) + if (!pctldesc) return NULL; - if (pctldesc->name == NULL) + if (!pctldesc->name) return NULL; pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL); @@ -1415,23 +1415,20 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, pctldev->dev = dev; /* check core ops for sanity */ - ret = pinctrl_check_ops(pctldev); - if (ret) { + if (pinctrl_check_ops(pctldev)) { dev_err(dev, "pinctrl ops lacks necessary functions\n"); goto out_err; } /* If we're implementing pinmuxing, check the ops for sanity */ if (pctldesc->pmxops) { - ret = pinmux_check_ops(pctldev); - if (ret) + if (pinmux_check_ops(pctldev)) goto out_err; } /* If we're implementing pinconfig, check the ops for sanity */ if (pctldesc->confops) { - ret = pinconf_check_ops(pctldev); - if (ret) + if (pinconf_check_ops(pctldev)) goto out_err; } @@ -1457,11 +1454,9 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, if (IS_ERR(s)) { dev_dbg(dev, "failed to lookup the default state\n"); } else { - ret = pinctrl_select_state_locked(pctldev->p, s); - if (ret) { + if (pinctrl_select_state_locked(pctldev->p, s)) dev_err(dev, "failed to select default state\n"); - } } } -- cgit v1.2.3 From 5d589b092ab212bbcc27828167e1c036e7fc77d2 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 23 May 2012 21:22:40 +0800 Subject: pinctrl: remove pinctrl_remove_gpio_range The gpio ranges will be automatically removed when the pinctrl driver is unregistered. Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 19 +++++-------------- drivers/pinctrl/pinctrl-tegra.c | 1 - drivers/pinctrl/pinctrl-u300.c | 2 -- 3 files changed, 5 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 50d9c289cffd..902428dfb37e 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -332,20 +332,6 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); -/** - * pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller - * @pctldev: pin controller device to remove the range from - * @range: the GPIO range to remove - */ -void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range) -{ - mutex_lock(&pinctrl_mutex); - list_del(&range->node); - mutex_unlock(&pinctrl_mutex); -} -EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range); - /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group @@ -1480,6 +1466,7 @@ EXPORT_SYMBOL_GPL(pinctrl_register); */ void pinctrl_unregister(struct pinctrl_dev *pctldev) { + struct pinctrl_gpio_range *range, *n; if (pctldev == NULL) return; @@ -1495,6 +1482,10 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) /* Destroy descriptor tree */ pinctrl_free_pindescs(pctldev, pctldev->desc->pins, pctldev->desc->npins); + /* remove gpio ranges map */ + list_for_each_entry_safe(range, n, &pctldev->gpio_ranges, node) + list_del(&range->node); + kfree(pctldev); mutex_unlock(&pinctrl_mutex); diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index b6934867d8d3..07228b17a370 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -764,7 +764,6 @@ int __devexit tegra_pinctrl_remove(struct platform_device *pdev) { struct tegra_pmx *pmx = platform_get_drvdata(pdev); - pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); pinctrl_unregister(pmx->pctl); return 0; diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 6cd697a079f9..5f43f9ae36d3 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1175,8 +1175,6 @@ static int __devexit u300_pmx_remove(struct platform_device *pdev) struct u300_pmx *upmx = platform_get_drvdata(pdev); int i; - for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) - pinctrl_remove_gpio_range(upmx->pctl, &u300_gpio_ranges[i]); pinctrl_unregister(upmx->pctl); iounmap(upmx->virtbase); release_mem_region(upmx->phybase, upmx->physize); -- cgit v1.2.3 From 3e5e00b654997aa2c3998d30f7213b9611eb23d7 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 23 May 2012 21:22:41 +0800 Subject: pinctrl: add pinctrl_add_gpio_ranges function Often GPIO ranges are added in batch, so create a special function for that. Acked-by: Stephen Warren Signed-off-by: Dong Aisheng Signed-off-by: Linus Walleij --- drivers/pinctrl/core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 902428dfb37e..fb7f3bebdc69 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -332,6 +332,17 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, } EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range); +void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *ranges, + unsigned nranges) +{ + int i; + + for (i = 0; i < nranges; i++) + pinctrl_add_gpio_range(pctldev, &ranges[i]); +} +EXPORT_SYMBOL_GPL(pinctrl_add_gpio_ranges); + /** * pinctrl_get_group_selector() - returns the group selector for a group * @pctldev: the pin controller handling the group -- cgit v1.2.3 From 3c94d1bbda491ba924e5c479e87bc2faae463253 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 18 Jun 2012 20:07:50 +0200 Subject: pinctrl: select the proper symbol The Kconfig entry for the COH901 pin controller depended on PINMUX_U300 which is stale, use PINCTRL_U300 as it is called these days. Signed-off-by: Linus Walleij --- drivers/pinctrl/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index c6e6ae0aa3b1..4c05fe8ced59 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -130,7 +130,7 @@ config PINCTRL_U300 config PINCTRL_COH901 bool "ST-Ericsson U300 COH 901 335/571 GPIO" - depends on GPIOLIB && ARCH_U300 && PINMUX_U300 + depends on GPIOLIB && ARCH_U300 && PINCTRL_U300 help Say yes here to support GPIO interface on ST-Ericsson U300. The names of the two IP block variants supported are -- cgit v1.2.3 From ebddc88e99f76ddae8c0a82e0461fc04d67d3da2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jun 2012 23:39:56 +0200 Subject: pinctrl/u300: drop unused variable The "i" variable was left after the GPIO range cleanup moved to core. Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-u300.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c index 5f43f9ae36d3..a7ad8c112d91 100644 --- a/drivers/pinctrl/pinctrl-u300.c +++ b/drivers/pinctrl/pinctrl-u300.c @@ -1173,7 +1173,6 @@ out_no_resource: static int __devexit u300_pmx_remove(struct platform_device *pdev) { struct u300_pmx *upmx = platform_get_drvdata(pdev); - int i; pinctrl_unregister(upmx->pctl); iounmap(upmx->virtbase); -- cgit v1.2.3 From 8003ae335ad29664171f178eb1a6ef89cc23f4e9 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Mon, 18 Jun 2012 23:16:04 +0530 Subject: pinctrl/pinctrl-spear: remove IS_ERR checking of pmx->pctl pinctrl_register returns a pointer of struct type struct pinctrl_dev, if successfully registered to pinctrl subsystem, otherwise returns NULL, and there wont' be any pointers which are not dereferencible. They are not type of pointer addresses but are kind of error codes rather actual addresses, but are a kind of return codes of functions returning integer types. return -ENODEV if device registration fails. Signed-off-by: Devendra Naga Signed-off-by: Linus Walleij --- drivers/pinctrl/spear/pinctrl-spear.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/spear/pinctrl-spear.c b/drivers/pinctrl/spear/pinctrl-spear.c index b3f6b2873fdd..5d4f44f462f0 100644 --- a/drivers/pinctrl/spear/pinctrl-spear.c +++ b/drivers/pinctrl/spear/pinctrl-spear.c @@ -336,9 +336,9 @@ int __devinit spear_pinctrl_probe(struct platform_device *pdev, spear_pinctrl_desc.npins = machdata->npins; pmx->pctl = pinctrl_register(&spear_pinctrl_desc, &pdev->dev, pmx); - if (IS_ERR(pmx->pctl)) { + if (!pmx->pctl) { dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); - return PTR_ERR(pmx->pctl); + return -ENODEV; } return 0; -- cgit v1.2.3 From cb0f7d35e385fc242c76bfe7f1d68c1904c6f965 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Mon, 18 Jun 2012 23:14:31 +0530 Subject: pinctrl/pinctrl-tegra: remove IS_ERR checking of pmx->pctl pinctrl_register returns a pointer of struct type struct pinctrl_dev, if successfully registered to pinctrl subsystem, otherwise returns NULL, and there wont' be any pointers which are not dereferencible. They are not type of pointer addresses but are kind of error codes rather actual addresses, but are a kind of return codes of functions returning integer types. return -ENODEV if device registration fails. Acked-by: Stephen Warren Signed-off-by: Devendra Naga Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-tegra.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index 07228b17a370..ae52e4e5d098 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -745,9 +745,9 @@ int __devinit tegra_pinctrl_probe(struct platform_device *pdev, } pmx->pctl = pinctrl_register(&tegra_pinctrl_desc, &pdev->dev, pmx); - if (IS_ERR(pmx->pctl)) { + if (!pmx->pctl) { dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); - return PTR_ERR(pmx->pctl); + return -ENODEV; } pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); -- cgit v1.2.3 From 27e8461cd5906921f6faac5bcf7a6753600062d5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jun 2012 23:36:15 +0200 Subject: pinctrl/coh901: use clk_prepare_[en|dis]able() The COH901 GPIO driver did not prepare or unprepare its clock, so let us fix it to do so. Acked-by: Pankaj Jangra Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-coh901.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index 55697a5d7482..cc0f00d73d15 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -770,7 +770,7 @@ static int __init u300_gpio_probe(struct platform_device *pdev) dev_err(gpio->dev, "could not get GPIO clock\n"); goto err_no_clk; } - err = clk_enable(gpio->clk); + err = clk_prepare_enable(gpio->clk); if (err) { dev_err(gpio->dev, "could not enable GPIO clock\n"); goto err_no_clk_enable; @@ -912,7 +912,7 @@ err_no_ioremap: release_mem_region(gpio->memres->start, resource_size(gpio->memres)); err_no_ioregion: err_no_resource: - clk_disable(gpio->clk); + clk_disable_unprepare(gpio->clk); err_no_clk_enable: clk_put(gpio->clk); err_no_clk: @@ -943,7 +943,7 @@ static int __exit u300_gpio_remove(struct platform_device *pdev) iounmap(gpio->base); release_mem_region(gpio->memres->start, resource_size(gpio->memres)); - clk_disable(gpio->clk); + clk_disable_unprepare(gpio->clk); clk_put(gpio->clk); platform_set_drvdata(pdev, NULL); kfree(gpio); -- cgit v1.2.3 From 18071610677fc491a226fa6d5471c89a26ce670d Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Wed, 20 Jun 2012 18:13:47 +0800 Subject: pinctrl: pinctrl-imx: fix map setting problem if NO_PAD_CTL is set new_map is allocated according to map_num instead of grp->npins, if a pin or some pins of a group has NO_PAD_CTL property, the map_num and the grp->npin are different definitely. When we set mapping information to the new_map[], we should skip those pins with NO_PAD_CTL from index, otherwise it is possible the driver will aceesss to a unallocated region. Acked-by: Dong Aisheng Cc: Shawn Guo Signed-off-by: Hui Wang Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-imx.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-imx.c b/drivers/pinctrl/pinctrl-imx.c index dd6d93aa5334..2bafb5d9c712 100644 --- a/drivers/pinctrl/pinctrl-imx.c +++ b/drivers/pinctrl/pinctrl-imx.c @@ -146,7 +146,7 @@ static int imx_dt_node_to_map(struct pinctrl_dev *pctldev, struct pinctrl_map *new_map; struct device_node *parent; int map_num = 1; - int i; + int i, j; /* * first find the group of this node and check if we need create @@ -184,13 +184,14 @@ static int imx_dt_node_to_map(struct pinctrl_dev *pctldev, /* create config map */ new_map++; - for (i = 0; i < grp->npins; i++) { + for (i = j = 0; i < grp->npins; i++) { if (!(grp->configs[i] & IMX_NO_PAD_CTL)) { - new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; - new_map[i].data.configs.group_or_pin = + new_map[j].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[j].data.configs.group_or_pin = pin_get_name(pctldev, grp->pins[i]); - new_map[i].data.configs.configs = &grp->configs[i]; - new_map[i].data.configs.num_configs = 1; + new_map[j].data.configs.configs = &grp->configs[i]; + new_map[j].data.configs.num_configs = 1; + j++; } } -- cgit v1.2.3 From 7beea7f3bfd803b09f7d83ff0bac1d6ceb634e84 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 29 Jun 2012 10:54:49 +0200 Subject: pinctrl/nomadik: fix hsir_a_1_pins pin list move DB8500_PIN_AJ11(HSIR_RDY0_a) from hsit_a_1_pins to hsir_a_1_pins Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik-db8500.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c index 8b2022276f71..4f2a82c66300 100644 --- a/drivers/pinctrl/pinctrl-nomadik-db8500.c +++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c @@ -467,9 +467,10 @@ static const unsigned mc1_a_1_pins[] = { DB8500_PIN_AH16, DB8500_PIN_AG15, DB8500_PIN_AH15 }; static const unsigned mc1dir_a_1_pins[] = { DB8500_PIN_AH13, DB8500_PIN_AG12, DB8500_PIN_AH12, DB8500_PIN_AH11 }; -static const unsigned hsir_a_1_pins[] = { DB8500_PIN_AG10, DB8500_PIN_AH10 }; -static const unsigned hsit_a_1_pins[] = { DB8500_PIN_AJ11, DB8500_PIN_AJ9, - DB8500_PIN_AH9, DB8500_PIN_AG9, DB8500_PIN_AG8, DB8500_PIN_AF8 }; +static const unsigned hsir_a_1_pins[] = { DB8500_PIN_AG10, DB8500_PIN_AH10, + DB8500_PIN_AJ11 }; +static const unsigned hsit_a_1_pins[] = { DB8500_PIN_AJ9, DB8500_PIN_AH9, + DB8500_PIN_AG9, DB8500_PIN_AG8, DB8500_PIN_AF8 }; static const unsigned clkout_a_1_pins[] = { DB8500_PIN_AH7, DB8500_PIN_AJ6 }; static const unsigned clkout_a_2_pins[] = { DB8500_PIN_AG7, DB8500_PIN_AF7 }; static const unsigned usb_a_1_pins[] = { DB8500_PIN_AF28, DB8500_PIN_AE29, -- cgit v1.2.3 From a3b0105765f0fd247077b04061060670724f68ec Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 29 Jun 2012 11:03:03 +0200 Subject: pinctrl/nomadik: add pin group smcs1 and smps0 Add smcs1_b_1_pins as DB8500_PIN_B14 can be SM_CS1 in alt B Add smps0_c_1_pins as DB8500_PIN_E8 can be SM_PS0 in alt C Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik-db8500.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c index 4f2a82c66300..9e7f379b84d8 100644 --- a/drivers/pinctrl/pinctrl-nomadik-db8500.c +++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c @@ -509,9 +509,11 @@ static const unsigned sm_b_1_pins[] = { DB8500_PIN_C6, DB8500_PIN_B3, DB8500_PIN_D9, DB8500_PIN_A5, DB8500_PIN_B4, DB8500_PIN_C8, DB8500_PIN_A12, DB8500_PIN_C10, DB8500_PIN_B10, DB8500_PIN_B9, DB8500_PIN_A9, DB8500_PIN_C7, DB8500_PIN_A7, DB8500_PIN_C5, - DB8500_PIN_C9, DB8500_PIN_B14 }; -/* This chip select pin can be "ps0" in alt B so have it separately */ + DB8500_PIN_C9 }; +/* This chip select pin can be "ps0" in alt C so have it separately */ static const unsigned smcs0_b_1_pins[] = { DB8500_PIN_E8 }; +/* This chip select pin can be "ps1" in alt C so have it separately */ +static const unsigned smcs1_b_1_pins[] = { DB8500_PIN_B14 }; static const unsigned ipgpio7_b_1_pins[] = { DB8500_PIN_B11 }; static const unsigned ipgpio2_b_1_pins[] = { DB8500_PIN_C12 }; static const unsigned ipgpio3_b_1_pins[] = { DB8500_PIN_C11 }; @@ -573,6 +575,7 @@ static const unsigned mc2rstn_c_1_pins[] = { DB8500_PIN_C8 }; static const unsigned kp_c_1_pins[] = { DB8500_PIN_C9, DB8500_PIN_B11, DB8500_PIN_C12, DB8500_PIN_C11, DB8500_PIN_D17, DB8500_PIN_D16, DB8500_PIN_C23, DB8500_PIN_D23 }; +static const unsigned smps0_c_1_pins[] = { DB8500_PIN_E8 }; static const unsigned smps1_c_1_pins[] = { DB8500_PIN_B14 }; static const unsigned u2rxtx_c_3_pins[] = { DB8500_PIN_B17, DB8500_PIN_C16 }; static const unsigned stmape_c_2_pins[] = { DB8500_PIN_C19, DB8500_PIN_C17, @@ -654,6 +657,7 @@ static const struct nmk_pingroup nmk_db8500_groups[] = { DB8500_PIN_GROUP(kp_b_1, NMK_GPIO_ALT_B), DB8500_PIN_GROUP(sm_b_1, NMK_GPIO_ALT_B), DB8500_PIN_GROUP(smcs0_b_1, NMK_GPIO_ALT_B), + DB8500_PIN_GROUP(smcs1_b_1, NMK_GPIO_ALT_B), DB8500_PIN_GROUP(ipgpio7_b_1, NMK_GPIO_ALT_B), DB8500_PIN_GROUP(ipgpio2_b_1, NMK_GPIO_ALT_B), DB8500_PIN_GROUP(ipgpio3_b_1, NMK_GPIO_ALT_B), @@ -694,6 +698,7 @@ static const struct nmk_pingroup nmk_db8500_groups[] = { DB8500_PIN_GROUP(mc5_c_1, NMK_GPIO_ALT_C), DB8500_PIN_GROUP(mc2rstn_c_1, NMK_GPIO_ALT_C), DB8500_PIN_GROUP(kp_c_1, NMK_GPIO_ALT_C), + DB8500_PIN_GROUP(smps0_c_1, NMK_GPIO_ALT_C), DB8500_PIN_GROUP(smps1_c_1, NMK_GPIO_ALT_C), DB8500_PIN_GROUP(u2rxtx_c_3, NMK_GPIO_ALT_C), DB8500_PIN_GROUP(stmape_c_2, NMK_GPIO_ALT_C), @@ -769,7 +774,8 @@ DB8500_FUNC_GROUPS(uartmod, "uartmodtx_b_1", "uartmodrx_b_1", "uartmodrx_b_2", DB8500_FUNC_GROUPS(stmmod, "stmmod_b_1", "stmmod_c_1"); DB8500_FUNC_GROUPS(spi3, "spi3_b_1"); /* Select between CS0 on alt B or PS1 on alt C */ -DB8500_FUNC_GROUPS(sm, "sm_b_1", "smcs0_b_1", "smcleale_c_1", "smps1_c_1"); +DB8500_FUNC_GROUPS(sm, "sm_b_1", "smcs0_b_1", "smcs1_b_1", "smcleale_c_1", + "smps0_c_1", "smps1_c_1"); DB8500_FUNC_GROUPS(lcda, "lcdaclk_b_1", "lcda_b_1"); DB8500_FUNC_GROUPS(ddrtrig, "ddrtrig_b_1"); DB8500_FUNC_GROUPS(pwl, "pwl_b_1", "pwl_b_2", "pwl_b_3", "pwl_b_4"); -- cgit v1.2.3 From f098e183f27c12997ce1d2fe52b602a8a3c6b813 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Fri, 29 Jun 2012 16:16:27 +0200 Subject: pinctrl/nomadik: add hsit_a_2 pin group On 8500 and 9540, all HSI pins are driven by HSI block except HSIT_ACWAKE0_a which is driven as GPIO due to specific constraints. A new group hsit_a_2 is created which exclude HSIT_ACWAKE0_a pin. Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik-db8500.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c index 9e7f379b84d8..42c4b778596d 100644 --- a/drivers/pinctrl/pinctrl-nomadik-db8500.c +++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c @@ -471,6 +471,8 @@ static const unsigned hsir_a_1_pins[] = { DB8500_PIN_AG10, DB8500_PIN_AH10, DB8500_PIN_AJ11 }; static const unsigned hsit_a_1_pins[] = { DB8500_PIN_AJ9, DB8500_PIN_AH9, DB8500_PIN_AG9, DB8500_PIN_AG8, DB8500_PIN_AF8 }; +static const unsigned hsit_a_2_pins[] = { DB8500_PIN_AJ9, DB8500_PIN_AH9, + DB8500_PIN_AG9, DB8500_PIN_AG8 }; static const unsigned clkout_a_1_pins[] = { DB8500_PIN_AH7, DB8500_PIN_AJ6 }; static const unsigned clkout_a_2_pins[] = { DB8500_PIN_AG7, DB8500_PIN_AF7 }; static const unsigned usb_a_1_pins[] = { DB8500_PIN_AF28, DB8500_PIN_AE29, @@ -635,6 +637,7 @@ static const struct nmk_pingroup nmk_db8500_groups[] = { DB8500_PIN_GROUP(mc1_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(hsir_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(hsit_a_1, NMK_GPIO_ALT_A), + DB8500_PIN_GROUP(hsit_a_2, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(clkout_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(clkout_a_2, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(usb_a_1, NMK_GPIO_ALT_A), @@ -758,7 +761,7 @@ DB8500_FUNC_GROUPS(ipgpio, "ipgpio0_a_1", "ipgpio1_a_1", "ipgpio7_b_1", DB8500_FUNC_GROUPS(msp2, "msp2sck_a_1", "msp2_a_1"); DB8500_FUNC_GROUPS(mc4, "mc4_a_1", "mc4rstn_c_1"); DB8500_FUNC_GROUPS(mc1, "mc1_a_1", "mc1dir_a_1"); -DB8500_FUNC_GROUPS(hsi, "hsir1_a_1", "hsit1_a_1"); +DB8500_FUNC_GROUPS(hsi, "hsir1_a_1", "hsit1_a_1", "hsit_a_2"); DB8500_FUNC_GROUPS(clkout, "clkout_a_1", "clkout_a_2", "clkout_c_1"); DB8500_FUNC_GROUPS(usb, "usb_a_1"); DB8500_FUNC_GROUPS(trig, "trig_b_1"); -- cgit v1.2.3 From fcd217edc056e505e9ab4d0ca602069acc0e1ff4 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Tue, 3 Jul 2012 18:26:52 +0200 Subject: pinctrl/nomadik: add pin group to mco function mc0_dat47_a_1 and mc0dat31dir_a_1 groups are missing for mco function Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik-db8500.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c index 42c4b778596d..98174936cb5c 100644 --- a/drivers/pinctrl/pinctrl-nomadik-db8500.c +++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c @@ -616,6 +616,8 @@ static const struct nmk_pingroup nmk_db8500_groups[] = { DB8500_PIN_GROUP(msp0tfstck_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(msp0rfsrck_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(mc0_a_1, NMK_GPIO_ALT_A), + DB8500_PIN_GROUP(mc0_dat47_a_1, NMK_GPIO_ALT_A), + DB8500_PIN_GROUP(mc0dat31dir_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(msp1txrx_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(msp1_a_1, NMK_GPIO_ALT_A), DB8500_PIN_GROUP(lcdb_a_1, NMK_GPIO_ALT_A), @@ -740,7 +742,7 @@ DB8500_FUNC_GROUPS(ipi2c, "ipi2c_a_1", "ipi2c_a_2"); */ DB8500_FUNC_GROUPS(msp0, "msp0txrx_a_1", "msp0tfstck_a_1", "msp0rfstck_a_1", "msp0txrx_b_1", "msp0sck_b_1"); -DB8500_FUNC_GROUPS(mc0, "mc0_a_1"); +DB8500_FUNC_GROUPS(mc0, "mc0_a_1", "mc0_dat47_a_1", "mc0dat31dir_a_1"); /* MSP0 can swap RX/TX like MSP0 but has no SCK pin available */ DB8500_FUNC_GROUPS(msp1, "msp1txrx_a_1", "msp1_a_1", "msp1txrx_b_1"); DB8500_FUNC_GROUPS(lcdb, "lcdb_a_1"); -- cgit v1.2.3 From dab058fd5ff834cb3b9de1d930ce731a605eb0c6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 3 Jul 2012 15:51:22 -0700 Subject: floppy: cancel any pending fd_timeouts before adding a new one In commit 070ad7e793dc ("floppy: convert to delayed work and single-thread wq") the 'fd_timeout' timer was converted to a delayed work. However, the "del_timer(&fd_timeout)" was lost in the process, and any previous pending timeouts would stay active when we then re-queued the timeout. This resulted in the floppy probe sequence having a (stale) 20s timeout rather than the intended 3s timeout, and thus made booting with the floppy driver (but no actual floppy controller) take much longer than it should. Of course, there's little reason for most people to compile the floppy driver into the kernel at all, which is why most people never noticed. Canceling the delayed work where we used to do the del_timer() fixes the issue, and makes the floppy probing use the proper new timeout instead. The three second timeout is still very wasteful, but better than the 20s one. Reported-and-tested-by: Andi Kleen Reported-and-tested-by: Calvin Walton Cc: Jiri Kosina Signed-off-by: Linus Torvalds --- drivers/block/floppy.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index cce7df367b79..553f43a90953 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -671,6 +671,7 @@ static void __reschedule_timeout(int drive, const char *message) if (drive == current_reqD) drive = current_drive; + __cancel_delayed_work(&fd_timeout); if (drive < 0 || drive >= N_DRIVE) { delay = 20UL * HZ; -- cgit v1.2.3 From 10684112c9d154172ac34e48a2ab68649e8f63ac Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Wed, 4 Jul 2012 09:35:35 +1000 Subject: md/raid10: fix careless build error build error introduced by commit b357f04a67c2aeee8 That function doesn't get extra args until a later patch. Bother. Reported-by: Fengguang Wu Reported-by: Simon Kirby Reported-by: Tobias Klausmann Signed-off-by: NeilBrown --- drivers/md/raid10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index acf5a828c7e1..8da6282254c3 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1394,7 +1394,7 @@ retry_write: bio_list_add(&conf->pending_bio_list, mbio); conf->pending_count++; spin_unlock_irqrestore(&conf->device_lock, flags); - if (!mddev_check_plugged(mddev, 0, 0)) + if (!mddev_check_plugged(mddev)) md_wakeup_thread(mddev->thread); if (!r10_bio->devs[i].repl_bio) -- cgit v1.2.3 From d50394266b340d930a7458fa669d36e99670f200 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sun, 1 Jul 2012 11:31:35 +0300 Subject: remoteproc/omap: fix randconfig unmet direct dependencies OMAP_REMOTEPROC selects REMOTEPROC and RPMSG, both of which depend on EXPERIMENTAL, so let's have OMAP_REMOTEPROC depend on EXPERIMENTAL too, in order to avoid the below randconfig warnings. warning: (OMAP_REMOTEPROC) selects REMOTEPROC which has unmet direct dependencies (EXPERIMENTAL) warning: (OMAP_REMOTEPROC) selects RPMSG which has unmet direct dependencies (EXPERIMENTAL) Cc: stable Reported-by: Tony Lindgren Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 24d880e78ec6..db4e39b9164a 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -7,6 +7,7 @@ config REMOTEPROC config OMAP_REMOTEPROC tristate "OMAP remoteproc support" + depends on EXPERIMENTAL depends on ARCH_OMAP4 depends on OMAP_IOMMU select REMOTEPROC -- cgit v1.2.3 From fb31fbeb647ed1910290712c13f11f1db09e5506 Mon Sep 17 00:00:00 2001 From: Alexander Holler Date: Tue, 3 Jul 2012 14:35:47 +0800 Subject: leds: heartbeat: fix bug on panic With commit 49dca5aebfdeadd4bf27b6cb4c60392147dc35a4 I introduced a bug (visible if CONFIG_PROVE_RCU is enabled) which occures when a panic has happened: [ 1526.520230] =============================== [ 1526.520230] [ INFO: suspicious RCU usage. ] [ 1526.520230] 3.5.0-rc1+ #12 Not tainted [ 1526.520230] ------------------------------- [ 1526.520230] /c/kernel-tests/mm/include/linux/rcupdate.h:436 Illegal context switch in RCU read-side critical section! [ 1526.520230] [ 1526.520230] other info that might help us debug this: [ 1526.520230] [ 1526.520230] [ 1526.520230] rcu_scheduler_active = 1, debug_locks = 0 [ 1526.520230] 3 locks held by net.agent/3279: [ 1526.520230] #0: (&mm->mmap_sem){++++++}, at: [] do_page_fault+0x193/0x390 [ 1526.520230] #1: (panic_lock){+.+...}, at: [] panic+0x37/0x1d3 [ 1526.520230] #2: (rcu_read_lock){.+.+..}, at: [] rcu_lock_acquire+0x0/0x29 [ 1526.520230] [ 1526.520230] stack backtrace: [ 1526.520230] Pid: 3279, comm: net.agent Not tainted 3.5.0-rc1+ #12 [ 1526.520230] Call Trace: [ 1526.520230] [] lockdep_rcu_suspicious+0x109/0x112 [ 1526.520230] [] rcu_preempt_sleep_check+0x45/0x47 [ 1526.520230] [] __might_sleep+0x1e/0x19a [ 1526.520230] [] down_write+0x26/0x81 [ 1526.520230] [] led_trigger_unregister+0x1f/0x9c [ 1526.520230] [] heartbeat_reboot_notifier+0x15/0x19 [ 1526.520230] [] notifier_call_chain+0x96/0xcd [ 1526.520230] [] __atomic_notifier_call_chain+0x8e/0xff [ 1526.520230] [] ? kmsg_dump+0x37/0x1eb [ 1526.520230] [] atomic_notifier_call_chain+0x14/0x16 [ 1526.520230] [] panic+0xe8/0x1d3 [ 1526.520230] [] out_of_memory+0x15d/0x1d3 So in case of a panic, now just turn of the LED. Other approaches like scheduling a work to unregister the trigger aren't working because there isn't much which still runs after a panic occured (except timers). Signed-off-by: Alexander Holler Signed-off-by: Bryan Wu --- drivers/leds/ledtrig-heartbeat.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/leds/ledtrig-heartbeat.c b/drivers/leds/ledtrig-heartbeat.c index 41dc76db4311..a019fbb70880 100644 --- a/drivers/leds/ledtrig-heartbeat.c +++ b/drivers/leds/ledtrig-heartbeat.c @@ -21,6 +21,8 @@ #include #include "leds.h" +static int panic_heartbeats; + struct heartbeat_trig_data { unsigned int phase; unsigned int period; @@ -34,6 +36,11 @@ static void led_heartbeat_function(unsigned long data) unsigned long brightness = LED_OFF; unsigned long delay = 0; + if (unlikely(panic_heartbeats)) { + led_set_brightness(led_cdev, LED_OFF); + return; + } + /* acts like an actual heart beat -- ie thump-thump-pause... */ switch (heartbeat_data->phase) { case 0: @@ -111,12 +118,19 @@ static int heartbeat_reboot_notifier(struct notifier_block *nb, return NOTIFY_DONE; } +static int heartbeat_panic_notifier(struct notifier_block *nb, + unsigned long code, void *unused) +{ + panic_heartbeats = 1; + return NOTIFY_DONE; +} + static struct notifier_block heartbeat_reboot_nb = { .notifier_call = heartbeat_reboot_notifier, }; static struct notifier_block heartbeat_panic_nb = { - .notifier_call = heartbeat_reboot_notifier, + .notifier_call = heartbeat_panic_notifier, }; static int __init heartbeat_trig_init(void) -- cgit v1.2.3 From e121aefa7d9f10eee5cf26ed47129237a05d940b Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sun, 1 Jul 2012 11:53:36 +0300 Subject: remoteproc: fix missing CONFIG_FW_LOADER configurations Remoteproc requires user space firmware loading support, so let's select FW_LOADER explicitly to avoid painful misconfigurations (which only show up in runtime). Cc: stable Reported-by: Mark Grosen Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index db4e39b9164a..f8d818abf98c 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -4,6 +4,7 @@ menu "Remoteproc drivers (EXPERIMENTAL)" config REMOTEPROC tristate depends on EXPERIMENTAL + select FW_CONFIG config OMAP_REMOTEPROC tristate "OMAP remoteproc support" -- cgit v1.2.3 From 5a081caa0414b9bbb82c17ffab9d6fe66edbb72f Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Wed, 6 Jun 2012 10:09:25 +0300 Subject: rpmsg: avoid premature deallocation of endpoints When an inbound message arrives, the rpmsg core looks up its associated endpoint and invokes the registered callback. If a message arrives while its endpoint is being removed (because the rpmsg driver was removed, or a recovery of a remote processor has kicked in) we must ensure atomicity, i.e.: - Either the ept is removed before it is found or - The ept is found but will not be freed until the callback returns This is achieved by maintaining a per-ept reference count, which, when drops to zero, will trigger deallocation of the ept. With this in hand, it is now forbidden to directly deallocate epts once they have been added to the endpoints idr. Cc: stable Reported-by: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/virtio_rpmsg_bus.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 75506ec2840e..9623327ba509 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -188,6 +188,26 @@ static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) rpdev->id.name); } +/** + * __ept_release() - deallocate an rpmsg endpoint + * @kref: the ept's reference count + * + * This function deallocates an ept, and is invoked when its @kref refcount + * drops to zero. + * + * Never invoke this function directly! + */ +static void __ept_release(struct kref *kref) +{ + struct rpmsg_endpoint *ept = container_of(kref, struct rpmsg_endpoint, + refcount); + /* + * At this point no one holds a reference to ept anymore, + * so we can directly free it + */ + kfree(ept); +} + /* for more info, see below documentation of rpmsg_create_ept() */ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, @@ -206,6 +226,8 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, return NULL; } + kref_init(&ept->refcount); + ept->rpdev = rpdev; ept->cb = cb; ept->priv = priv; @@ -238,7 +260,7 @@ rem_idr: idr_remove(&vrp->endpoints, request); free_ept: mutex_unlock(&vrp->endpoints_lock); - kfree(ept); + kref_put(&ept->refcount, __ept_release); return NULL; } @@ -306,7 +328,7 @@ __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) idr_remove(&vrp->endpoints, ept->addr); mutex_unlock(&vrp->endpoints_lock); - kfree(ept); + kref_put(&ept->refcount, __ept_release); } /** @@ -790,7 +812,13 @@ static void rpmsg_recv_done(struct virtqueue *rvq) /* use the dst addr to fetch the callback of the appropriate user */ mutex_lock(&vrp->endpoints_lock); + ept = idr_find(&vrp->endpoints, msg->dst); + + /* let's make sure no one deallocates ept while we use it */ + if (ept) + kref_get(&ept->refcount); + mutex_unlock(&vrp->endpoints_lock); if (ept && ept->cb) @@ -798,6 +826,10 @@ static void rpmsg_recv_done(struct virtqueue *rvq) else dev_warn(dev, "msg received with no recepient\n"); + /* farewell, ept, we don't need you anymore */ + if (ept) + kref_put(&ept->refcount, __ept_release); + /* publish the real size of the buffer */ sg_init_one(&sg, msg, RPMSG_BUF_SIZE); -- cgit v1.2.3 From 15fd943af50dbc5f7f4de33835795c72595f7bf4 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 7 Jun 2012 15:39:35 +0300 Subject: rpmsg: make sure inflight messages don't invoke just-removed callbacks When inbound messages arrive, rpmsg core looks up their associated endpoint (by destination address) and then invokes their callback. We've made sure that endpoints will never be de-allocated after they were found by rpmsg core, but we also need to protect against the (rare) scenario where the rpmsg driver was just removed, and its callback function isn't available anymore. This is achieved by introducing a callback mutex, which must be taken before the callback is invoked, and, obviously, before it is removed. Cc: stable Reported-by: Fernando Guzman Lugo Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/virtio_rpmsg_bus.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 9623327ba509..39d3aa41adda 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -227,6 +227,7 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, } kref_init(&ept->refcount); + mutex_init(&ept->cb_lock); ept->rpdev = rpdev; ept->cb = cb; @@ -324,10 +325,16 @@ EXPORT_SYMBOL(rpmsg_create_ept); static void __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept) { + /* make sure new inbound messages can't find this ept anymore */ mutex_lock(&vrp->endpoints_lock); idr_remove(&vrp->endpoints, ept->addr); mutex_unlock(&vrp->endpoints_lock); + /* make sure in-flight inbound messages won't invoke cb anymore */ + mutex_lock(&ept->cb_lock); + ept->cb = NULL; + mutex_unlock(&ept->cb_lock); + kref_put(&ept->refcount, __ept_release); } @@ -821,14 +828,20 @@ static void rpmsg_recv_done(struct virtqueue *rvq) mutex_unlock(&vrp->endpoints_lock); - if (ept && ept->cb) - ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src); - else - dev_warn(dev, "msg received with no recepient\n"); + if (ept) { + /* make sure ept->cb doesn't go away while we use it */ + mutex_lock(&ept->cb_lock); - /* farewell, ept, we don't need you anymore */ - if (ept) + if (ept->cb) + ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, + msg->src); + + mutex_unlock(&ept->cb_lock); + + /* farewell, ept, we don't need you anymore */ kref_put(&ept->refcount, __ept_release); + } else + dev_warn(dev, "msg received with no recepient\n"); /* publish the real size of the buffer */ sg_init_one(&sg, msg, RPMSG_BUF_SIZE); -- cgit v1.2.3 From 5e754f330d189d9082017525668220e458757e5b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 3 Jul 2012 23:05:14 +0200 Subject: pinctrl/nomadik: use devm_* allocators for gpio probe The GPIO portions of the Nomadik pinctrl driver was not using the managed devm_* interfaces, lets' use them consequently and cut down the error path and special case handling. Acked-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik.c | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 3e7e47d6b385..095bbc6450cb 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -1194,7 +1194,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) } if (np) { - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(&dev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -1229,29 +1229,23 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) goto out; } - if (request_mem_region(res->start, resource_size(res), - dev_name(&dev->dev)) == NULL) { - ret = -EBUSY; - goto out; - } - - base = ioremap(res->start, resource_size(res)); + base = devm_request_and_ioremap(&dev->dev, res); if (!base) { ret = -ENOMEM; - goto out_release; + goto out; } - clk = clk_get(&dev->dev, NULL); + clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(clk)) { ret = PTR_ERR(clk); - goto out_unmap; + goto out; } clk_prepare(clk); - nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL); + nmk_chip = devm_kzalloc(&dev->dev, sizeof(*nmk_chip), GFP_KERNEL); if (!nmk_chip) { ret = -ENOMEM; - goto out_clk; + goto out; } /* @@ -1286,7 +1280,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) ret = gpiochip_add(&nmk_chip->chip); if (ret) - goto out_free; + goto out; BUG_ON(nmk_chip->bank >= ARRAY_SIZE(nmk_gpio_chips)); @@ -1300,7 +1294,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) if (!nmk_chip->domain) { pr_err("%s: Failed to create irqdomain\n", np->full_name); ret = -ENOSYS; - goto out_free; + goto out; } nmk_gpio_init_irq(nmk_chip); @@ -1309,20 +1303,9 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) return 0; -out_free: - kfree(nmk_chip); -out_clk: - clk_disable(clk); - clk_put(clk); -out_unmap: - iounmap(base); -out_release: - release_mem_region(res->start, resource_size(res)); out: dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret, pdata->first_gpio, pdata->first_gpio+31); - if (np) - kfree(pdata); return ret; } -- cgit v1.2.3 From 50bcd47c31372b09a39da7288bf80450805b2ca6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 4 Jul 2012 11:25:36 +0200 Subject: pinctrl/nomadik: kerneldoc fix Document a missing function parameter to nmk_config_pin(). Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 095bbc6450cb..e2a2dd1990c1 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -434,7 +434,7 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) /** * nmk_config_pin - configure a pin's mux attributes * @cfg: pin confguration - * + * @sleep: Non-zero to apply the sleep mode configuration * Configures a pin's mode (alternate function or GPIO), its pull up status, * and its sleep mode based on the specified configuration. The @cfg is * usually one of the SoC specific macros defined in mach/-pins.h. These -- cgit v1.2.3 From 94f48ab32b5a875fcf2f62e72de8f666136d98ba Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Jul 2012 09:59:17 +0800 Subject: regulator: tps65910: Set enable enable_time in regulator_desc Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 66da87cc0879..a534e0872006 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -412,13 +412,6 @@ static int tps65911_get_ctrl_register(int id) } } -static int tps65910_enable_time(struct regulator_dev *dev) -{ - struct tps65910_reg *pmic = rdev_get_drvdata(dev); - int id = rdev_get_id(dev); - return pmic->info[id]->enable_time_us; -} - static int tps65910_set_mode(struct regulator_dev *dev, unsigned int mode) { struct tps65910_reg *pmic = rdev_get_drvdata(dev); @@ -762,7 +755,6 @@ static struct regulator_ops tps65910_ops_dcdc = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage_sel = tps65910_get_voltage_dcdc_sel, @@ -775,7 +767,6 @@ static struct regulator_ops tps65910_ops_vdd3 = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage = tps65910_get_voltage_vdd3, @@ -786,7 +777,6 @@ static struct regulator_ops tps65910_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage_sel = tps65910_get_voltage_sel, @@ -798,7 +788,6 @@ static struct regulator_ops tps65911_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .enable_time = tps65910_enable_time, .set_mode = tps65910_set_mode, .get_mode = tps65910_get_mode, .get_voltage_sel = tps65911_get_voltage_sel, @@ -1139,6 +1128,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->desc[i].name = info->name; pmic->desc[i].id = i; pmic->desc[i].n_voltages = info->n_voltages; + pmic->desc[i].enable_time = info->enable_time_us; if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) { pmic->desc[i].ops = &tps65910_ops_dcdc; -- cgit v1.2.3 From a2a8222be8385818ade54554a50f7904ed0e506f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Jul 2012 10:19:46 +0800 Subject: regulator: gpio-regulator: Set enable enable_time in regulator_desc Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 242851a4c1a6..3ed8d158833b 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -39,7 +39,6 @@ struct gpio_regulator_data { int enable_gpio; bool enable_high; bool is_enabled; - unsigned startup_delay; struct gpio *gpios; int nr_gpios; @@ -81,13 +80,6 @@ static int gpio_regulator_disable(struct regulator_dev *dev) return 0; } -static int gpio_regulator_enable_time(struct regulator_dev *dev) -{ - struct gpio_regulator_data *data = rdev_get_drvdata(dev); - - return data->startup_delay; -} - static int gpio_regulator_get_value(struct regulator_dev *dev) { struct gpio_regulator_data *data = rdev_get_drvdata(dev); @@ -156,7 +148,6 @@ static struct regulator_ops gpio_regulator_voltage_ops = { .is_enabled = gpio_regulator_is_enabled, .enable = gpio_regulator_enable, .disable = gpio_regulator_disable, - .enable_time = gpio_regulator_enable_time, .get_voltage = gpio_regulator_get_value, .set_voltage = gpio_regulator_set_voltage, .list_voltage = gpio_regulator_list_voltage, @@ -166,7 +157,6 @@ static struct regulator_ops gpio_regulator_current_ops = { .is_enabled = gpio_regulator_is_enabled, .enable = gpio_regulator_enable, .disable = gpio_regulator_disable, - .enable_time = gpio_regulator_enable_time, .get_current_limit = gpio_regulator_get_value, .set_current_limit = gpio_regulator_set_current_limit, }; @@ -213,6 +203,7 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) drvdata->nr_states = config->nr_states; drvdata->desc.owner = THIS_MODULE; + drvdata->desc.enable_time = config->startup_delay; /* handle regulator type*/ switch (config->type) { @@ -233,7 +224,6 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) } drvdata->enable_gpio = config->enable_gpio; - drvdata->startup_delay = config->startup_delay; if (gpio_is_valid(config->enable_gpio)) { drvdata->enable_high = config->enable_high; -- cgit v1.2.3 From 4b7c948f558bf1d51aa25a6f621056c0acf45228 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Jul 2012 10:20:46 +0800 Subject: regulator: gpio-regulator: Use core GPIO enable support Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 103 ++++++------------------------------- 1 file changed, 16 insertions(+), 87 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 3ed8d158833b..34b67bee9323 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -36,10 +36,6 @@ struct gpio_regulator_data { struct regulator_desc desc; struct regulator_dev *dev; - int enable_gpio; - bool enable_high; - bool is_enabled; - struct gpio *gpios; int nr_gpios; @@ -49,37 +45,6 @@ struct gpio_regulator_data { int state; }; -static int gpio_regulator_is_enabled(struct regulator_dev *dev) -{ - struct gpio_regulator_data *data = rdev_get_drvdata(dev); - - return data->is_enabled; -} - -static int gpio_regulator_enable(struct regulator_dev *dev) -{ - struct gpio_regulator_data *data = rdev_get_drvdata(dev); - - if (gpio_is_valid(data->enable_gpio)) { - gpio_set_value_cansleep(data->enable_gpio, data->enable_high); - data->is_enabled = true; - } - - return 0; -} - -static int gpio_regulator_disable(struct regulator_dev *dev) -{ - struct gpio_regulator_data *data = rdev_get_drvdata(dev); - - if (gpio_is_valid(data->enable_gpio)) { - gpio_set_value_cansleep(data->enable_gpio, !data->enable_high); - data->is_enabled = false; - } - - return 0; -} - static int gpio_regulator_get_value(struct regulator_dev *dev) { struct gpio_regulator_data *data = rdev_get_drvdata(dev); @@ -145,18 +110,12 @@ static int gpio_regulator_set_current_limit(struct regulator_dev *dev, } static struct regulator_ops gpio_regulator_voltage_ops = { - .is_enabled = gpio_regulator_is_enabled, - .enable = gpio_regulator_enable, - .disable = gpio_regulator_disable, .get_voltage = gpio_regulator_get_value, .set_voltage = gpio_regulator_set_voltage, .list_voltage = gpio_regulator_list_voltage, }; static struct regulator_ops gpio_regulator_current_ops = { - .is_enabled = gpio_regulator_is_enabled, - .enable = gpio_regulator_enable, - .disable = gpio_regulator_disable, .get_current_limit = gpio_regulator_get_value, .set_current_limit = gpio_regulator_set_current_limit, }; @@ -223,51 +182,12 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) break; } - drvdata->enable_gpio = config->enable_gpio; - - if (gpio_is_valid(config->enable_gpio)) { - drvdata->enable_high = config->enable_high; - - ret = gpio_request(config->enable_gpio, config->supply_name); - if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator enable GPIO %d: %d\n", - config->enable_gpio, ret); - goto err_memstate; - } - - /* set output direction without changing state - * to prevent glitch - */ - if (config->enabled_at_boot) { - drvdata->is_enabled = true; - ret = gpio_direction_output(config->enable_gpio, - config->enable_high); - } else { - drvdata->is_enabled = false; - ret = gpio_direction_output(config->enable_gpio, - !config->enable_high); - } - - if (ret) { - dev_err(&pdev->dev, - "Could not configure regulator enable GPIO %d direction: %d\n", - config->enable_gpio, ret); - goto err_enablegpio; - } - } else { - /* Regulator without GPIO control is considered - * always enabled - */ - drvdata->is_enabled = true; - } - drvdata->nr_gpios = config->nr_gpios; ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); if (ret) { dev_err(&pdev->dev, "Could not obtain regulator setting GPIOs: %d\n", ret); - goto err_enablegpio; + goto err_memstate; } /* build initial state from gpio init data. */ @@ -282,6 +202,21 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) cfg.init_data = config->init_data; cfg.driver_data = drvdata; + if (config->enable_gpio >= 0) + cfg.ena_gpio = config->enable_gpio; + cfg.ena_gpio_invert = !config->enable_high; + if (config->enabled_at_boot) { + if (config->enable_high) + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + else + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + } else { + if (config->enable_high) + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_LOW; + else + cfg.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; + } + drvdata->dev = regulator_register(&drvdata->desc, &cfg); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); @@ -295,9 +230,6 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) err_stategpio: gpio_free_array(drvdata->gpios, drvdata->nr_gpios); -err_enablegpio: - if (gpio_is_valid(config->enable_gpio)) - gpio_free(config->enable_gpio); err_memstate: kfree(drvdata->states); err_memgpio: @@ -319,9 +251,6 @@ static int __devexit gpio_regulator_remove(struct platform_device *pdev) kfree(drvdata->states); kfree(drvdata->gpios); - if (gpio_is_valid(drvdata->enable_gpio)) - gpio_free(drvdata->enable_gpio); - kfree(drvdata->desc.name); return 0; -- cgit v1.2.3 From fca53d862dcbddaa9db017b70a64392da8c20bce Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Jul 2012 10:46:34 +0800 Subject: regulator: twl: Set enable enable_time in regulator_desc Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 31 ++++--------------------------- 1 file changed, 4 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index ca308cb8e676..8f0bd56c2118 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -43,9 +43,6 @@ struct twlreg_info { u8 table_len; const u16 *table; - /* regulator specific turn-on delay */ - u16 delay; - /* State REMAP default configuration */ u8 remap; @@ -223,20 +220,6 @@ static int twl6030reg_enable(struct regulator_dev *rdev) return ret; } -static int twl4030reg_enable_time(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - return info->delay; -} - -static int twl6030reg_enable_time(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - return info->delay; -} - static int twl4030reg_disable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -508,7 +491,6 @@ static struct regulator_ops twl4030ldo_ops = { .enable = twl4030reg_enable, .disable = twl4030reg_disable, .is_enabled = twl4030reg_is_enabled, - .enable_time = twl4030reg_enable_time, .set_mode = twl4030reg_set_mode, @@ -622,7 +604,6 @@ static struct regulator_ops twl6030ldo_ops = { .enable = twl6030reg_enable, .disable = twl6030reg_disable, .is_enabled = twl6030reg_is_enabled, - .enable_time = twl6030reg_enable_time, .set_mode = twl6030reg_set_mode, @@ -656,7 +637,6 @@ static struct regulator_ops twl4030fixed_ops = { .enable = twl4030reg_enable, .disable = twl4030reg_disable, .is_enabled = twl4030reg_is_enabled, - .enable_time = twl4030reg_enable_time, .set_mode = twl4030reg_set_mode, @@ -671,7 +651,6 @@ static struct regulator_ops twl6030fixed_ops = { .enable = twl6030reg_enable, .disable = twl6030reg_disable, .is_enabled = twl6030reg_is_enabled, - .enable_time = twl6030reg_enable_time, .set_mode = twl6030reg_set_mode, @@ -682,7 +661,6 @@ static struct regulator_ops twl6030_fixed_resource = { .enable = twl6030reg_enable, .disable = twl6030reg_disable, .is_enabled = twl6030reg_is_enabled, - .enable_time = twl6030reg_enable_time, .get_status = twl6030reg_get_status, }; @@ -879,7 +857,6 @@ static struct regulator_ops twlsmps_ops = { .enable = twl6030reg_enable, .disable = twl6030reg_disable, .is_enabled = twl6030reg_is_enabled, - .enable_time = twl6030reg_enable_time, .set_mode = twl6030reg_set_mode, @@ -902,7 +879,6 @@ static struct twlreg_info TWL4030_INFO_##label = { \ .id = num, \ .table_len = ARRAY_SIZE(label##_VSEL_table), \ .table = label##_VSEL_table, \ - .delay = turnon_delay, \ .remap = remap_conf, \ .desc = { \ .name = #label, \ @@ -911,6 +887,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \ .ops = &twl4030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .enable_time = turnon_delay, \ }, \ } @@ -918,7 +895,6 @@ static struct twlreg_info TWL4030_INFO_##label = { \ static struct twlreg_info TWL4030_INFO_##label = { \ .base = offset, \ .id = num, \ - .delay = turnon_delay, \ .remap = remap_conf, \ .desc = { \ .name = #label, \ @@ -926,6 +902,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \ .ops = &twl4030smps_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .enable_time = turnon_delay, \ }, \ } @@ -980,7 +957,6 @@ static struct twlreg_info TWLFIXED_INFO_##label = { \ .base = offset, \ .id = num, \ .min_mV = mVolts, \ - .delay = turnon_delay, \ .remap = remap_conf, \ .desc = { \ .name = #label, \ @@ -989,19 +965,20 @@ static struct twlreg_info TWLFIXED_INFO_##label = { \ .ops = &operations, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .enable_time = turnon_delay, \ }, \ } #define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) \ static struct twlreg_info TWLRES_INFO_##label = { \ .base = offset, \ - .delay = turnon_delay, \ .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ .ops = &twl6030_fixed_resource, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ + .enable_time = turnon_delay, \ }, \ } -- cgit v1.2.3 From 9c6a74c5e0ca3bfac09cb1e7bf7629cc0f3aa48c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Jul 2012 11:55:07 +0800 Subject: regulator: ad5398: Fix min/max current limit boundary checking It is ok to request current limit with min_uA < chip->min_uA and max_uA > chip->max_uA. We need to set min_uA = chip->min_uA if (min_uA < chip->min_uA), this ensures the equation to calcuate selator does not return negative number. Also set max_uA = chip->max_uA if (max_uA > chip->max_uA), as suggested by Sonic. Signed-off-by: Axel Lin Acked-by: Sonic Zhang Signed-off-by: Mark Brown --- drivers/regulator/ad5398.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ad5398.c b/drivers/regulator/ad5398.c index 46d05f38baf8..f123f7e3b752 100644 --- a/drivers/regulator/ad5398.c +++ b/drivers/regulator/ad5398.c @@ -89,9 +89,12 @@ static int ad5398_set_current_limit(struct regulator_dev *rdev, int min_uA, int unsigned short data; int ret; - if (min_uA > chip->max_uA || min_uA < chip->min_uA) - return -EINVAL; - if (max_uA > chip->max_uA || max_uA < chip->min_uA) + if (min_uA < chip->min_uA) + min_uA = chip->min_uA; + if (max_uA > chip->max_uA) + max_uA = chip->max_uA; + + if (min_uA > chip->max_uA || max_uA < chip->min_uA) return -EINVAL; selector = DIV_ROUND_UP((min_uA - chip->min_uA) * chip->current_level, -- cgit v1.2.3 From 14070ade02cc378bc30dae383532768a94805988 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 4 Jul 2012 07:45:16 +0000 Subject: ARM: at91: fix new build errors MULTI_IRQ_HANDLER and SPARSE_IRQ are now required everywhere because mach/irqs.h and mach/entry-macros.S are gone but the symbols are only selected for AT91SAM9, not for the NOMMU parts. A few files now need to include linux/io.h directly, which used to be included through other headers that have changed. The new at91_aic_irq_priorities variable is only used with CONFIG_OF enabled and should not be visible otherwise. Signed-off-by: Arnd Bergmann Acked-by: Ludovic Desroches Acked-by: Nicolas Ferre --- drivers/rtc/rtc-at91rm9200.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index dc474bc6522d..fca9790c7de7 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -27,6 +27,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 0c47935c5b5cd4916cf1c1ed4a2894807f7bcc3e Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Wed, 4 Jul 2012 10:20:31 -0300 Subject: HID: add battery quirk for Apple Wireless ANSI Add USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, to the quirk list since it report wrong feature type and wrong percentage range. Signed-off-by: Daniel Nicoletti Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 132b0019365e..5301006f6c15 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -301,6 +301,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, {} }; -- cgit v1.2.3 From 6dc027c977a0cf34828ebb1742cc58fa9adb0e2f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 4 Jul 2012 12:50:02 +0100 Subject: regulator: arizona-micsupp: Force regulated mode until we have API support It's almost certainly what the user would expect. Signed-off-by: Mark Brown --- drivers/regulator/arizona-micsupp.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index fdd7473ada4a..450a069aa9b6 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -144,6 +144,10 @@ static __devinit int arizona_micsupp_probe(struct platform_device *pdev) else config.init_data = &micsupp->init_data; + /* Default to regulated mode until the API supports bypass */ + regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1, + ARIZONA_CPMIC_BYPASS, 0); + micsupp->regulator = regulator_register(&arizona_micsupp, &config); if (IS_ERR(micsupp->regulator)) { ret = PTR_ERR(micsupp->regulator); -- cgit v1.2.3 From 6264307ed090cc41a47a1e55be713d75e81f1b27 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Wed, 4 Jul 2012 12:14:43 -0400 Subject: HID: magicmouse: Removing report_touches switch Remove the report_touches switch as it is not so useful to turn off reporting touch events for a touch device. Let the userspace to do the filtering if the turning off is needed. V2: Remove report_touches as suggeted by Chase Douglas Signed-off-by: Yufeng Shen Reviewed-and-tested-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-magicmouse.c | 95 +++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 40ac6654f1d1..fd88f216720a 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -48,10 +48,6 @@ static bool scroll_acceleration = false; module_param(scroll_acceleration, bool, 0644); MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events"); -static bool report_touches = true; -module_param(report_touches, bool, 0644); -MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)"); - static bool report_undeciphered; module_param(report_undeciphered, bool, 0644); MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); @@ -276,7 +272,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda msc->single_touch_id = SINGLE_TOUCH_UP; /* Generate the input events for this touch. */ - if (report_touches && down) { + if (down) { input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); @@ -335,7 +331,7 @@ static int magicmouse_raw_event(struct hid_device *hdev, for (ii = 0; ii < npoints; ii++) magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); - if (report_touches && msc->ntouches == 0) + if (msc->ntouches == 0) input_mt_sync(input); /* When emulating three-button mode, it is important @@ -422,53 +418,52 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); } - if (report_touches) { - __set_bit(EV_ABS, input->evbit); - - input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, - 4, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2, - 4, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); - - /* Note: Touch Y position from the device is inverted relative - * to how pointer motion is reported (and relative to how USB - * HID recommends the coordinates work). This driver keeps - * the origin at the same position, and just uses the additive - * inverse of the reported Y. - */ - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { - input_set_abs_params(input, ABS_MT_POSITION_X, - MOUSE_MIN_X, MOUSE_MAX_X, 4, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, - MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0); - - input_abs_set_res(input, ABS_MT_POSITION_X, - MOUSE_RES_X); - input_abs_set_res(input, ABS_MT_POSITION_Y, - MOUSE_RES_Y); - } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ - input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X, - TRACKPAD_MAX_X, 4, 0); - input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y, - TRACKPAD_MAX_Y, 4, 0); - input_set_abs_params(input, ABS_MT_POSITION_X, - TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, - TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0); - - input_abs_set_res(input, ABS_X, TRACKPAD_RES_X); - input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y); - input_abs_set_res(input, ABS_MT_POSITION_X, - TRACKPAD_RES_X); - input_abs_set_res(input, ABS_MT_POSITION_Y, - TRACKPAD_RES_Y); - } - input_set_events_per_packet(input, 60); + __set_bit(EV_ABS, input->evbit); + + input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, + 4, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2, + 4, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); + + /* Note: Touch Y position from the device is inverted relative + * to how pointer motion is reported (and relative to how USB + * HID recommends the coordinates work). This driver keeps + * the origin at the same position, and just uses the additive + * inverse of the reported Y. + */ + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + input_set_abs_params(input, ABS_MT_POSITION_X, + MOUSE_MIN_X, MOUSE_MAX_X, 4, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0); + + input_abs_set_res(input, ABS_MT_POSITION_X, + MOUSE_RES_X); + input_abs_set_res(input, ABS_MT_POSITION_Y, + MOUSE_RES_Y); + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X, + TRACKPAD_MAX_X, 4, 0); + input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y, + TRACKPAD_MAX_Y, 4, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, + TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0); + + input_abs_set_res(input, ABS_X, TRACKPAD_RES_X); + input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y); + input_abs_set_res(input, ABS_MT_POSITION_X, + TRACKPAD_RES_X); + input_abs_set_res(input, ABS_MT_POSITION_Y, + TRACKPAD_RES_Y); } + input_set_events_per_packet(input, 60); + if (report_undeciphered) { __set_bit(EV_MSC, input->evbit); __set_bit(MSC_RAW, input->mscbit); -- cgit v1.2.3 From a6d1bc1d5fb0f6ce817bef188a3d93255db03992 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Wed, 4 Jul 2012 12:15:43 -0400 Subject: HID: magicmouse: Implement Multi-touch Protocol B (MT-B) The driver for Apple Magic Trackpad/Mouse currently uses Multi-touch Protocol A (MT-A) to report touch events and uses ABS_MT_TRACKING_ID to do finger tracking. The fact of the device being able to track individual finger makes it possible to report touch events using MT-B. This patch converts the driver to use MT-B as it is preferred to MT-A. V4: Removed BTN_TOUCH evnet. V3: Removed the single touch related logic. V2: Converting entirely to MT-B as Henrik Rydberg suggested. Signed-off-by: Yufeng Shen Reviewed-and-tested-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/hid-magicmouse.c | 66 +++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index fd88f216720a..73647266daad 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -68,15 +69,6 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define SCROLL_ACCEL_DEFAULT 7 -/* Single touch emulation should only begin when no touches are currently down. - * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches - * are down and the touch providing for single touch emulation is lifted, - * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is - * occurring, single_touch_id corresponds with the tracking id of the touch used. - */ -#define NO_TOUCHES -1 -#define SINGLE_TOUCH_UP -2 - /* Touch surface information. Dimension is in hundredths of a mm, min and max * are in units. */ #define MOUSE_DIMENSION_X (float)9056 @@ -125,7 +117,6 @@ struct magicmouse_sc { u8 size; } touches[16]; int tracking_ids[16]; - int single_touch_id; }; static int magicmouse_firm_touch(struct magicmouse_sc *msc) @@ -264,16 +255,14 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda } } - if (down) { + if (down) msc->ntouches++; - if (msc->single_touch_id == NO_TOUCHES) - msc->single_touch_id = id; - } else if (msc->single_touch_id == id) - msc->single_touch_id = SINGLE_TOUCH_UP; + + input_mt_slot(input, id); + input_mt_report_slot_state(input, MT_TOOL_FINGER, down); /* Generate the input events for this touch. */ if (down) { - input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); input_report_abs(input, ABS_MT_ORIENTATION, -orientation); @@ -286,8 +275,6 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ input_event(input, EV_MSC, MSC_RAW, tdata[8]); } - - input_mt_sync(input); } } @@ -308,12 +295,6 @@ static int magicmouse_raw_event(struct hid_device *hdev, for (ii = 0; ii < npoints; ii++) magicmouse_emit_touch(msc, ii, data + ii * 9 + 4); - /* We don't need an MT sync here because trackpad emits a - * BTN_TOUCH event in a new frame when all touches are released. - */ - if (msc->ntouches == 0) - msc->single_touch_id = NO_TOUCHES; - clicks = data[1]; /* The following bits provide a device specific timestamp. They @@ -331,9 +312,6 @@ static int magicmouse_raw_event(struct hid_device *hdev, for (ii = 0; ii < npoints; ii++) magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); - if (msc->ntouches == 0) - input_mt_sync(input); - /* When emulating three-button mode, it is important * to have the current touch information before * generating a click event. @@ -366,25 +344,17 @@ static int magicmouse_raw_event(struct hid_device *hdev, input_report_rel(input, REL_Y, y); } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ input_report_key(input, BTN_MOUSE, clicks & 1); - input_report_key(input, BTN_TOUCH, msc->ntouches > 0); - input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1); - input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2); - input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3); - input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4); - if (msc->single_touch_id >= 0) { - input_report_abs(input, ABS_X, - msc->touches[msc->single_touch_id].x); - input_report_abs(input, ABS_Y, - msc->touches[msc->single_touch_id].y); - } + input_mt_report_pointer_emulation(input, true); } input_sync(input); return 1; } -static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) +static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) { + int error; + __set_bit(EV_KEY, input->evbit); if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { @@ -413,6 +383,7 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); __set_bit(BTN_TOOL_QUADTAP, input->keybit); + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); __set_bit(BTN_TOUCH, input->keybit); __set_bit(INPUT_PROP_POINTER, input->propbit); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); @@ -421,7 +392,9 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h __set_bit(EV_ABS, input->evbit); - input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); + error = input_mt_init_slots(input, 16); + if (error) + return error; input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2, @@ -468,6 +441,8 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h __set_bit(EV_MSC, input->evbit); __set_bit(MSC_RAW, input->mscbit); } + + return 0; } static int magicmouse_input_mapping(struct hid_device *hdev, @@ -506,8 +481,6 @@ static int magicmouse_probe(struct hid_device *hdev, msc->quirks = id->driver_data; hid_set_drvdata(hdev, msc); - msc->single_touch_id = NO_TOUCHES; - ret = hid_parse(hdev); if (ret) { hid_err(hdev, "magicmouse hid parse failed\n"); @@ -523,8 +496,13 @@ static int magicmouse_probe(struct hid_device *hdev, /* We do this after hid-input is done parsing reports so that * hid-input uses the most natural button and axis IDs. */ - if (msc->input) - magicmouse_setup_input(msc->input, hdev); + if (msc->input) { + ret = magicmouse_setup_input(msc->input, hdev); + if (ret) { + hid_err(hdev, "magicmouse setup input failed (%d)\n", ret); + goto err_stop_hw; + } + } if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) report = hid_register_report(hdev, HID_INPUT_REPORT, -- cgit v1.2.3 From 9b7e31bbf4bb58b12e11a7f24b7c3e48bbd2f4da Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 4 Jul 2012 13:02:56 -0700 Subject: Input: request threaded-only IRQs with IRQF_ONESHOT Since commit 1c6c69525b ("genirq: Reject bogus threaded irq requests") threaded IRQs without a primary handler need to be requested with IRQF_ONESHOT, otherwise the request will fail. This patch adds the IRQF_ONESHOT to input drivers where it is missing. Not modified by this patch are those drivers where the requested IRQ will always be a nested IRQ (e.g. because it's part of an MFD), since for this special case IRQF_ONESHOT is not required to be specified when requesting the IRQ. Signed-off-by: Lars-Peter Clausen Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/as5011.c | 5 +++-- drivers/input/keyboard/mcs_touchkey.c | 3 ++- drivers/input/keyboard/mpr121_touchkey.c | 2 +- drivers/input/keyboard/qt1070.c | 3 ++- drivers/input/keyboard/tca6416-keypad.c | 3 ++- drivers/input/keyboard/tca8418_keypad.c | 2 +- drivers/input/keyboard/tnetv107x-keypad.c | 8 ++++---- drivers/input/misc/ad714x.c | 8 +++++--- drivers/input/misc/dm355evm_keys.c | 3 ++- drivers/input/touchscreen/ad7879.c | 2 +- drivers/input/touchscreen/atmel_mxt_ts.c | 3 ++- drivers/input/touchscreen/bu21013_ts.c | 3 ++- drivers/input/touchscreen/cy8ctmg110_ts.c | 3 ++- drivers/input/touchscreen/intel-mid-touch.c | 2 +- drivers/input/touchscreen/pixcir_i2c_ts.c | 2 +- drivers/input/touchscreen/tnetv107x-ts.c | 2 +- drivers/input/touchscreen/tsc2005.c | 3 ++- 17 files changed, 34 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c index 3063464474bf..feeefcb09e78 100644 --- a/drivers/input/joystick/as5011.c +++ b/drivers/input/joystick/as5011.c @@ -281,7 +281,8 @@ static int __devinit as5011_probe(struct i2c_client *client, error = request_threaded_irq(as5011->button_irq, NULL, as5011_button_interrupt, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "as5011_button", as5011); if (error < 0) { dev_err(&client->dev, @@ -295,7 +296,7 @@ static int __devinit as5011_probe(struct i2c_client *client, error = request_threaded_irq(as5011->axis_irq, NULL, as5011_axis_interrupt, - plat_data->axis_irqflags, + plat_data->axis_irqflags | IRQF_ONESHOT, "as5011_joystick", as5011); if (error) { dev_err(&client->dev, diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c index 64a0ca4c92f3..0d77f6c84950 100644 --- a/drivers/input/keyboard/mcs_touchkey.c +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -178,7 +178,8 @@ static int __devinit mcs_touchkey_probe(struct i2c_client *client, } error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, - IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->dev.driver->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); goto err_free_mem; diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index caa218a51b5a..7613f1cac951 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -248,7 +248,7 @@ static int __devinit mpr_touchkey_probe(struct i2c_client *client, error = request_threaded_irq(client->irq, NULL, mpr_touchkey_interrupt, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->dev.driver->name, mpr121); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c index 0b7b2f891752..ca68f2992d72 100644 --- a/drivers/input/keyboard/qt1070.c +++ b/drivers/input/keyboard/qt1070.c @@ -201,7 +201,8 @@ static int __devinit qt1070_probe(struct i2c_client *client, msleep(QT1070_RESET_TIME); err = request_threaded_irq(client->irq, NULL, qt1070_interrupt, - IRQF_TRIGGER_NONE, client->dev.driver->name, data); + IRQF_TRIGGER_NONE | IRQF_ONESHOT, + client->dev.driver->name, data); if (err) { dev_err(&client->dev, "fail to request irq\n"); goto err_free_mem; diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 3afea3f89718..c355cdde8d22 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -278,7 +278,8 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client, error = request_threaded_irq(chip->irqnum, NULL, tca6416_keys_isr, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "tca6416-keypad", chip); if (error) { dev_dbg(&client->dev, diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 5f87b28b3192..893869b29ed9 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -360,7 +360,7 @@ static int __devinit tca8418_keypad_probe(struct i2c_client *client, client->irq = gpio_to_irq(client->irq); error = request_threaded_irq(client->irq, NULL, tca8418_irq_handler, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, keypad_data); if (error) { dev_dbg(&client->dev, diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index a4a445fb7020..4c34f21fbe2d 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -227,15 +227,15 @@ static int __devinit keypad_probe(struct platform_device *pdev) goto error_clk; } - error = request_threaded_irq(kp->irq_press, NULL, keypad_irq, 0, - dev_name(dev), kp); + error = request_threaded_irq(kp->irq_press, NULL, keypad_irq, + IRQF_ONESHOT, dev_name(dev), kp); if (error < 0) { dev_err(kp->dev, "Could not allocate keypad press key irq\n"); goto error_irq_press; } - error = request_threaded_irq(kp->irq_release, NULL, keypad_irq, 0, - dev_name(dev), kp); + error = request_threaded_irq(kp->irq_release, NULL, keypad_irq, + IRQF_ONESHOT, dev_name(dev), kp); if (error < 0) { dev_err(kp->dev, "Could not allocate keypad release key irq\n"); goto error_irq_release; diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index 0ac75bbad4d6..2e5d5e1de647 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -972,6 +972,7 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, struct ad714x_platform_data *plat_data = dev->platform_data; struct ad714x_chip *ad714x; void *drv_mem; + unsigned long irqflags; struct ad714x_button_drv *bt_drv; struct ad714x_slider_drv *sd_drv; @@ -1162,10 +1163,11 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, alloc_idx++; } + irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING; + irqflags |= IRQF_ONESHOT; + error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread, - plat_data->irqflags ? - plat_data->irqflags : IRQF_TRIGGER_FALLING, - "ad714x_captouch", ad714x); + irqflags, "ad714x_captouch", ad714x); if (error) { dev_err(dev, "can't allocate irq %d\n", ad714x->irq); goto err_unreg_dev; diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c index 35083c6836c3..c1313d8535c3 100644 --- a/drivers/input/misc/dm355evm_keys.c +++ b/drivers/input/misc/dm355evm_keys.c @@ -213,7 +213,8 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev) /* REVISIT: flush the event queue? */ status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq, - IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), keys); + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&pdev->dev), keys); if (status < 0) goto fail2; diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index e2482b40da51..bd4eb4277697 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -597,7 +597,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, AD7879_TMR(ts->pen_down_acc_interval); err = request_threaded_irq(ts->irq, NULL, ad7879_irq, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, dev_name(dev), ts); if (err) { dev_err(dev, "irq %d busy?\n", ts->irq); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 42e645062c20..25fd0561a17d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1149,7 +1149,8 @@ static int __devinit mxt_probe(struct i2c_client *client, goto err_free_object; error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - pdata->irqflags, client->dev.driver->name, data); + pdata->irqflags | IRQF_ONESHOT, + client->dev.driver->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); goto err_free_object; diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index f2d03c06c2da..5c487d23f11c 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -509,7 +509,8 @@ static int __devinit bu21013_probe(struct i2c_client *client, input_set_drvdata(in_dev, bu21013_data); error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, - IRQF_TRIGGER_FALLING | IRQF_SHARED, + IRQF_TRIGGER_FALLING | IRQF_SHARED | + IRQF_ONESHOT, DRIVER_TP, bu21013_data); if (error) { dev_err(&client->dev, "request irq %d failed\n", pdata->irq); diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c index 237753ad1031..464f1bf4b61d 100644 --- a/drivers/input/touchscreen/cy8ctmg110_ts.c +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -251,7 +251,8 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client, } err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, - IRQF_TRIGGER_RISING, "touch_reset_key", ts); + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "touch_reset_key", ts); if (err < 0) { dev_err(&client->dev, "irq %d busy? error %d\n", client->irq, err); diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c index 3cd7a837f82b..cf299377fc49 100644 --- a/drivers/input/touchscreen/intel-mid-touch.c +++ b/drivers/input/touchscreen/intel-mid-touch.c @@ -620,7 +620,7 @@ static int __devinit mrstouch_probe(struct platform_device *pdev) MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0); err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq, - 0, "mrstouch", tsdev); + IRQF_ONESHOT, "mrstouch", tsdev); if (err) { dev_err(tsdev->dev, "unable to allocate irq\n"); goto err_free_mem; diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 72f6ba3a4709..953b4c105cad 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -165,7 +165,7 @@ static int __devinit pixcir_i2c_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); error = request_threaded_irq(client->irq, NULL, pixcir_ts_isr, - IRQF_TRIGGER_FALLING, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, tsdata); if (error) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c index 7e7488097359..368d2c6cf780 100644 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ b/drivers/input/touchscreen/tnetv107x-ts.c @@ -297,7 +297,7 @@ static int __devinit tsc_probe(struct platform_device *pdev) goto error_clk; } - error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, 0, + error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, IRQF_ONESHOT, dev_name(dev), ts); if (error < 0) { dev_err(ts->dev, "Could not allocate ts irq\n"); diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index b6adeaee9cc5..5ce3fa8ce646 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -650,7 +650,8 @@ static int __devinit tsc2005_probe(struct spi_device *spi) tsc2005_stop_scan(ts); error = request_threaded_irq(spi->irq, NULL, tsc2005_irq_thread, - IRQF_TRIGGER_RISING, "tsc2005", ts); + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "tsc2005", ts); if (error) { dev_err(&spi->dev, "Failed to request irq, err: %d\n", error); goto err_free_mem; -- cgit v1.2.3 From 64e2bc41304726a5ca8a4d37754db69b88841ebf Mon Sep 17 00:00:00 2001 From: Lauri Hintsala Date: Wed, 4 Jul 2012 13:49:54 +0300 Subject: clk: mxs: imx28: decrease the frequency of ref_io1 for SSP2 and SSP3 SSP0 and SSP1 use ref_io0 which has decreased frequency. Expand the frequency fix for ref_io1 to get SSP2 and SSP3 to work. Signed-off-by: Lauri Hintsala Signed-off-by: Shawn Guo --- drivers/clk/mxs/clk-imx28.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 365053f052cd..98624eec5dd4 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -112,11 +112,11 @@ static void __init clk_misc_init(void) /* * 480 MHz seems too high to be ssp clock source directly, - * so set frac0 to get a 288 MHz ref_io0. + * so set frac0 to get a 288 MHz ref_io0 and ref_io1. */ val = readl_relaxed(FRAC0); - val &= ~(0x3f << BP_FRAC0_IO0FRAC); - val |= 30 << BP_FRAC0_IO0FRAC; + val &= ~((0x3f << BP_FRAC0_IO0FRAC) | (0x3f << BP_FRAC0_IO1FRAC)); + val |= (30 << BP_FRAC0_IO0FRAC) | (30 << BP_FRAC0_IO1FRAC); writel_relaxed(val, FRAC0); } -- cgit v1.2.3 From 178709bbfe9d4fe432c272ed65a34b8582703c23 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:00:03 -0700 Subject: ipoib: Convert over to dev_lookup_neigh_skb(). Signed-off-by: David S. Miller --- drivers/infiniband/ulp/ipoib/ipoib_main.c | 4 +++- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 3974c290b667..bbee4b2d7a13 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -715,7 +715,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) rcu_read_lock(); if (likely(skb_dst(skb))) { - n = dst_get_neighbour_noref(skb_dst(skb)); + n = dst_neigh_lookup_skb(skb_dst(skb), skb); if (!n) { ++dev->stats.tx_dropped; dev_kfree_skb_any(skb); @@ -797,6 +797,8 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) } } unlock: + if (n) + neigh_release(n); rcu_read_unlock(); return NETDEV_TX_OK; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 20ebc6fd1bb9..fbb95ee538b2 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -720,16 +720,20 @@ out: rcu_read_lock(); if (dst) - n = dst_get_neighbour_noref(dst); - if (n && !*to_ipoib_neigh(n)) { - struct ipoib_neigh *neigh = ipoib_neigh_alloc(n, - skb->dev); - - if (neigh) { - kref_get(&mcast->ah->ref); - neigh->ah = mcast->ah; - list_add_tail(&neigh->list, &mcast->neigh_list); + n = dst_neigh_lookup_skb(dst, skb); + if (n) { + if (!*to_ipoib_neigh(n)) { + struct ipoib_neigh *neigh; + + neigh = ipoib_neigh_alloc(n, skb->dev); + if (neigh) { + kref_get(&mcast->ah->ref); + neigh->ah = mcast->ah; + list_add_tail(&neigh->list, + &mcast->neigh_list); + } } + neigh_release(n); } rcu_read_unlock(); spin_unlock_irqrestore(&priv->lock, flags); -- cgit v1.2.3 From 24db1ba866eebf5b516df80ea2212d2479bfb502 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:02:33 -0700 Subject: qeth: Convert over to dst_neigh_lookup_skb(). Signed-off-by: David S. Miller --- drivers/s390/net/qeth_l3_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 7be5e9775691..73ac63d901c9 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2700,10 +2700,11 @@ int inline qeth_l3_get_cast_type(struct qeth_card *card, struct sk_buff *skb) rcu_read_lock(); dst = skb_dst(skb); if (dst) - n = dst_get_neighbour_noref(dst); + n = dst_neigh_lookup_skb(dst, skb); if (n) { cast_type = n->type; rcu_read_unlock(); + neigh_release(n); if ((cast_type == RTN_BROADCAST) || (cast_type == RTN_MULTICAST) || (cast_type == RTN_ANYCAST)) -- cgit v1.2.3 From 0b399d46b317a6d0a73ad523e014ecfa4d449769 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:08:58 -0700 Subject: cxgbi: Convert over to dst_neigh_lookup(). Signed-off-by: David S. Miller --- drivers/scsi/cxgbi/libcxgbi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index d9253db1d0e2..b44c1cff3114 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -494,7 +494,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) goto err_out; } dst = &rt->dst; - n = dst_get_neighbour_noref(dst); + n = dst_neigh_lookup(dst, &daddr->sin_addr.s_addr); if (!n) { err = -ENODEV; goto rel_rt; @@ -506,7 +506,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) &daddr->sin_addr.s_addr, ntohs(daddr->sin_port), ndev->name); err = -ENETUNREACH; - goto rel_rt; + goto rel_neigh; } if (ndev->flags & IFF_LOOPBACK) { @@ -521,7 +521,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) pr_info("dst %pI4, %s, NOT cxgbi device.\n", &daddr->sin_addr.s_addr, ndev->name); err = -ENETUNREACH; - goto rel_rt; + goto rel_neigh; } log_debug(1 << CXGBI_DBG_SOCK, "route to %pI4 :%u, ndev p#%d,%s, cdev 0x%p.\n", @@ -531,7 +531,7 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) csk = cxgbi_sock_create(cdev); if (!csk) { err = -ENOMEM; - goto rel_rt; + goto rel_neigh; } csk->cdev = cdev; csk->port_id = port; @@ -541,9 +541,13 @@ static struct cxgbi_sock *cxgbi_check_route(struct sockaddr *dst_addr) csk->daddr.sin_port = daddr->sin_port; csk->daddr.sin_family = daddr->sin_family; csk->saddr.sin_addr.s_addr = fl4.saddr; + neigh_release(n); return csk; +rel_neigh: + neigh_release(n); + rel_rt: ip_rt_put(rt); if (csk) -- cgit v1.2.3 From c473737765c0f72ceb5b245ada7ead798d88b4f6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:10:55 -0700 Subject: cxgb4i: Convert over to dst_neigh_lookup(). Signed-off-by: David S. Miller --- drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index 5a4a3bfc60cf..cc9a06897f34 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -1142,7 +1142,7 @@ static int init_act_open(struct cxgbi_sock *csk) cxgbi_sock_set_flag(csk, CTPF_HAS_ATID); cxgbi_sock_get(csk); - n = dst_get_neighbour_noref(csk->dst); + n = dst_neigh_lookup(csk->dst, &csk->daddr.sin_addr.s_addr); if (!n) { pr_err("%s, can't get neighbour of csk->dst.\n", ndev->name); goto rel_resource; @@ -1182,9 +1182,12 @@ static int init_act_open(struct cxgbi_sock *csk) cxgbi_sock_set_state(csk, CTP_ACTIVE_OPEN); send_act_open_req(csk, skb, csk->l2t); + neigh_release(n); return 0; rel_resource: + if (n) + neigh_release(n); if (skb) __kfree_skb(skb); return -EINVAL; -- cgit v1.2.3 From 3923040bd1ba2ee5357f6ac01c82d8c24d73ce26 Mon Sep 17 00:00:00 2001 From: Patrice Chotard Date: Wed, 4 Jul 2012 17:00:04 +0200 Subject: pinctrl/nomadik: add spi2_oc1_2 pin group On 8500 and 9540, all SPI2 pins are driven by SPI block except SPI2_FRM which is driven as GPIO due to specific constraints. A new group spi2_oc1_2 is created which exclude SPI2_FRM pin. Signed-off-by: Patrice Chotard Signed-off-by: Linus Walleij --- drivers/pinctrl/pinctrl-nomadik-db8500.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c index 98174936cb5c..6f99769c6733 100644 --- a/drivers/pinctrl/pinctrl-nomadik-db8500.c +++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c @@ -601,6 +601,8 @@ static const unsigned kp_oc1_1_pins[] = { DB8500_PIN_C6, DB8500_PIN_B3, DB8500_PIN_D6, DB8500_PIN_B7 }; static const unsigned spi2_oc1_1_pins[] = { DB8500_PIN_AH13, DB8500_PIN_AG12, DB8500_PIN_AH12, DB8500_PIN_AH11 }; +static const unsigned spi2_oc1_2_pins[] = { DB8500_PIN_AH13, DB8500_PIN_AH12, + DB8500_PIN_AH11 }; #define DB8500_PIN_GROUP(a,b) { .name = #a, .pins = a##_pins, \ .npins = ARRAY_SIZE(a##_pins), .altsetting = b } @@ -720,6 +722,7 @@ static const struct nmk_pingroup nmk_db8500_groups[] = { /* Other alt C1 column, these are still configured as alt C */ DB8500_PIN_GROUP(kp_oc1_1, NMK_GPIO_ALT_C), DB8500_PIN_GROUP(spi2_oc1_1, NMK_GPIO_ALT_C), + DB8500_PIN_GROUP(spi2_oc1_2, NMK_GPIO_ALT_C), }; /* We use this macro to define the groups applicable to a function */ @@ -795,7 +798,7 @@ DB8500_FUNC_GROUPS(mc5, "mc5_c_1"); DB8500_FUNC_GROUPS(usbsim, "usbsim_c_1", "usbsim_c_2"); DB8500_FUNC_GROUPS(i2c3, "i2c3_c_1", "i2c3_c_2"); DB8500_FUNC_GROUPS(spi0, "spi0_c_1"); -DB8500_FUNC_GROUPS(spi2, "spi2_oc1_1"); +DB8500_FUNC_GROUPS(spi2, "spi2_oc1_1", "spi2_oc1_2"); #define FUNCTION(fname) \ { \ -- cgit v1.2.3 From 1d248b1cf4e09dbec8cef5f7fbeda5874248bd09 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jul 2012 01:01:51 -0700 Subject: net: Pass neighbours and dest address into NETEVENT_REDIRECT events. Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 23 +++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 55cf72af69ce..633c6029e53c 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -62,7 +62,8 @@ static const unsigned int MAX_ATIDS = 64 * 1024; static const unsigned int ATID_BASE = 0x10000; static void cxgb_neigh_update(struct neighbour *neigh); -static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new); +static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, + struct dst_entry *new, struct neighbour *new_neigh); static inline int offload_activated(struct t3cdev *tdev) { @@ -968,8 +969,9 @@ static int nb_callback(struct notifier_block *self, unsigned long event, } case (NETEVENT_REDIRECT):{ struct netevent_redirect *nr = ctx; - cxgb_redirect(nr->old, nr->new); - cxgb_neigh_update(dst_get_neighbour_noref(nr->new)); + cxgb_redirect(nr->old, nr->old_neigh, + nr->new, nr->new_neigh); + cxgb_neigh_update(nr->new_neigh); break; } default: @@ -1107,10 +1109,10 @@ static void set_l2t_ix(struct t3cdev *tdev, u32 tid, struct l2t_entry *e) tdev->send(tdev, skb); } -static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) +static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, + struct dst_entry *new, struct neighbour *new_neigh) { struct net_device *olddev, *newdev; - struct neighbour *n; struct tid_info *ti; struct t3cdev *tdev; u32 tid; @@ -1118,15 +1120,8 @@ static void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) struct l2t_entry *e; struct t3c_tid_entry *te; - n = dst_get_neighbour_noref(old); - if (!n) - return; - olddev = n->dev; - - n = dst_get_neighbour_noref(new); - if (!n) - return; - newdev = n->dev; + olddev = old_neigh->dev; + newdev = new_neigh->dev; if (!is_offloading(olddev)) return; -- cgit v1.2.3 From 534cb283efef9fdbd9f70f4615054d26aa444dd6 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 2 Jul 2012 22:35:31 -0700 Subject: cxgb3: Convert t3_l2t_get() over to dst_neigh_lookup(). This means passing in a suitable destination address. Signed-off-by: David S. Miller --- drivers/infiniband/hw/cxgb3/iwch_cm.c | 5 +++-- drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 11 +++++++---- drivers/net/ethernet/chelsio/cxgb3/l2t.c | 6 ++++-- drivers/net/ethernet/chelsio/cxgb3/l2t.h | 2 +- drivers/scsi/cxgbi/cxgb3i/cxgb3i.c | 3 ++- 5 files changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 740dcc065cf2..77b6b182778a 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -1374,7 +1374,7 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) goto reject; } dst = &rt->dst; - l2t = t3_l2t_get(tdev, dst, NULL); + l2t = t3_l2t_get(tdev, dst, NULL, &req->peer_ip); if (!l2t) { printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", __func__); @@ -1942,7 +1942,8 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) goto fail3; } ep->dst = &rt->dst; - ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst, NULL); + ep->l2t = t3_l2t_get(ep->com.tdev, ep->dst, NULL, + &cm_id->remote_addr.sin_addr.s_addr); if (!ep->l2t) { printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); err = -ENOMEM; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 633c6029e53c..2dbbcbb450d3 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -63,7 +63,8 @@ static const unsigned int ATID_BASE = 0x10000; static void cxgb_neigh_update(struct neighbour *neigh); static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, - struct dst_entry *new, struct neighbour *new_neigh); + struct dst_entry *new, struct neighbour *new_neigh, + const void *daddr); static inline int offload_activated(struct t3cdev *tdev) { @@ -970,7 +971,8 @@ static int nb_callback(struct notifier_block *self, unsigned long event, case (NETEVENT_REDIRECT):{ struct netevent_redirect *nr = ctx; cxgb_redirect(nr->old, nr->old_neigh, - nr->new, nr->new_neigh); + nr->new, nr->new_neigh, + nr->daddr); cxgb_neigh_update(nr->new_neigh); break; } @@ -1110,7 +1112,8 @@ static void set_l2t_ix(struct t3cdev *tdev, u32 tid, struct l2t_entry *e) } static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, - struct dst_entry *new, struct neighbour *new_neigh) + struct dst_entry *new, struct neighbour *new_neigh, + const void *daddr) { struct net_device *olddev, *newdev; struct tid_info *ti; @@ -1139,7 +1142,7 @@ static void cxgb_redirect(struct dst_entry *old, struct neighbour *old_neigh, } /* Add new L2T entry */ - e = t3_l2t_get(tdev, new, newdev); + e = t3_l2t_get(tdev, new, newdev, daddr); if (!e) { printk(KERN_ERR "%s: couldn't allocate new l2t entry!\n", __func__); diff --git a/drivers/net/ethernet/chelsio/cxgb3/l2t.c b/drivers/net/ethernet/chelsio/cxgb3/l2t.c index 3fa3c8833ed7..8d53438638b2 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb3/l2t.c @@ -299,7 +299,7 @@ static inline void reuse_entry(struct l2t_entry *e, struct neighbour *neigh) } struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct dst_entry *dst, - struct net_device *dev) + struct net_device *dev, const void *daddr) { struct l2t_entry *e = NULL; struct neighbour *neigh; @@ -311,7 +311,7 @@ struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct dst_entry *dst, int smt_idx; rcu_read_lock(); - neigh = dst_get_neighbour_noref(dst); + neigh = dst_neigh_lookup(dst, daddr); if (!neigh) goto done_rcu; @@ -360,6 +360,8 @@ struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct dst_entry *dst, done_unlock: write_unlock_bh(&d->lock); done_rcu: + if (neigh) + neigh_release(neigh); rcu_read_unlock(); return e; } diff --git a/drivers/net/ethernet/chelsio/cxgb3/l2t.h b/drivers/net/ethernet/chelsio/cxgb3/l2t.h index c4e864369751..8cffcdfd5678 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/l2t.h +++ b/drivers/net/ethernet/chelsio/cxgb3/l2t.h @@ -110,7 +110,7 @@ static inline void set_arp_failure_handler(struct sk_buff *skb, void t3_l2e_free(struct l2t_data *d, struct l2t_entry *e); void t3_l2t_update(struct t3cdev *dev, struct neighbour *neigh); struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct dst_entry *dst, - struct net_device *dev); + struct net_device *dev, const void *daddr); int t3_l2t_send_slow(struct t3cdev *dev, struct sk_buff *skb, struct l2t_entry *e); void t3_l2t_send_event(struct t3cdev *dev, struct l2t_entry *e); diff --git a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c index 36739da8bc15..49692a1ac44a 100644 --- a/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c +++ b/drivers/scsi/cxgbi/cxgb3i/cxgb3i.c @@ -966,7 +966,8 @@ static int init_act_open(struct cxgbi_sock *csk) csk->saddr.sin_addr.s_addr = chba->ipv4addr; csk->rss_qid = 0; - csk->l2t = t3_l2t_get(t3dev, dst, ndev); + csk->l2t = t3_l2t_get(t3dev, dst, ndev, + &csk->daddr.sin_addr.s_addr); if (!csk->l2t) { pr_err("NO l2t available.\n"); return -EINVAL; -- cgit v1.2.3 From 90b1ebe7af2592597e7568bb203d59f4918f76ad Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:51 +0000 Subject: mlx4: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a0313de122de..14d9c762b60f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1539,8 +1540,8 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); struct msix_entry *entries; int nreq = min_t(int, dev->caps.num_ports * - min_t(int, num_online_cpus() + 1, MAX_MSIX_P_PORT) - + MSIX_LEGACY_SZ, MAX_MSIX); + min_t(int, netif_get_num_default_rss_queues() + 1, + MAX_MSIX_P_PORT) + MSIX_LEGACY_SZ, MAX_MSIX); int err; int i; -- cgit v1.2.3 From 9cbb5760235272564f4bb0dd7f256fb7c510a2d5 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:52 +0000 Subject: vxge: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Jon Mason Signed-off-by: David S. Miller --- drivers/net/ethernet/neterion/vxge/vxge-main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index 2578eb1f025d..2fd1edbc5e0e 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -3687,7 +3687,8 @@ static int __devinit vxge_config_vpaths( return 0; if (!driver_config->g_no_cpus) - driver_config->g_no_cpus = num_online_cpus(); + driver_config->g_no_cpus = + netif_get_num_default_rss_queues(); driver_config->vpath_per_dev = driver_config->g_no_cpus >> 1; if (!driver_config->vpath_per_dev) -- cgit v1.2.3 From 9eb8738d1eb869c34017ff1c23ac9f725c4aeb30 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:53 +0000 Subject: qlge: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Anirban Chakraborty Cc: Jitendra Kalsaria Cc: Ron Mercer Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qlge/qlge_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 09d8d33171df..3c3499d928b9 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4649,7 +4649,7 @@ static int __devinit qlge_probe(struct pci_dev *pdev, int err = 0; ndev = alloc_etherdev_mq(sizeof(struct ql_adapter), - min(MAX_CPUS, (int)num_online_cpus())); + min(MAX_CPUS, netif_get_num_default_rss_queues())); if (!ndev) return -ENOMEM; -- cgit v1.2.3 From dbfa600148a25903976910863c75dae185f8d187 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:54 +0000 Subject: cxgb3: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Divy Le Ray Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index abb6ce7c1b7e..9b0874957df5 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3050,7 +3050,7 @@ static struct pci_error_handlers t3_err_handler = { static void set_nqsets(struct adapter *adap) { int i, j = 0; - int num_cpus = num_online_cpus(); + int num_cpus = netif_get_num_default_rss_queues(); int hwports = adap->params.nports; int nqsets = adap->msix_nvectors - 1; -- cgit v1.2.3 From 5952dde723077e5e0db43cbc9dfb00a4f47c42ef Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:55 +0000 Subject: cxgb4: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Divy Le Ray Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index e1f96fbb48c1..5ed49af23d6a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3493,8 +3493,8 @@ static void __devinit cfg_queues(struct adapter *adap) */ if (n10g) q10g = (MAX_ETH_QSETS - (adap->params.nports - n10g)) / n10g; - if (q10g > num_online_cpus()) - q10g = num_online_cpus(); + if (q10g > netif_get_num_default_rss_queues()) + q10g = netif_get_num_default_rss_queues(); for_each_port(adap, i) { struct port_info *pi = adap2pinfo(adap, i); -- cgit v1.2.3 From 98f2d21f90278856b33e965d1d4bb756754e70f3 Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:56 +0000 Subject: myri10ge: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Jon Mason Signed-off-by: David S. Miller --- drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 90153fc983cb..fa85cf1353fd 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -3775,7 +3775,7 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp) mgp->num_slices = 1; msix_cap = pci_find_capability(pdev, PCI_CAP_ID_MSIX); - ncpus = num_online_cpus(); + ncpus = netif_get_num_default_rss_queues(); if (myri10ge_max_slices == 1 || msix_cap == 0 || (myri10ge_max_slices == -1 && ncpus < 2)) -- cgit v1.2.3 From 118008784965003307ea164370094c7d0810546e Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:57 +0000 Subject: tg3: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Matt Carlson Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/tg3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index e47ff8be1d7b..6cbab0369738 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -9908,7 +9908,7 @@ static bool tg3_enable_msix(struct tg3 *tp) int i, rc; struct msix_entry msix_ent[tp->irq_max]; - tp->irq_cnt = num_online_cpus(); + tp->irq_cnt = netif_get_num_default_rss_queues(); if (tp->irq_cnt > 1) { /* We want as many rx rings enabled as there are cpus. * In multiqueue MSI-X mode, the first MSI-X vector -- cgit v1.2.3 From 0a742128db29031e55d27e37b1b098433c56033a Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:58 +0000 Subject: bnx2: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Michael Chan Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 9eb7624639b9..1901da153312 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -6250,7 +6250,7 @@ bnx2_enable_msix(struct bnx2 *bp, int msix_vecs) static int bnx2_setup_int_mode(struct bnx2 *bp, int dis_msi) { - int cpus = num_online_cpus(); + int cpus = netif_get_num_default_rss_queues(); int msix_vecs; if (!bp->num_req_rx_rings) -- cgit v1.2.3 From 7d515413557907078e805ee7fd6f3920bc33762d Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:18:59 +0000 Subject: bnx2x: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index daa894bd772a..53659f321d51 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -822,7 +822,8 @@ static inline int bnx2x_calc_num_queues(struct bnx2x *bp) { return num_queues ? min_t(int, num_queues, BNX2X_MAX_QUEUES(bp)) : - min_t(int, num_online_cpus(), BNX2X_MAX_QUEUES(bp)); + min_t(int, netif_get_num_default_rss_queues(), + BNX2X_MAX_QUEUES(bp)); } static inline void bnx2x_clear_sge_mask_next_elems(struct bnx2x_fastpath *fp) -- cgit v1.2.3 From 30e80b55dd250b17c2570e234d2be34a0718063e Mon Sep 17 00:00:00 2001 From: Yuval Mintz Date: Sun, 1 Jul 2012 03:19:00 +0000 Subject: be2net: set maximal number of default RSS queues Signed-off-by: Yuval Mintz Signed-off-by: Eilon Greenstein Cc: Sathya Perla Cc: Subbu Seetharaman Cc: Ajit Khaparde Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_main.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index edce7af97c87..2141bd784751 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -2172,12 +2172,14 @@ static void be_msix_disable(struct be_adapter *adapter) static uint be_num_rss_want(struct be_adapter *adapter) { + u32 num = 0; if ((adapter->function_caps & BE_FUNCTION_CAPS_RSS) && !sriov_want(adapter) && be_physfn(adapter) && - !be_is_mc(adapter)) - return (adapter->be3_native) ? BE3_MAX_RSS_QS : BE2_MAX_RSS_QS; - else - return 0; + !be_is_mc(adapter)) { + num = (adapter->be3_native) ? BE3_MAX_RSS_QS : BE2_MAX_RSS_QS; + num = min_t(u32, num, (u32)netif_get_num_default_rss_queues()); + } + return num; } static void be_msix_enable(struct be_adapter *adapter) -- cgit v1.2.3 From 23c342153ef2a4ad8bd4f2d6515126449658d2b3 Mon Sep 17 00:00:00 2001 From: "alex.bluesman.smirnov@gmail.com" Date: Sun, 1 Jul 2012 20:18:32 +0000 Subject: drivers/ieee802154/at231rf230: remove unused return status Remove excessive variable used for the return status. Signed-off-by: Alexander Smirnov Signed-off-by: David S. Miller --- drivers/ieee802154/at86rf230.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee802154/at86rf230.c b/drivers/ieee802154/at86rf230.c index 4d033d4c4ddc..902e38bb382f 100644 --- a/drivers/ieee802154/at86rf230.c +++ b/drivers/ieee802154/at86rf230.c @@ -585,7 +585,6 @@ err: static int at86rf230_rx(struct at86rf230_local *lp) { u8 len = 128, lqi = 0; - int rc; struct sk_buff *skb; skb = alloc_skb(len, GFP_KERNEL); @@ -607,7 +606,7 @@ static int at86rf230_rx(struct at86rf230_local *lp) ieee802154_rx_irqsafe(lp->dev, skb, lqi); - dev_dbg(&lp->spi->dev, "READ_FBUF: %d %d %x\n", rc, len, lqi); + dev_dbg(&lp->spi->dev, "READ_FBUF: %d %x\n", len, lqi); return 0; err: -- cgit v1.2.3 From e2c98eaf928a2a0ecaca1db9aa5dc56a36699d9f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 5 Jul 2012 14:19:42 +0800 Subject: regulator: core: remove sysfs entry properly in regulator_put With changes introduced by commit 222cc7b (regulator: core: Allow multiple requests of a single supply mapping) on create_regulator, regulator_put needs a corresponding update on sysfs entry removing. Also regulator->dev still needs to get assigned in create_regulator, otherwise, sysfs_remove_link call in regulator_put will get bypassed. Reported-by: Fabio Estevam Tested-by: Dong Aisheng Signed-off-by: Shawn Guo Signed-off-by: Mark Brown --- drivers/regulator/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f98a8ee29933..974276afd1f0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1063,6 +1063,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, list_add(®ulator->list, &rdev->consumer_list); if (dev) { + regulator->dev = dev; + /* Add a link to the device sysfs entry */ size = scnprintf(buf, REG_STR_SIZE, "%s-%s", dev->kobj.name, supply_name); @@ -1359,11 +1361,8 @@ void regulator_put(struct regulator *regulator) debugfs_remove_recursive(regulator->debugfs); /* remove any sysfs entries */ - if (regulator->dev) { + if (regulator->dev) sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); - device_remove_file(regulator->dev, ®ulator->dev_attr); - kfree(regulator->dev_attr.attr.name); - } kfree(regulator->supply_name); list_del(®ulator->list); kfree(regulator); -- cgit v1.2.3 From f8568cbd9459b87d989efdd9c67a9dd9c6ebef3b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 4 Jul 2012 10:03:23 +0800 Subject: regulator: ab3100: Set enable enable_time in regulator_desc Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab3100.c | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 16d420b2f7a8..5f771a86f6e6 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -347,31 +347,6 @@ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg) return abreg->plfdata->external_voltage; } -static int ab3100_enable_time_regulator(struct regulator_dev *reg) -{ - struct ab3100_regulator *abreg = reg->reg_data; - - /* Per-regulator power on delay from spec */ - switch (abreg->regreg) { - case AB3100_LDO_A: /* Fallthrough */ - case AB3100_LDO_C: /* Fallthrough */ - case AB3100_LDO_D: /* Fallthrough */ - case AB3100_LDO_E: /* Fallthrough */ - case AB3100_LDO_H: /* Fallthrough */ - case AB3100_LDO_K: - return 200; - case AB3100_LDO_F: - return 600; - case AB3100_LDO_G: - return 400; - case AB3100_BUCK: - return 1000; - default: - break; - } - return 0; -} - static int ab3100_get_fixed_voltage_regulator(struct regulator_dev *reg) { return reg->desc->min_uV; @@ -383,7 +358,6 @@ static struct regulator_ops regulator_ops_fixed = { .disable = ab3100_disable_regulator, .is_enabled = ab3100_is_enabled_regulator, .get_voltage = ab3100_get_fixed_voltage_regulator, - .enable_time = ab3100_enable_time_regulator, }; static struct regulator_ops regulator_ops_variable = { @@ -393,7 +367,6 @@ static struct regulator_ops regulator_ops_variable = { .get_voltage = ab3100_get_voltage_regulator, .set_voltage_sel = ab3100_set_voltage_regulator_sel, .list_voltage = regulator_list_voltage_table, - .enable_time = ab3100_enable_time_regulator, }; static struct regulator_ops regulator_ops_variable_sleepable = { @@ -404,7 +377,6 @@ static struct regulator_ops regulator_ops_variable_sleepable = { .set_voltage_sel = ab3100_set_voltage_regulator_sel, .set_suspend_voltage = ab3100_set_suspend_voltage_regulator, .list_voltage = regulator_list_voltage_table, - .enable_time = ab3100_enable_time_regulator, }; /* @@ -430,6 +402,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .min_uV = LDO_A_VOLTAGE, + .enable_time = 200, }, { .name = "LDO_C", @@ -439,6 +412,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .min_uV = LDO_C_VOLTAGE, + .enable_time = 200, }, { .name = "LDO_D", @@ -448,6 +422,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .min_uV = LDO_D_VOLTAGE, + .enable_time = 200, }, { .name = "LDO_E", @@ -457,6 +432,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .volt_table = ldo_e_buck_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .enable_time = 200, }, { .name = "LDO_F", @@ -466,6 +442,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .volt_table = ldo_f_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .enable_time = 600, }, { .name = "LDO_G", @@ -475,6 +452,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .volt_table = ldo_g_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .enable_time = 400, }, { .name = "LDO_H", @@ -484,6 +462,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .volt_table = ldo_h_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .enable_time = 200, }, { .name = "LDO_K", @@ -493,6 +472,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .volt_table = ldo_k_typ_voltages, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .enable_time = 200, }, { .name = "LDO_EXT", @@ -508,6 +488,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { .n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages), .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, + .enable_time = 1000, }, }; -- cgit v1.2.3 From 6c3b956b94a6547d26c805db7c505429e54e7573 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 5 Jul 2012 09:38:15 +0800 Subject: regulator: ab3100: Use rdev_get_drvdata() rather than use reg->reg_data driectly Signed-off-by: Axel Lin Acked-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab3100.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index 5f771a86f6e6..182b553059c9 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -155,7 +155,7 @@ ab3100_regulators[AB3100_NUM_REGULATORS] = { */ static int ab3100_enable_regulator(struct regulator_dev *reg) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); int err; u8 regval; @@ -186,7 +186,7 @@ static int ab3100_enable_regulator(struct regulator_dev *reg) static int ab3100_disable_regulator(struct regulator_dev *reg) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); int err; u8 regval; @@ -219,7 +219,7 @@ static int ab3100_disable_regulator(struct regulator_dev *reg) static int ab3100_is_enabled_regulator(struct regulator_dev *reg) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); u8 regval; int err; @@ -236,7 +236,7 @@ static int ab3100_is_enabled_regulator(struct regulator_dev *reg) static int ab3100_get_voltage_regulator(struct regulator_dev *reg) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); u8 regval; int err; @@ -270,7 +270,7 @@ static int ab3100_get_voltage_regulator(struct regulator_dev *reg) static int ab3100_set_voltage_regulator_sel(struct regulator_dev *reg, unsigned selector) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); u8 regval; int err; @@ -299,7 +299,7 @@ static int ab3100_set_voltage_regulator_sel(struct regulator_dev *reg, static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, int uV) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); u8 regval; int err; int bestindex; @@ -342,7 +342,7 @@ static int ab3100_set_suspend_voltage_regulator(struct regulator_dev *reg, */ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg) { - struct ab3100_regulator *abreg = reg->reg_data; + struct ab3100_regulator *abreg = rdev_get_drvdata(reg); return abreg->plfdata->external_voltage; } -- cgit v1.2.3 From 4706fcab9b064e6077404ccb6c5ca9c188758068 Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Thu, 5 Jul 2012 09:28:25 +0530 Subject: regulator: max77686: Add device tree support. This patch device tree support for regulator driver. It uses the parent (mfd's) DT node to parse the regulator data for max77686. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 58 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 23f956a6c5f2..fc695eb3886e 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -233,6 +234,50 @@ static struct regulator_desc regulators[] = { regulator_desc_buck(9), }; +#ifdef CONFIG_OF +static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, + struct max77686_platform_data *pdata) +{ + struct device_node *pmic_np, *regulators_np; + struct max77686_regulator_data *rdata; + struct of_regulator_match rmatch; + unsigned int i; + + pmic_np = iodev->dev->of_node; + regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators"); + if (!regulators_np) { + dev_err(iodev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + pdata->num_regulators = ARRAY_SIZE(regulators); + rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + pdata->num_regulators, GFP_KERNEL); + if (!rdata) { + dev_err(iodev->dev, + "could not allocate memory for regulator data\n"); + return -ENOMEM; + } + + for (i = 0; i < pdata->num_regulators; i++) { + rmatch.name = regulators[i].name; + rmatch.init_data = NULL; + of_regulator_match(iodev->dev, regulators_np, &rmatch, 1); + rdata[i].initdata = rmatch.init_data; + } + + pdata->regulators = rdata; + + return 0; +} +#else +static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, + struct max77686_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + static __devinit int max77686_pmic_probe(struct platform_device *pdev) { struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); @@ -245,7 +290,18 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); - if (!pdata || pdata->num_regulators != MAX77686_REGULATORS) { + if (!pdata) { + dev_err(&pdev->dev, "no platform data found for regulator\n"); + return -ENODEV; + } + + if (iodev->dev->of_node) { + ret = max77686_pmic_dt_parse_pdata(iodev, pdata); + if (ret) + return ret; + } + + if (pdata->num_regulators != MAX77686_REGULATORS) { dev_err(&pdev->dev, "Invalid initial data for regulator's initialiation\n"); return -EINVAL; -- cgit v1.2.3 From 2e2070c85aa19f6c5bb3642a1429abf42f101e8d Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Sun, 27 May 2012 22:55:41 +0200 Subject: gpio-sta2x11: don't use pdata if null If there is no platform data available, the driver shouldn't use the pointer or it will oops. Since things will mostly work nonetheless, (the BIOS may have set up the pins properly), I'd better not fail the probe even in this case. Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Signed-off-by: Linus Walleij --- drivers/gpio/gpio-sta2x11.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-sta2x11.c b/drivers/gpio/gpio-sta2x11.c index 38416be8ba11..6064fb376e11 100644 --- a/drivers/gpio/gpio-sta2x11.c +++ b/drivers/gpio/gpio-sta2x11.c @@ -383,8 +383,9 @@ static int __devinit gsta_probe(struct platform_device *dev) } spin_lock_init(&chip->lock); gsta_gpio_setup(chip); - for (i = 0; i < GSTA_NR_GPIO; i++) - gsta_set_config(chip, i, gpio_pdata->pinconfig[i]); + if (gpio_pdata) + for (i = 0; i < GSTA_NR_GPIO; i++) + gsta_set_config(chip, i, gpio_pdata->pinconfig[i]); /* 384 was used in previous code: be compatible for other drivers */ err = irq_alloc_descs(-1, 384, GSTA_NR_GPIO, NUMA_NO_NODE); -- cgit v1.2.3 From afcc0f8c174ff34305146878c40e7dcc72fcbbdf Mon Sep 17 00:00:00 2001 From: Christian Dietrich Date: Thu, 31 May 2012 13:12:57 +0200 Subject: gpio/msm_v1: CONFIG_GPIO_MSM_V1 is only available on three SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The feature GPIO_MSM_V1 is only available on three SoCs. On all other MSM SoCs the INT_GPIO_GROUP{1,2} is undeclared, but Kconfig does allow such configurations. Therefore the produced configuration is valid, but does not compile. The problem is fixed by adding the missing Kconfig constraints. drivers/gpio/gpio-msm-v1.c: In function ‘msm_init_gpio’: drivers/gpio/gpio-msm-v1.c:629:26: error: 'INT_GPIO_GROUP1' undeclared drivers/gpio/gpio-msm-v1.c:630:26: error: 'INT_GPIO_GROUP2' undeclared Signed-off-by: Christian Dietrich Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index c4067d0141f7..542f0c04b695 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -136,7 +136,7 @@ config GPIO_MPC8XXX config GPIO_MSM_V1 tristate "Qualcomm MSM GPIO v1" - depends on GPIOLIB && ARCH_MSM + depends on GPIOLIB && ARCH_MSM && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50) help Say yes here to support the GPIO interface on ARM v6 based Qualcomm MSM chips. Most of the pins on the MSM can be -- cgit v1.2.3 From 8cd578b6e28693f357867a77598a88ef3deb6b39 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 9 Jun 2012 11:07:56 +0800 Subject: gpiolib: wm8994: Pay attention to the value set when enabling as output Not paying attention to the value being set is a bad thing because it means that we'll not set the hardware up to reflect what was requested. Not setting the hardware up to reflect what was requested means that the caller won't get the results they wanted. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org Signed-off-by: Linus Walleij --- drivers/gpio/gpio-wm8994.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-wm8994.c b/drivers/gpio/gpio-wm8994.c index 92ea5350dfe9..aa61ad2fcaaa 100644 --- a/drivers/gpio/gpio-wm8994.c +++ b/drivers/gpio/gpio-wm8994.c @@ -89,8 +89,11 @@ static int wm8994_gpio_direction_out(struct gpio_chip *chip, struct wm8994_gpio *wm8994_gpio = to_wm8994_gpio(chip); struct wm8994 *wm8994 = wm8994_gpio->wm8994; + if (value) + value = WM8994_GPN_LVL; + return wm8994_set_bits(wm8994, WM8994_GPIO_1 + offset, - WM8994_GPN_DIR, 0); + WM8994_GPN_DIR | WM8994_GPN_LVL, value); } static void wm8994_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -- cgit v1.2.3 From 3996bfc787a3b3d7c7ad97b7519247f9f2ac4068 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 7 Jun 2012 17:56:09 -0600 Subject: gpio: export devm_gpio_request_one Without this, modules can't use this API, leading to build failures. Signed-off-by: Stephen Warren Signed-off-by: Linus Walleij --- drivers/gpio/devres.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/devres.c b/drivers/gpio/devres.c index 9e9947cb86a3..1077754f8289 100644 --- a/drivers/gpio/devres.c +++ b/drivers/gpio/devres.c @@ -98,6 +98,7 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, return 0; } +EXPORT_SYMBOL(devm_gpio_request_one); /** * devm_gpio_free - free an interrupt -- cgit v1.2.3 From 6be5bfc3bf0d31a70745a52e69f7f46de974193f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 5 Jul 2012 18:12:01 +0530 Subject: regulator: fixed: dt: support for input supply Add support for input supply in DT parsing of node. The input supply will be provided by the property "vin-supply" in the regulator node. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 1a0b17e8d8a9..185468c4d38f 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -102,6 +102,9 @@ of_get_fixed_voltage_config(struct device *dev) if (of_find_property(np, "gpio-open-drain", NULL)) config->gpio_is_open_drain = true; + if (of_find_property(np, "vin-supply", NULL)) + config->input_supply = "vin"; + return config; } @@ -169,6 +172,17 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.enable_time = config->startup_delay; + if (config->input_supply) { + drvdata->desc.supply_name = kstrdup(config->input_supply, + GFP_KERNEL); + if (!drvdata->desc.supply_name) { + dev_err(&pdev->dev, + "Failed to allocate input supply\n"); + ret = -ENOMEM; + goto err_name; + } + } + if (config->microvolts) drvdata->desc.n_voltages = 1; @@ -202,7 +216,7 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); - goto err_name; + goto err_input; } platform_set_drvdata(pdev, drvdata); @@ -212,6 +226,8 @@ static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) return 0; +err_input: + kfree(drvdata->desc.supply_name); err_name: kfree(drvdata->desc.name); err: @@ -223,6 +239,7 @@ static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); regulator_unregister(drvdata->dev); + kfree(drvdata->desc.supply_name); kfree(drvdata->desc.name); return 0; -- cgit v1.2.3 From 6d044b90022a6a5cffc35395c4b559b6c36deb06 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 5 Jul 2012 17:31:56 +0300 Subject: iwlwifi: REPLY_RX doesn't exist any more Remove this dead code, it is unused for device newer than 4965. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- drivers/net/wireless/iwlwifi/dvm/main.c | 1 - drivers/net/wireless/iwlwifi/dvm/rx.c | 44 +++++++++------------------------ 2 files changed, 11 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 612f05d757db..84d3db5aa506 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1232,7 +1232,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, struct iwl_trans_config trans_cfg; static const u8 no_reclaim_cmds[] = { REPLY_RX_PHY_CMD, - REPLY_RX, REPLY_RX_MPDU_CMD, REPLY_COMPRESSED_BA, STATISTICS_NOTIFICATION, diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index c1f7a18e08dd..fee5cffa1669 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -88,7 +88,6 @@ const char *iwl_dvm_cmd_strings[REPLY_MAX] = { IWL_CMD_ENTRY(REPLY_PHY_CALIBRATION_CMD), IWL_CMD_ENTRY(REPLY_RX_PHY_CMD), IWL_CMD_ENTRY(REPLY_RX_MPDU_CMD), - IWL_CMD_ENTRY(REPLY_RX), IWL_CMD_ENTRY(REPLY_COMPRESSED_BA), IWL_CMD_ENTRY(CALIBRATION_CFG_CMD), IWL_CMD_ENTRY(CALIBRATION_RES_NOTIFICATION), @@ -895,8 +894,7 @@ static int iwlagn_calc_rssi(struct iwl_priv *priv, return max_rssi - agc - IWLAGN_RSSI_OFFSET; } -/* Called for REPLY_RX (legacy ABG frames), or - * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */ +/* Called for REPLY_RX_MPDU_CMD */ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) @@ -911,37 +909,17 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, u32 ampdu_status; u32 rate_n_flags; - /** - * REPLY_RX and REPLY_RX_MPDU_CMD are handled differently. - * REPLY_RX: physical layer info is in this buffer - * REPLY_RX_MPDU_CMD: physical layer info was sent in separate - * command and cached in priv->last_phy_res - * - * Here we set up local variables depending on which command is - * received. - */ - if (pkt->hdr.cmd == REPLY_RX) { - phy_res = (struct iwl_rx_phy_res *)pkt->data; - header = (struct ieee80211_hdr *)(pkt->data + sizeof(*phy_res) - + phy_res->cfg_phy_cnt); - - len = le16_to_cpu(phy_res->byte_count); - rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*phy_res) + - phy_res->cfg_phy_cnt + len); - ampdu_status = le32_to_cpu(rx_pkt_status); - } else { - if (!priv->last_phy_res_valid) { - IWL_ERR(priv, "MPDU frame without cached PHY data\n"); - return 0; - } - phy_res = &priv->last_phy_res; - amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; - header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); - len = le16_to_cpu(amsdu->byte_count); - rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); - ampdu_status = iwlagn_translate_rx_status(priv, - le32_to_cpu(rx_pkt_status)); + if (!priv->last_phy_res_valid) { + IWL_ERR(priv, "MPDU frame without cached PHY data\n"); + return 0; } + phy_res = &priv->last_phy_res; + amsdu = (struct iwl_rx_mpdu_res_start *)pkt->data; + header = (struct ieee80211_hdr *)(pkt->data + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *)(pkt->data + sizeof(*amsdu) + len); + ampdu_status = iwlagn_translate_rx_status(priv, + le32_to_cpu(rx_pkt_status)); if ((unlikely(phy_res->cfg_phy_cnt > 20))) { IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d\n", -- cgit v1.2.3 From 33a4e985bab25d6753b52d1322b4e2fff706dd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 6 Jun 2012 11:49:23 +0200 Subject: gpio/mxc: make irqs work for fsl,imx21-gpio devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chained handler was set for the platform device with id == 0. When the gpio devices are instantiated by a device tree, all have id == -1 and so the handler was unset resulting in unusable gpio irqs on i.MX21 and i.MX27 (when using oftree). Acked-by: Shawn Guo Cc: Grant Likely Signed-off-by: Uwe Kleine-König Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mxc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index c337143b18f8..c89c4c1e668d 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -398,10 +398,12 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) writel(~0, port->base + GPIO_ISR); if (mxc_gpio_hwtype == IMX21_GPIO) { - /* setup one handler for all GPIO interrupts */ - if (pdev->id == 0) - irq_set_chained_handler(port->irq, - mx2_gpio_irq_handler); + /* + * Setup one handler for all GPIO interrupts. Actually setting + * the handler is needed only once, but doing it for every port + * is more robust and easier. + */ + irq_set_chained_handler(port->irq, mx2_gpio_irq_handler); } else { /* setup one handler for each entry */ irq_set_chained_handler(port->irq, mx3_gpio_irq_handler); -- cgit v1.2.3 From 626f9914a371e22f15135f67db6c2aa64adbdacb Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 4 Jul 2012 20:36:45 +0530 Subject: gpio: tps65910: initialize of_node of gpio_chip Initialize the gpio chip's of_node to the device's node to work with DT based system. Signed-off-by: Laxman Dewangan Signed-off-by: Linus Walleij --- drivers/gpio/gpio-tps65910.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-tps65910.c b/drivers/gpio/gpio-tps65910.c index c1ad2884f2ed..0749f9630869 100644 --- a/drivers/gpio/gpio-tps65910.c +++ b/drivers/gpio/gpio-tps65910.c @@ -149,6 +149,7 @@ static int __devinit tps65910_gpio_probe(struct platform_device *pdev) tps65910_gpio->gpio_chip.set = tps65910_gpio_set; tps65910_gpio->gpio_chip.get = tps65910_gpio_get; tps65910_gpio->gpio_chip.dev = &pdev->dev; + tps65910_gpio->gpio_chip.of_node = tps65910->dev->of_node; if (pdata && pdata->gpio_base) tps65910_gpio->gpio_chip.base = pdata->gpio_base; else -- cgit v1.2.3 From 1d5fcfec22ce5f69db0d29284d2b65ff8ab1bfaa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:12:32 +0200 Subject: PM / Domains: Add device domain data reference counter Add a mechanism for counting references to the struct generic_pm_domain_data object pointed to by dev->power.subsys_data->domain_data if the device in question belongs to a generic PM domain. This change is necessary for a subsequent patch making it possible to allocate that object from within pm_genpd_add_callbacks(), so that drivers can attach their PM domain device callbacks to devices before those devices are added to PM domains. This patch has been tested on the SH7372 Mackerel board. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 83 +++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 4b5f090fccb6..45eb3b155b6d 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -297,7 +297,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, pdd = dev->power.subsys_data ? dev->power.subsys_data->domain_data : NULL; - if (pdd) { + if (pdd && pdd->dev) { to_gpd_data(pdd)->td.constraint_changed = true; genpd = dev_to_genpd(dev); } else { @@ -1266,6 +1266,27 @@ static void pm_genpd_complete(struct device *dev) #endif /* CONFIG_PM_SLEEP */ +static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) +{ + struct generic_pm_domain_data *gpd_data; + + gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); + if (!gpd_data) + return NULL; + + mutex_init(&gpd_data->lock); + gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; + dev_pm_qos_add_notifier(dev, &gpd_data->nb); + return gpd_data; +} + +static void __pm_genpd_free_dev_data(struct device *dev, + struct generic_pm_domain_data *gpd_data) +{ + dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + kfree(gpd_data); +} + /** * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. @@ -1275,7 +1296,7 @@ static void pm_genpd_complete(struct device *dev) int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td) { - struct generic_pm_domain_data *gpd_data; + struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; struct pm_domain_data *pdd; int ret = 0; @@ -1284,14 +1305,10 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) return -EINVAL; - gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); - if (!gpd_data) + gpd_data_new = __pm_genpd_alloc_dev_data(dev); + if (!gpd_data_new) return -ENOMEM; - mutex_init(&gpd_data->lock); - gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; - dev_pm_qos_add_notifier(dev, &gpd_data->nb); - genpd_acquire_lock(genpd); if (genpd->prepared_count > 0) { @@ -1305,35 +1322,42 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, goto out; } + ret = dev_pm_get_subsys_data(dev); + if (ret) + goto out; + genpd->device_count++; genpd->max_off_time_changed = true; - dev_pm_get_subsys_data(dev); - - mutex_lock(&gpd_data->lock); spin_lock_irq(&dev->power.lock); + dev->pm_domain = &genpd->domain; - dev->power.subsys_data->domain_data = &gpd_data->base; - gpd_data->base.dev = dev; - list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); - gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + } else { + gpd_data = gpd_data_new; + dev->power.subsys_data->domain_data = &gpd_data->base; + } + gpd_data->refcount++; if (td) gpd_data->td = *td; + spin_unlock_irq(&dev->power.lock); + + mutex_lock(&gpd_data->lock); + gpd_data->base.dev = dev; + list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); + gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF; gpd_data->td.constraint_changed = true; gpd_data->td.effective_constraint_ns = -1; - spin_unlock_irq(&dev->power.lock); mutex_unlock(&gpd_data->lock); - genpd_release_lock(genpd); - - return 0; - out: genpd_release_lock(genpd); - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - kfree(gpd_data); + if (gpd_data != gpd_data_new) + __pm_genpd_free_dev_data(dev, gpd_data_new); + return ret; } @@ -1379,6 +1403,7 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; + bool remove = false; int ret = 0; dev_dbg(dev, "%s()\n", __func__); @@ -1399,22 +1424,28 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, genpd->max_off_time_changed = true; spin_lock_irq(&dev->power.lock); + dev->pm_domain = NULL; pdd = dev->power.subsys_data->domain_data; list_del_init(&pdd->list_node); - dev->power.subsys_data->domain_data = NULL; + gpd_data = to_gpd_data(pdd); + if (--gpd_data->refcount == 0) { + dev->power.subsys_data->domain_data = NULL; + remove = true; + } + spin_unlock_irq(&dev->power.lock); - gpd_data = to_gpd_data(pdd); mutex_lock(&gpd_data->lock); pdd->dev = NULL; mutex_unlock(&gpd_data->lock); genpd_release_lock(genpd); - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); - kfree(gpd_data); dev_pm_put_subsys_data(dev); + if (remove) + __pm_genpd_free_dev_data(dev, gpd_data); + return 0; out: -- cgit v1.2.3 From 62d4490294e3de7015f715c00af992d7b6a6e22c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:12:54 +0200 Subject: PM / Domains: Allow device callbacks to be added at any time Make it possible to modify device callbacks used by the generic PM domains core code at any time, not only after the device has been added to a domain. This will allow device drivers to provide their own device PM domain callbacks even if they are registered before adding the devices to PM domains. For this purpose, use the observation that the struct generic_pm_domain_data object containing the relevant callback pointers may be allocated by pm_genpd_add_callbacks() and the callbacks may be set before __pm_genpd_add_device() is run for the given device. This object will then be used by __pm_genpd_add_device(), but it has to be protected from premature removal by reference counting. Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 64 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 45eb3b155b6d..9727bc56320a 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1603,33 +1603,52 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, * @dev: Device to add the callbacks to. * @ops: Set of callbacks to add. * @td: Timing data to add to the device along with the callbacks (optional). + * + * Every call to this routine should be balanced with a call to + * __pm_genpd_remove_callbacks() and they must not be nested. */ int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops, struct gpd_timing_data *td) { - struct pm_domain_data *pdd; + struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; int ret = 0; - if (!(dev && dev->power.subsys_data && ops)) + if (!(dev && ops)) return -EINVAL; + gpd_data_new = __pm_genpd_alloc_dev_data(dev); + if (!gpd_data_new) + return -ENOMEM; + pm_runtime_disable(dev); device_pm_lock(); - pdd = dev->power.subsys_data->domain_data; - if (pdd) { - struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); + ret = dev_pm_get_subsys_data(dev); + if (ret) + goto out; + + spin_lock_irq(&dev->power.lock); - gpd_data->ops = *ops; - if (td) - gpd_data->td = *td; + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); } else { - ret = -EINVAL; + gpd_data = gpd_data_new; + dev->power.subsys_data->domain_data = &gpd_data->base; } + gpd_data->refcount++; + gpd_data->ops = *ops; + if (td) + gpd_data->td = *td; + spin_unlock_irq(&dev->power.lock); + + out: device_pm_unlock(); pm_runtime_enable(dev); + if (gpd_data != gpd_data_new) + __pm_genpd_free_dev_data(dev, gpd_data_new); + return ret; } EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); @@ -1638,10 +1657,13 @@ EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks); * __pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device. * @dev: Device to remove the callbacks from. * @clear_td: If set, clear the device's timing data too. + * + * This routine can only be called after pm_genpd_add_callbacks(). */ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) { - struct pm_domain_data *pdd; + struct generic_pm_domain_data *gpd_data = NULL; + bool remove = false; int ret = 0; if (!(dev && dev->power.subsys_data)) @@ -1650,21 +1672,35 @@ int __pm_genpd_remove_callbacks(struct device *dev, bool clear_td) pm_runtime_disable(dev); device_pm_lock(); - pdd = dev->power.subsys_data->domain_data; - if (pdd) { - struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd); + spin_lock_irq(&dev->power.lock); + if (dev->power.subsys_data->domain_data) { + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); gpd_data->ops = (struct gpd_dev_ops){ 0 }; if (clear_td) gpd_data->td = (struct gpd_timing_data){ 0 }; + + if (--gpd_data->refcount == 0) { + dev->power.subsys_data->domain_data = NULL; + remove = true; + } } else { ret = -EINVAL; } + spin_unlock_irq(&dev->power.lock); + device_pm_unlock(); pm_runtime_enable(dev); - return ret; + if (ret) + return ret; + + dev_pm_put_subsys_data(dev); + if (remove) + __pm_genpd_free_dev_data(dev, gpd_data); + + return 0; } EXPORT_SYMBOL_GPL(__pm_genpd_remove_callbacks); -- cgit v1.2.3 From 25ac77613aa8fca131599705e3d7da2a0eaa06a0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Thu, 5 Jul 2012 15:23:25 +0200 Subject: ACPI: intel_idle : break dependency between modules When the system is booted with some cpus offline, the idle driver is not initialized. When a cpu is set online, the acpi code call the intel idle init function. Unfortunately this code introduce a dependency between intel_idle and acpi. This patch is intended to remove this dependency by using the notifier of intel_idle. This patch has the benefit of encapsulating the intel_idle driver and remove some exported functions. Signed-off-by: Daniel Lezcano Acked-by: Srivatsa S. Bhat Signed-off-by: Rafael J. Wysocki --- drivers/acpi/processor_driver.c | 7 ------- drivers/idle/intel_idle.c | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 0734086537b8..8648b29f6eec 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -427,18 +427,11 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb, * Initialize missing things */ if (pr->flags.need_hotplug_init) { - struct cpuidle_driver *idle_driver = - cpuidle_get_driver(); - printk(KERN_INFO "Will online and init hotplugged " "CPU: %d\n", pr->id); WARN(acpi_processor_start(pr), "Failed to start CPU:" " %d\n", pr->id); pr->flags.need_hotplug_init = 0; - if (idle_driver && !strcmp(idle_driver->name, - "intel_idle")) { - intel_idle_cpu_init(pr->id); - } /* Normal CPU soft online event */ } else { acpi_processor_ppc_has_changed(pr, 0); diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d0f59c3f87ef..fe95d5464a02 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -96,6 +96,7 @@ static const struct idle_cpu *icpu; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; static int intel_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); +static int intel_idle_cpu_init(int cpu); static struct cpuidle_state *cpuidle_state_table; @@ -302,22 +303,35 @@ static void __setup_broadcast_timer(void *arg) clockevents_notify(reason, &cpu); } -static int setup_broadcast_cpuhp_notify(struct notifier_block *n, - unsigned long action, void *hcpu) +static int cpu_hotplug_notify(struct notifier_block *n, + unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; + struct cpuidle_device *dev; switch (action & 0xf) { case CPU_ONLINE: - smp_call_function_single(hotcpu, __setup_broadcast_timer, - (void *)true, 1); + + if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) + smp_call_function_single(hotcpu, __setup_broadcast_timer, + (void *)true, 1); + + /* + * Some systems can hotplug a cpu at runtime after + * the kernel has booted, we have to initialize the + * driver in this case + */ + dev = per_cpu_ptr(intel_idle_cpuidle_devices, hotcpu); + if (!dev->registered) + intel_idle_cpu_init(hotcpu); + break; } return NOTIFY_OK; } -static struct notifier_block setup_broadcast_notifier = { - .notifier_call = setup_broadcast_cpuhp_notify, +static struct notifier_block cpu_hotplug_notifier = { + .notifier_call = cpu_hotplug_notify, }; static void auto_demotion_disable(void *dummy) @@ -405,10 +419,10 @@ static int intel_idle_probe(void) if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; - else { + else on_each_cpu(__setup_broadcast_timer, (void *)true, 1); - register_cpu_notifier(&setup_broadcast_notifier); - } + + register_cpu_notifier(&cpu_hotplug_notifier); pr_debug(PREFIX "v" INTEL_IDLE_VERSION " model 0x%X\n", boot_cpu_data.x86_model); @@ -494,7 +508,7 @@ static int intel_idle_cpuidle_driver_init(void) * allocate, initialize, register cpuidle_devices * @cpu: cpu/core to initialize */ -int intel_idle_cpu_init(int cpu) +static int intel_idle_cpu_init(int cpu) { int cstate; struct cpuidle_device *dev; @@ -539,7 +553,6 @@ int intel_idle_cpu_init(int cpu) return 0; } -EXPORT_SYMBOL_GPL(intel_idle_cpu_init); static int __init intel_idle_init(void) { @@ -581,10 +594,10 @@ static void __exit intel_idle_exit(void) intel_idle_cpuidle_devices_uninit(); cpuidle_unregister_driver(&intel_idle_driver); - if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { + + if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) on_each_cpu(__setup_broadcast_timer, (void *)false, 1); - unregister_cpu_notifier(&setup_broadcast_notifier); - } + unregister_cpu_notifier(&cpu_hotplug_notifier); return; } -- cgit v1.2.3 From db288c9c5f9db45610dab3940377625132b4af41 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 15:20:00 -0600 Subject: PCI / PM: restore the original behavior of pci_set_power_state() Commit cc2893b6 (PCI: Ensure we re-enable devices on resume) addressed the problem with USB not being powered after resume on recent Lenovo machines, but it did that in a suboptimal way. Namely, it should have changed the relevant code paths only, which are pci_pm_resume_noirq() and pci_pm_restore_noirq() supposed to restore the device's power and standard configuration registers after system resume from suspend or hibernation. Instead, however, it modified pci_set_power_state() which is executed in several other situations too. That resulted in some undesirable effects, like attempting to change a device's power state in the same way multiple times in a row (up to as many as 4 times in a row in the snd_hda_intel driver). Fix the bug addressed by commit cc2893b6 in an alternative way, by forcibly powering up all devices in pci_pm_default_resume_early(), which is called by pci_pm_resume_noirq() and pci_pm_restore_noirq() to restore the device's power and standard configuration registers, and modifying pci_pm_runtime_resume() to avoid the forcible power-up if not necessary. Then, revert the changes made by commit cc2893b6 to make the confusion introduced by it go away. Acked-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 14 ++++++++------ drivers/pci/pci.c | 19 ++++++++++++++++++- drivers/pci/pci.h | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index ca2e4c79a588..1dd1d9dfa173 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -459,16 +459,17 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev) return 0; } +#endif + +#ifdef CONFIG_PM_SLEEP + static void pci_pm_default_resume_early(struct pci_dev *pci_dev) { - pci_restore_standard_config(pci_dev); + pci_power_up(pci_dev); + pci_restore_state(pci_dev); pci_fixup_device(pci_fixup_resume_early, pci_dev); } -#endif - -#ifdef CONFIG_PM_SLEEP - /* * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (second part). @@ -1054,7 +1055,8 @@ static int pci_pm_runtime_resume(struct device *dev) if (!pm || !pm->runtime_resume) return -ENOSYS; - pci_pm_default_resume_early(pci_dev); + pci_restore_standard_config(pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); __pci_enable_wake(pci_dev, PCI_D0, true, false); pci_fixup_device(pci_fixup_resume, pci_dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8effb9b23eec..acae2705e7f6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -672,6 +672,19 @@ void pci_update_current_state(struct pci_dev *dev, pci_power_t state) } } +/** + * pci_power_up - Put the given device into D0 forcibly + * @dev: PCI device to power up + */ +void pci_power_up(struct pci_dev *dev) +{ + if (platform_pci_power_manageable(dev)) + platform_pci_set_power_state(dev, PCI_D0); + + pci_raw_set_power_state(dev, PCI_D0); + pci_update_current_state(dev, PCI_D0); +} + /** * pci_platform_power_transition - Use platform to change device power state * @dev: PCI device to handle. @@ -762,7 +775,7 @@ int __pci_complete_power_transition(struct pci_dev *dev, pci_power_t state) { int ret; - if (state < PCI_D0) + if (state <= PCI_D0) return -EINVAL; ret = pci_platform_power_transition(dev, state); /* Power off the bridge may power off the whole hierarchy */ @@ -804,6 +817,10 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ return 0; + /* Check if we're already there */ + if (dev->current_state == state) + return 0; + __pci_start_power_transition(dev, state); /* This device is quirked not to be put into D3, so diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5cd3dce7a245..331857855eb4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -67,6 +67,7 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); +extern void pci_power_up(struct pci_dev *dev); extern void pci_disable_enabled_device(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); -- cgit v1.2.3 From 8e16e33c168a6efd0c9f7fa9dd4c1e1db9a74553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 2 Jul 2012 19:53:55 +0200 Subject: USB: option: add ZTE MF60 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switches into a composite device by ejecting the initial driver CD. The four interfaces are: QCDM, AT, QMI/wwan and mass storage. Let this driver manage the two serial interfaces: T: Bus=02 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 28 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=19d2 ProdID=1402 Rev= 0.00 S: Manufacturer=ZTE,Incorporated S: Product=ZTE WCDMA Technologies MSM S: SerialNumber=xxxxx C:* #Ifs= 4 Cfg#= 1 Atr=c0 MxPwr=500mA I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=option E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=qmi_wwan E: Ad=83(I) Atr=03(Int.) MxPS= 64 Ivl=2ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=4ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms Signed-off-by: Bjørn Mork Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index adf8ce72be50..167aed8b2488 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -554,6 +554,10 @@ static const struct option_blacklist_info net_intf1_blacklist = { .reserved = BIT(1), }; +static const struct option_blacklist_info net_intf2_blacklist = { + .reserved = BIT(2), +}; + static const struct option_blacklist_info net_intf3_blacklist = { .reserved = BIT(3), }; @@ -1099,6 +1103,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1298, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1299, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1300, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1402, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf2_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2002, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_k3765_z_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x2003, 0xff, 0xff, 0xff) }, -- cgit v1.2.3 From aacef9c561a693341566a6850c451ce3df68cb9a Mon Sep 17 00:00:00 2001 From: Gaosen Zhang Date: Thu, 5 Jul 2012 21:49:00 +0800 Subject: USB: option: Add MEDIATEK product ids Signed-off-by: Gaosen Zhang Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 167aed8b2488..417ab1b0aa30 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -497,6 +497,15 @@ static void option_instat_callback(struct urb *urb); /* MediaTek products */ #define MEDIATEK_VENDOR_ID 0x0e8d +#define MEDIATEK_PRODUCT_DC_1COM 0x00a0 +#define MEDIATEK_PRODUCT_DC_4COM 0x00a5 +#define MEDIATEK_PRODUCT_DC_5COM 0x00a4 +#define MEDIATEK_PRODUCT_7208_1COM 0x7101 +#define MEDIATEK_PRODUCT_7208_2COM 0x7102 +#define MEDIATEK_PRODUCT_FP_1COM 0x0003 +#define MEDIATEK_PRODUCT_FP_2COM 0x0023 +#define MEDIATEK_PRODUCT_FPDC_1COM 0x0043 +#define MEDIATEK_PRODUCT_FPDC_2COM 0x0033 /* Cellient products */ #define CELLIENT_VENDOR_ID 0x2692 @@ -1246,6 +1255,17 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a1, 0xff, 0x02, 0x01) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a2, 0xff, 0x00, 0x00) }, { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, 0x00a2, 0xff, 0x02, 0x01) }, /* MediaTek MT6276M modem & app port */ + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_1COM, 0x0a, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_5COM, 0xff, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_5COM, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM, 0xff, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_DC_4COM, 0xff, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7208_1COM, 0x02, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_7208_2COM, 0x02, 0x02, 0x01) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_1COM, 0x0a, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FP_2COM, 0x0a, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_1COM, 0x0a, 0x00, 0x00) }, + { USB_DEVICE_AND_INTERFACE_INFO(MEDIATEK_VENDOR_ID, MEDIATEK_PRODUCT_FPDC_2COM, 0x0a, 0x00, 0x00) }, { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) }, { } /* Terminating entry */ }; -- cgit v1.2.3 From b7d28e32c93801d60c1a7a817f774a02b7bdde43 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 2 Jul 2012 12:34:24 +0200 Subject: USB: metro-usb: fix tty_flip_buffer_push use Do not set low_latency flag at open as tty_flip_buffer_push must not be called in IRQ context with low_latency set. Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/metro-usb.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 81423f7361db..d47eb06fe463 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -222,14 +222,6 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) metro_priv->throttled = 0; spin_unlock_irqrestore(&metro_priv->lock, flags); - /* - * Force low_latency on so that our tty_push actually forces the data - * through, otherwise it is scheduled, and with high data rates (like - * with OHCI) data can get lost. - */ - if (tty) - tty->low_latency = 1; - /* Clear the urb pipe. */ usb_clear_halt(serial->dev, port->interrupt_in_urb->pipe); -- cgit v1.2.3 From b086b6b10d9f182cd8d2f0dcfd7fd11edba93fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Mon, 2 Jul 2012 10:33:14 +0200 Subject: USB: cdc-wdm: fix lockup on error in wdm_read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear the WDM_READ flag on empty reads to avoid running forever in an infinite tight loop, causing lockups: Jul 1 21:58:11 nemi kernel: [ 3658.898647] qmi_wwan 2-1:1.2: Unexpected error -71 Jul 1 21:58:36 nemi kernel: [ 3684.072021] BUG: soft lockup - CPU#0 stuck for 23s! [qmi.pl:12235] Jul 1 21:58:36 nemi kernel: [ 3684.072212] CPU 0 Jul 1 21:58:36 nemi kernel: [ 3684.072355] Jul 1 21:58:36 nemi kernel: [ 3684.072367] Pid: 12235, comm: qmi.pl Tainted: P O 3.5.0-rc2+ #13 LENOVO 2776LEG/2776LEG Jul 1 21:58:36 nemi kernel: [ 3684.072383] RIP: 0010:[] [] spin_unlock_irq+0x8/0xc [cdc_wdm] Jul 1 21:58:36 nemi kernel: [ 3684.072388] RSP: 0018:ffff88022dca1e70 EFLAGS: 00000282 Jul 1 21:58:36 nemi kernel: [ 3684.072393] RAX: ffff88022fc3f650 RBX: ffffffff811c56f7 RCX: 00000001000ce8c1 Jul 1 21:58:36 nemi kernel: [ 3684.072398] RDX: 0000000000000010 RSI: 000000000267d810 RDI: ffff88022fc3f650 Jul 1 21:58:36 nemi kernel: [ 3684.072403] RBP: ffff88022dca1eb0 R08: ffffffffa063578e R09: 0000000000000000 Jul 1 21:58:36 nemi kernel: [ 3684.072407] R10: 0000000000000008 R11: 0000000000000246 R12: 0000000000000002 Jul 1 21:58:36 nemi kernel: [ 3684.072412] R13: 0000000000000246 R14: ffffffff00000002 R15: ffff8802281d8c88 Jul 1 21:58:36 nemi kernel: [ 3684.072418] FS: 00007f666a260700(0000) GS:ffff88023bc00000(0000) knlGS:0000000000000000 Jul 1 21:58:36 nemi kernel: [ 3684.072423] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 Jul 1 21:58:36 nemi kernel: [ 3684.072428] CR2: 000000000270d9d8 CR3: 000000022e865000 CR4: 00000000000007f0 Jul 1 21:58:36 nemi kernel: [ 3684.072433] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 Jul 1 21:58:36 nemi kernel: [ 3684.072438] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Jul 1 21:58:36 nemi kernel: [ 3684.072444] Process qmi.pl (pid: 12235, threadinfo ffff88022dca0000, task ffff88022ff76380) Jul 1 21:58:36 nemi kernel: [ 3684.072448] Stack: Jul 1 21:58:36 nemi kernel: [ 3684.072458] ffffffffa063592e 0000000100020000 ffff88022fc3f650 ffff88022fc3f6a8 Jul 1 21:58:36 nemi kernel: [ 3684.072466] 0000000000000200 0000000100000000 000000000267d810 0000000000000000 Jul 1 21:58:36 nemi kernel: [ 3684.072475] 0000000000000000 ffff880212cfb6d0 0000000000000200 ffff880212cfb6c0 Jul 1 21:58:36 nemi kernel: [ 3684.072479] Call Trace: Jul 1 21:58:36 nemi kernel: [ 3684.072489] [] ? wdm_read+0x1a0/0x263 [cdc_wdm] Jul 1 21:58:36 nemi kernel: [ 3684.072500] [] ? vfs_read+0xa1/0xfb Jul 1 21:58:36 nemi kernel: [ 3684.072509] [] ? alarm_setitimer+0x35/0x64 Jul 1 21:58:36 nemi kernel: [ 3684.072517] [] ? sys_read+0x45/0x6e Jul 1 21:58:36 nemi kernel: [ 3684.072525] [] ? system_call_fastpath+0x16/0x1b Jul 1 21:58:36 nemi kernel: [ 3684.072557] Code: <66> 66 90 c3 83 ff ed 89 f8 74 16 7f 06 83 ff a1 75 0a c3 83 ff f4 The WDM_READ flag is normally cleared by wdm_int_callback before resubmitting the read urb, and set by wdm_in_callback when this urb returns with data or an error. But a crashing device may cause both a read error and cancelling all urbs. Make sure that the flag is cleared by wdm_read if the buffer is empty. We don't clear the flag on errors, as there may be pending data in the buffer which should be processed. The flag will instead be cleared on the next wdm_read call. Cc: stable Signed-off-by: Bjørn Mork Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 8fd398dffced..ee469274a3fe 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -500,6 +500,8 @@ retry: goto retry; } if (!desc->reslength) { /* zero length read */ + dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); + clear_bit(WDM_READ, &desc->flags); spin_unlock_irq(&desc->iuspin); goto retry; } -- cgit v1.2.3 From 700db99d0140e9da2a31e08ebd3e1b121691aa26 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 5 Jul 2012 21:08:05 -0700 Subject: ipoib: Need to do dst_neigh_lookup_skb() outside of priv->lock. Otherwise local_bh_enable() complains. Reported-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index fbb95ee538b2..7cecb16d3d48 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -658,9 +658,15 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast) void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb) { struct ipoib_dev_priv *priv = netdev_priv(dev); + struct dst_entry *dst = skb_dst(skb); struct ipoib_mcast *mcast; + struct neighbour *n; unsigned long flags; + n = NULL; + if (dst) + n = dst_neigh_lookup_skb(dst, skb); + spin_lock_irqsave(&priv->lock, flags); if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags) || @@ -715,12 +721,6 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb) out: if (mcast && mcast->ah) { - struct dst_entry *dst = skb_dst(skb); - struct neighbour *n = NULL; - - rcu_read_lock(); - if (dst) - n = dst_neigh_lookup_skb(dst, skb); if (n) { if (!*to_ipoib_neigh(n)) { struct ipoib_neigh *neigh; @@ -735,13 +735,14 @@ out: } neigh_release(n); } - rcu_read_unlock(); spin_unlock_irqrestore(&priv->lock, flags); ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); return; } unlock: + if (n) + neigh_release(n); spin_unlock_irqrestore(&priv->lock, flags); } -- cgit v1.2.3 From a3a7cab17d36bb591867a6d3ea41b5fdb4e44d87 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 27 Jun 2012 09:44:45 +0530 Subject: of: Fix null pointer related warnings in base.c file Fixes the following sparse warnings: drivers/of/base.c:176:41: warning: Using plain integer as NULL pointer drivers/of/base.c:178:37: warning: Using plain integer as NULL pointer drivers/of/base.c:500:49: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Signed-off-by: Rob Herring --- drivers/of/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index d9bfd49b1935..ddba57799fd7 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -173,9 +173,9 @@ struct property *of_find_property(const struct device_node *np, return NULL; read_lock(&devtree_lock); - for (pp = np->properties; pp != 0; pp = pp->next) { + for (pp = np->properties; pp; pp = pp->next) { if (of_prop_cmp(pp->name, name) == 0) { - if (lenp != 0) + if (lenp) *lenp = pp->length; break; } @@ -497,7 +497,7 @@ struct device_node *of_find_node_with_property(struct device_node *from, read_lock(&devtree_lock); np = from ? from->allnext : allnodes; for (; np; np = np->allnext) { - for (pp = np->properties; pp != 0; pp = pp->next) { + for (pp = np->properties; pp; pp = pp->next) { if (of_prop_cmp(pp->name, prop_name) == 0) { of_node_get(np); goto out; -- cgit v1.2.3 From 1af4c7f18a442771d86d496c761e9816349f98c9 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 29 Jun 2012 13:57:58 +0900 Subject: of: return -ENOENT when no property Make of_parse_phandle_with_args return -ENOENT instead of -EINVAL when no matching property is found, which allows to discriminate between absence of property and parsing error. Signed-off-by: Alexandre Courbot Signed-off-by: Rob Herring --- drivers/of/base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index ddba57799fd7..85757952f12d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -902,7 +902,7 @@ int of_parse_phandle_with_args(struct device_node *np, const char *list_name, /* Retrieve the phandle list property */ list = of_get_property(np, list_name, &size); if (!list) - return -EINVAL; + return -ENOENT; list_end = list + size / sizeof(*list); /* Loop over the phandles until all the requested entry is found */ -- cgit v1.2.3 From 74a7f08448adea6cb47cd9b260c98ff168117e92 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 15 Jun 2012 11:50:25 -0600 Subject: devicetree: add helper inline for retrieving a node's full name The pattern (np ? np->full_name : "") is rather common in the kernel, but can also make for quite long lines. This patch adds a new inline function, of_node_full_name() so that the test for a valid node pointer doesn't need to be open coded at all call sites. Signed-off-by: Grant Likely Cc: Paul Mundt Cc: Benjamin Herrenschmidt Cc: Thomas Gleixner Signed-off-by: Rob Herring --- drivers/of/base.c | 2 +- drivers/of/irq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/of/base.c b/drivers/of/base.c index 85757952f12d..9ec0a2f1b028 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1173,7 +1173,7 @@ static void of_alias_add(struct alias_prop *ap, struct device_node *np, ap->stem[stem_len] = 0; list_add_tail(&ap->link, &aliases_lookup); pr_debug("adding DT alias:%s: stem=%s id=%i node=%s\n", - ap->alias, ap->stem, ap->id, np ? np->full_name : NULL); + ap->alias, ap->stem, ap->id, of_node_full_name(np)); } /** diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9cf00602f566..ff8ab7b27373 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -255,7 +255,7 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec, skiplevel: /* Iterate again with new parent */ - pr_debug(" -> new parent: %s\n", newpar ? newpar->full_name : "<>"); + pr_debug(" -> new parent: %s\n", of_node_full_name(newpar)); of_node_put(ipar); ipar = newpar; newpar = NULL; -- cgit v1.2.3 From 1044c180de4ba426aa5fb1b4b13b5f219ddb7105 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 6 Jul 2012 14:10:23 +0100 Subject: regmap: Add hook for printk logging for debugging during early init Sometimes for failures during very early init the trace infrastructure isn't available early enough to be used. For this sort of problem defining LOG_DEVICE will add printks for basic register I/O on a specific device, allowing trace to be extracted when the trace system doesn't come up early enough to work with. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 1cf721421bec..f8a937654af2 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -21,6 +21,18 @@ #include "internal.h" +/* + * Sometimes for failures during very early init the trace + * infrastructure isn't available early enough to be used. For this + * sort of problem defining LOG_DEVICE will add printks for basic + * register I/O on a specific device. + */ +#undef LOG_DEVICE + +static int _regmap_update_bits(struct regmap *map, unsigned int reg, + unsigned int mask, unsigned int val, + bool *change); + bool regmap_writeable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) @@ -700,6 +712,11 @@ int _regmap_write(struct regmap *map, unsigned int reg, } } +#ifdef LOG_DEVICE + if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) + dev_info(map->dev, "%x <= %x\n", reg, val); +#endif + trace_regmap_reg_write(map->dev, reg, val); if (map->format.format_write) { @@ -903,6 +920,12 @@ static int _regmap_read(struct regmap *map, unsigned int reg, ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); if (ret == 0) { *val = map->format.parse_val(map->work_buf); + +#ifdef LOG_DEVICE + if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) + dev_info(map->dev, "%x => %x\n", reg, *val); +#endif + trace_regmap_reg_read(map->dev, reg, *val); } -- cgit v1.2.3 From 48f8b641297df49021093763a3271119a84990a2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 9 Jun 2012 19:08:25 +0300 Subject: mtd: cafe_nand: fix an & vs | mistake The intent here was clearly to set result to true if the 0x40000000 flag was set. But instead there was a | vs & typo and we always set result to true. Artem: check the spec at wiki.laptop.org/images/5/5c/88ALP01_Datasheet_July_2007.pdf and this fix looks correct. Signed-off-by: Dan Carpenter Cc: stable@vger.kernel.org Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 41371ba1a811..f3f6cfedd69e 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -102,7 +102,7 @@ static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; static int cafe_device_ready(struct mtd_info *mtd) { struct cafe_priv *cafe = mtd->priv; - int result = !!(cafe_readl(cafe, NAND_STATUS) | 0x40000000); + int result = !!(cafe_readl(cafe, NAND_STATUS) & 0x40000000); uint32_t irqs = cafe_readl(cafe, NAND_IRQ); cafe_writel(cafe, irqs, NAND_IRQ); -- cgit v1.2.3 From 4d6d0ae2a088e1e054ef6d96ceb1b41523291e71 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 4 Jul 2012 12:58:40 +0200 Subject: mac80211_hwsim: add testmode code to stop/wake queues This was useful for debugging the queue stop/wake issues and is pretty small so let's just put it in. Signed-off-by: Johannes Berg --- drivers/net/wireless/mac80211_hwsim.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index c68728c6c6fa..3f38d846b093 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1082,6 +1082,8 @@ enum hwsim_testmode_attr { enum hwsim_testmode_cmd { HWSIM_TM_CMD_SET_PS = 0, HWSIM_TM_CMD_GET_PS = 1, + HWSIM_TM_CMD_STOP_QUEUES = 2, + HWSIM_TM_CMD_WAKE_QUEUES = 3, }; static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { @@ -1121,6 +1123,12 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, if (nla_put_u32(skb, HWSIM_TM_ATTR_PS, hwsim->ps)) goto nla_put_failure; return cfg80211_testmode_reply(skb); + case HWSIM_TM_CMD_STOP_QUEUES: + ieee80211_stop_queues(hw); + return 0; + case HWSIM_TM_CMD_WAKE_QUEUES: + ieee80211_wake_queues(hw); + return 0; default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 096bcc231fd263bc8df215f0d616b08e3696c6db Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 29 May 2012 10:16:09 +0200 Subject: mtd: mxc_nand: use 32bit copy functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following commit changes the function used to copy from/to the hardware buffer to memcpy_[from|to]io. This does not work since the hardware cannot handle the byte accesses used by these functions. Instead of reverting this patch introduce 32bit correspondents of these functions. | commit 5775ba36ea9c760c2d7e697dac04f2f7fc95aa62 | Author: Uwe Kleine-König | Date: Tue Apr 24 10:05:22 2012 +0200 | | mtd: mxc_nand: fix several sparse warnings about incorrect address space | | Signed-off-by: Uwe Kleine-König | Signed-off-by: Artem Bityutskiy Signed-off-by: Sascha Hauer Cc: Uwe Kleine-König Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index c58e6a93f445..6acc790c2fbb 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -273,6 +273,26 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { static const char *part_probes[] = { "RedBoot", "cmdlinepart", "ofpart", NULL }; +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + *t++ = __raw_readl(s++); +} + +static void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + int i; + u32 __iomem *t = trg; + const u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + __raw_writel(*s++, t++); +} + static int check_int_v3(struct mxc_nand_host *host) { uint32_t tmp; @@ -519,7 +539,7 @@ static void send_read_id_v3(struct mxc_nand_host *host) wait_op_done(host, true); - memcpy_fromio(host->data_buf, host->main_area0, 16); + memcpy32_fromio(host->data_buf, host->main_area0, 16); } /* Request the NANDFC to perform a read of the NAND device ID. */ @@ -535,7 +555,7 @@ static void send_read_id_v1_v2(struct mxc_nand_host *host) /* Wait for operation to complete */ wait_op_done(host, true); - memcpy_fromio(host->data_buf, host->main_area0, 16); + memcpy32_fromio(host->data_buf, host->main_area0, 16); if (this->options & NAND_BUSWIDTH_16) { /* compress the ID info */ @@ -797,16 +817,16 @@ static void copy_spare(struct mtd_info *mtd, bool bfrom) if (bfrom) { for (i = 0; i < n - 1; i++) - memcpy_fromio(d + i * j, s + i * t, j); + memcpy32_fromio(d + i * j, s + i * t, j); /* the last section */ - memcpy_fromio(d + i * j, s + i * t, mtd->oobsize - i * j); + memcpy32_fromio(d + i * j, s + i * t, mtd->oobsize - i * j); } else { for (i = 0; i < n - 1; i++) - memcpy_toio(&s[i * t], &d[i * j], j); + memcpy32_toio(&s[i * t], &d[i * j], j); /* the last section */ - memcpy_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j); + memcpy32_toio(&s[i * t], &d[i * j], mtd->oobsize - i * j); } } @@ -1070,7 +1090,8 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, host->devtype_data->send_page(mtd, NFC_OUTPUT); - memcpy_fromio(host->data_buf, host->main_area0, mtd->writesize); + memcpy32_fromio(host->data_buf, host->main_area0, + mtd->writesize); copy_spare(mtd, true); break; @@ -1086,7 +1107,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, break; case NAND_CMD_PAGEPROG: - memcpy_toio(host->main_area0, host->data_buf, mtd->writesize); + memcpy32_toio(host->main_area0, host->data_buf, mtd->writesize); copy_spare(mtd, false); host->devtype_data->send_page(mtd, NFC_INPUT); host->devtype_data->send_cmd(host, command, true); -- cgit v1.2.3 From 6023813a2d5949ba368e7df464f2ccb649719777 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 26 Jun 2012 17:26:16 +0200 Subject: mtd: gpmi-nand: fix read page when reading to vmalloced area The gpmi-nand driver uses virt_addr_valid() to check whether a buffer is suitable for dma. If it's not, a driver allocated buffer is used instead. Then after a page read the driver allocated buffer must be copied to the user supplied buffer. This does not happen since commit 7725cc85932bd02dd12c23108e0ef748c551ccba. This patch fixes the issue. The bug is encountered with UBI which uses a vmalloced buffer for the volume table. Signed-off-by: Sascha Hauer Tested-by: snijsure@grid-net.com Acked-by: Huang Shijie Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index a05b7b444d4f..a6cad5caba78 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -920,12 +920,12 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, */ memset(chip->oob_poi, ~0, mtd->oobsize); chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; - - read_page_swap_end(this, buf, mtd->writesize, - this->payload_virt, this->payload_phys, - nfc_geo->payload_size, - payload_virt, payload_phys); } + + read_page_swap_end(this, buf, mtd->writesize, + this->payload_virt, this->payload_phys, + nfc_geo->payload_size, + payload_virt, payload_phys); exit_nfc: return ret; } -- cgit v1.2.3 From 596fd46268634082314b3af1ded4612e1b7f3f03 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Wed, 16 May 2012 16:21:52 -0300 Subject: mtd: nandsim: don't open code a do_div helper We don't need to open code the divide function, just use div_u64 that already exists and do the same job. While this is a straightforward clean up, there is more to that, the real motivation for this. While building on a cross compiling environment in armel, using gcc 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5), I was getting the following build error: ERROR: "__aeabi_uldivmod" [drivers/mtd/nand/nandsim.ko] undefined! After investigating with objdump and hand built assembly version generated with the compiler, I narrowed __aeabi_uldivmod as being generated from the divide function. When nandsim.c is built with -fno-inline-functions-called-once, that happens when CONFIG_DEBUG_SECTION_MISMATCH is enabled, the do_div optimization in arch/arm/include/asm/div64.h doesn't work as expected with the open coded divide function: even if the do_div we are using doesn't have a constant divisor, the compiler still includes the else parts of the optimized do_div macro, and translates the divisions there to use __aeabi_uldivmod, instead of only calling __do_div_asm -> __do_div64 and optimizing/removing everything else out. So to reproduce, gcc 4.6 plus CONFIG_DEBUG_SECTION_MISMATCH=y and CONFIG_MTD_NAND_NANDSIM=m should do it, building on armel. After this change, the compiler does the intended thing even with -fno-inline-functions-called-once, and optimizes out as expected the constant handling in the optimized do_div on arm. As this also avoids a build issue, I'm marking for Stable, as I think is applicable for this case. Signed-off-by: Herton Ronaldo Krzesinski Cc: stable@vger.kernel.org Acked-by: Nicolas Pitre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 6cc8fbfabb8e..cf0cd3146817 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include @@ -546,12 +546,6 @@ static char *get_partition_name(int i) return kstrdup(buf, GFP_KERNEL); } -static uint64_t divide(uint64_t n, uint32_t d) -{ - do_div(n, d); - return n; -} - /* * Initialize the nandsim structure. * @@ -580,7 +574,7 @@ static int init_nandsim(struct mtd_info *mtd) ns->geom.oobsz = mtd->oobsize; ns->geom.secsz = mtd->erasesize; ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; - ns->geom.pgnum = divide(ns->geom.totsz, ns->geom.pgsz); + ns->geom.pgnum = div_u64(ns->geom.totsz, ns->geom.pgsz); ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; @@ -921,7 +915,7 @@ static int setup_wear_reporting(struct mtd_info *mtd) if (!rptwear) return 0; - wear_eb_count = divide(mtd->size, mtd->erasesize); + wear_eb_count = div_u64(mtd->size, mtd->erasesize); mem = wear_eb_count * sizeof(unsigned long); if (mem / sizeof(unsigned long) != wear_eb_count) { NS_ERR("Too many erase blocks for wear reporting\n"); -- cgit v1.2.3 From 9033132ddff7d096f3e54704c4250384c5ab3aa2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 6 Jul 2012 19:06:19 +0200 Subject: msi-laptop: Use struct dev_pm_ops for power management Make the msi-laptop driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki Acked-by: Lee, Chun-Yi --- drivers/platform/x86/msi-laptop.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index bb5132128b33..f64441844317 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -85,7 +85,8 @@ #define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4 #define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4) -static int msi_laptop_resume(struct platform_device *device); +static int msi_laptop_resume(struct device *device); +static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume); #define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f @@ -437,8 +438,8 @@ static struct platform_driver msipf_driver = { .driver = { .name = "msi-laptop-pf", .owner = THIS_MODULE, + .pm = &msi_laptop_pm, }, - .resume = msi_laptop_resume, }; static struct platform_device *msipf_device; @@ -752,7 +753,7 @@ err_bluetooth: return retval; } -static int msi_laptop_resume(struct platform_device *device) +static int msi_laptop_resume(struct device *device) { u8 data; int result; -- cgit v1.2.3 From 156ffcb42a3b3fbebbb7f0e08e679b0954c0829c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 5 Jul 2012 22:11:33 +0200 Subject: mg_disk: Use struct dev_pm_ops for power management Make the mg_disk driver define its PM callbacks through a struct dev_pm_ops object rather than by using legacy PM hooks in struct platform_driver. Signed-off-by: Rafael J. Wysocki --- drivers/block/mg_disk.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/block/mg_disk.c b/drivers/block/mg_disk.c index 76fa3deaee84..1788f491e0fb 100644 --- a/drivers/block/mg_disk.c +++ b/drivers/block/mg_disk.c @@ -780,9 +780,9 @@ static const struct block_device_operations mg_disk_ops = { .getgeo = mg_getgeo }; -static int mg_suspend(struct platform_device *plat_dev, pm_message_t state) +static int mg_suspend(struct device *dev) { - struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_drv_data *prv_data = dev->platform_data; struct mg_host *host = prv_data->host; if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) @@ -804,9 +804,9 @@ static int mg_suspend(struct platform_device *plat_dev, pm_message_t state) return 0; } -static int mg_resume(struct platform_device *plat_dev) +static int mg_resume(struct device *dev) { - struct mg_drv_data *prv_data = plat_dev->dev.platform_data; + struct mg_drv_data *prv_data = dev->platform_data; struct mg_host *host = prv_data->host; if (mg_wait(host, MG_STAT_READY, MG_TMAX_CONF_TO_CMD)) @@ -825,6 +825,8 @@ static int mg_resume(struct platform_device *plat_dev) return 0; } +static SIMPLE_DEV_PM_OPS(mg_pm, mg_suspend, mg_resume); + static int mg_probe(struct platform_device *plat_dev) { struct mg_host *host; @@ -1074,11 +1076,10 @@ static int mg_remove(struct platform_device *plat_dev) static struct platform_driver mg_disk_driver = { .probe = mg_probe, .remove = mg_remove, - .suspend = mg_suspend, - .resume = mg_resume, .driver = { .name = MG_DEV_NAME, .owner = THIS_MODULE, + .pm = &mg_pm, } }; -- cgit v1.2.3 From 86f5fcfc3e400b2ac1562cb0fd6aabc9f83ee3e2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 6 Jul 2012 18:19:13 +0100 Subject: regulator: core: Mark all DT based boards as having full constraints Since DT doesn't provide an idiomatic mechanism for enabling full constraints and since it's much more natural with DT to provide them just assume that a DT enabled system has full constraints. Signed-off-by: Mark Brown Acked-by: Liam Girdwood --- drivers/regulator/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 09a737c868b5..4c53b39b92a6 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3472,6 +3472,15 @@ static int __init regulator_init_complete(void) struct regulation_constraints *c; int enabled, ret; + /* + * Since DT doesn't provide an idiomatic mechanism for + * enabling full constraints and since it's much more natural + * with DT to provide them just assume that a DT enabled + * system has full constraints. + */ + if (of_have_populated_dt()) + has_full_constraints = true; + mutex_lock(®ulator_list_mutex); /* If we have a full configuration then disable any regulators -- cgit v1.2.3 From 19228a6a59250d414824ae07e06ad057a404ea3e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Fri, 6 Jul 2012 14:13:12 +0530 Subject: regulator: tps65910: add support for input supply There is multiple voltage input pins on device which takes the voltage input for different voltage regulator. Support to configure the voltage input supplied by different regulator for each regulators. Signed-off-by: Laxman Dewangan Acked-by: Stephen Warren Signed-off-by: Mark Brown --- drivers/regulator/tps65910-regulator.c | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index a534e0872006..e319d963fee6 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -85,6 +85,7 @@ static const unsigned int VMMC_VSEL_table[] = { struct tps_info { const char *name; + const char *vin_name; u8 n_voltages; const unsigned int *voltage_table; int enable_time_us; @@ -93,20 +94,24 @@ struct tps_info { static struct tps_info tps65910_regs[] = { { .name = "vrtc", + .vin_name = "vcc7", .enable_time_us = 2200, }, { .name = "vio", + .vin_name = "vccio", .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", + .vin_name = "vcc1", .enable_time_us = 350, }, { .name = "vdd2", + .vin_name = "vcc2", .enable_time_us = 350, }, { @@ -117,48 +122,56 @@ static struct tps_info tps65910_regs[] = { }, { .name = "vdig1", + .vin_name = "vcc6", .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table), .voltage_table = VDIG1_VSEL_table, .enable_time_us = 100, }, { .name = "vdig2", + .vin_name = "vcc6", .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table), .voltage_table = VDIG2_VSEL_table, .enable_time_us = 100, }, { .name = "vpll", + .vin_name = "vcc5", .n_voltages = ARRAY_SIZE(VPLL_VSEL_table), .voltage_table = VPLL_VSEL_table, .enable_time_us = 100, }, { .name = "vdac", + .vin_name = "vcc5", .n_voltages = ARRAY_SIZE(VDAC_VSEL_table), .voltage_table = VDAC_VSEL_table, .enable_time_us = 100, }, { .name = "vaux1", + .vin_name = "vcc4", .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table), .voltage_table = VAUX1_VSEL_table, .enable_time_us = 100, }, { .name = "vaux2", + .vin_name = "vcc4", .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table), .voltage_table = VAUX2_VSEL_table, .enable_time_us = 100, }, { .name = "vaux33", + .vin_name = "vcc3", .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table), .voltage_table = VAUX33_VSEL_table, .enable_time_us = 100, }, { .name = "vmmc", + .vin_name = "vcc3", .n_voltages = ARRAY_SIZE(VMMC_VSEL_table), .voltage_table = VMMC_VSEL_table, .enable_time_us = 100, @@ -168,21 +181,25 @@ static struct tps_info tps65910_regs[] = { static struct tps_info tps65911_regs[] = { { .name = "vrtc", + .vin_name = "vcc7", .enable_time_us = 2200, }, { .name = "vio", + .vin_name = "vccio", .n_voltages = ARRAY_SIZE(VIO_VSEL_table), .voltage_table = VIO_VSEL_table, .enable_time_us = 350, }, { .name = "vdd1", + .vin_name = "vcc1", .n_voltages = 73, .enable_time_us = 350, }, { .name = "vdd2", + .vin_name = "vcc2", .n_voltages = 73, .enable_time_us = 350, }, @@ -193,41 +210,49 @@ static struct tps_info tps65911_regs[] = { }, { .name = "ldo1", + .vin_name = "vcc6", .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo2", + .vin_name = "vcc6", .n_voltages = 47, .enable_time_us = 420, }, { .name = "ldo3", + .vin_name = "vcc5", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo4", + .vin_name = "vcc5", .n_voltages = 47, .enable_time_us = 230, }, { .name = "ldo5", + .vin_name = "vcc4", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo6", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo7", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, { .name = "ldo8", + .vin_name = "vcc3", .n_voltages = 24, .enable_time_us = 230, }, @@ -1013,6 +1038,9 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( *tps65910_reg_matches = matches; for (idx = 0; idx < count; idx++) { + struct tps_info *info = matches[idx].driver_data; + char in_supply[32]; /* 32 is max size of property name */ + if (!matches[idx].init_data || !matches[idx].of_node) continue; @@ -1023,6 +1051,13 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( "ti,regulator-ext-sleep-control", &prop); if (!ret) pmic_plat_data->regulator_ext_sleep_control[idx] = prop; + + if (info->vin_name) { + snprintf(in_supply, 32, "%s-supply", info->vin_name); + if (of_find_property(np, in_supply, 0)) + pmic_plat_data->input_supply[idx] = + info->vin_name; + } } return pmic_plat_data; @@ -1126,6 +1161,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) pmic->info[i] = info; pmic->desc[i].name = info->name; + pmic->desc[i].supply_name = pmic_plat_data->input_supply[i]; pmic->desc[i].id = i; pmic->desc[i].n_voltages = info->n_voltages; pmic->desc[i].enable_time = info->enable_time_us; -- cgit v1.2.3 From d1ef065d43476fd0e245cad2ef1d226e7cb6324b Mon Sep 17 00:00:00 2001 From: Yadwinder Singh Brar Date: Fri, 6 Jul 2012 14:50:08 +0530 Subject: regulator: max77686: Initialise rmatch.of_node to NULL. Now of_regulator_match() returns without finding the match if match->of_node is not NULL. Signed-off-by: Yadwinder Singh Brar Signed-off-by: Mark Brown --- drivers/regulator/max77686.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index fc695eb3886e..c564af6f05a3 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -262,6 +262,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, for (i = 0; i < pdata->num_regulators; i++) { rmatch.name = regulators[i].name; rmatch.init_data = NULL; + rmatch.of_node = NULL; of_regulator_match(iodev->dev, regulators_np, &rmatch, 1); rdata[i].initdata = rmatch.init_data; } -- cgit v1.2.3 From 84774e615749acfce6b2f679b95924dffb5f5de1 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 5 Jul 2012 15:15:36 +0100 Subject: of: address: Don't fail a lookup just because a node has no reg property Sometimes it doesn't make any sense for a node to have an address. In this case device lookup will always be unsuccessful because we currently assume every node will have a reg property. This patch changes the semantics so that the resource address and the lookup address will only be compared if one exists. Things like AUXDATA() rely on of_dev_lookup to return the lookup entry of a particular device in order to do things like apply platform_data to a device. However, this is currently broken for nodes which do not have a reg property, meaning that platform_data can not be passed in those cases. Acked-by: Rob Herring Acked-by: Arnd Bergmann Signed-off-by: Lee Jones --- drivers/of/platform.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 343ad29e211c..96004807dd28 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -317,10 +317,9 @@ static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *l for(; lookup->compatible != NULL; lookup++) { if (!of_device_is_compatible(np, lookup->compatible)) continue; - if (of_address_to_resource(np, 0, &res)) - continue; - if (res.start != lookup->phys_addr) - continue; + if (!of_address_to_resource(np, 0, &res)) + if (res.start != lookup->phys_addr) + continue; pr_debug("%s: devname=%s\n", np->full_name, lookup->name); return lookup; } -- cgit v1.2.3 From 863555be0c81558b1af277addcf68acb8f778860 Mon Sep 17 00:00:00 2001 From: Mark Rustad Date: Tue, 26 Jun 2012 15:57:30 -0700 Subject: tcm_fc: Resolve suspicious RCU usage warnings Use rcu_dereference_protected to tell rcu that the ft_lport_lock is held during ft_lport_create. This resolved "suspicious RCU usage" warnings when debugging options are turned on. Signed-off-by: Mark Rustad Tested-by: Ross Brattain Cc: Signed-off-by: Nicholas Bellinger --- drivers/target/tcm_fc/tfc_sess.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index cb99da920068..87901fa74dd7 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -58,7 +58,8 @@ static struct ft_tport *ft_tport_create(struct fc_lport *lport) struct ft_tport *tport; int i; - tport = rcu_dereference(lport->prov[FC_TYPE_FCP]); + tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP], + lockdep_is_held(&ft_lport_lock)); if (tport && tport->tpg) return tport; -- cgit v1.2.3 From 4f1d0f1971ba37010603a3a5c763f398b888d2f4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 4 Jul 2012 16:35:35 +0100 Subject: qla2xxx: print the right array elements in qlt_async_event Based upon Alan's patch from Coverity scan id 793583, these debug messages in qlt_async_event() should be starting from byte 0, which is always the Asynchronous Event Status Code from the parent switch statement. Also, rename reason_code -> login_code following the language used in 2500 FW spec for Port Database Changed (0x8014) -> Port Database Changed Event Mailbox Register for mailbox[2]. Signed-off-by: Alan Cox Cc: Chad Dupuis Cc: Giridhar Malavali Signed-off-by: Nicholas Bellinger --- drivers/scsi/qla2xxx/qla_target.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 6986552b47e6..77759c78cc21 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3960,7 +3960,7 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, { struct qla_hw_data *ha = vha->hw; struct qla_tgt *tgt = ha->tgt.qla_tgt; - int reason_code; + int login_code; ql_dbg(ql_dbg_tgt, vha, 0xe039, "scsi(%ld): ha state %d init_done %d oper_mode %d topo %d\n", @@ -4003,9 +4003,9 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03b, "qla_target(%d): Async LOOP_UP occured " - "(m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx, - le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), - le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); + "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); if (tgt->link_reinit_iocb_pending) { qlt_send_notify_ack(vha, (void *)&tgt->link_reinit_iocb, 0, 0, 0, 0, 0, 0); @@ -4020,23 +4020,24 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, case MBA_RSCN_UPDATE: ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03c, "qla_target(%d): Async event %#x occured " - "(m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx, code, - le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), - le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); + "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, code, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); break; case MBA_PORT_UPDATE: ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d, "qla_target(%d): Port update async event %#x " - "occured: updating the ports database (m[1]=%x, m[2]=%x, " - "m[3]=%x, m[4]=%x)", vha->vp_idx, code, - le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), - le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); - reason_code = le16_to_cpu(mailbox[2]); - if (reason_code == 0x4) + "occured: updating the ports database (m[0]=%x, m[1]=%x, " + "m[2]=%x, m[3]=%x)", vha->vp_idx, code, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); + + login_code = le16_to_cpu(mailbox[2]); + if (login_code == 0x4) ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03e, "Async MB 2: Got PLOGI Complete\n"); - else if (reason_code == 0x7) + else if (login_code == 0x7) ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f, "Async MB 2: Port Logged Out\n"); break; @@ -4044,9 +4045,9 @@ void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, default: ql_dbg(ql_dbg_tgt_mgt, vha, 0xf040, "qla_target(%d): Async event %#x occured: " - "ignore (m[1]=%x, m[2]=%x, m[3]=%x, m[4]=%x)", vha->vp_idx, - code, le16_to_cpu(mailbox[1]), le16_to_cpu(mailbox[2]), - le16_to_cpu(mailbox[3]), le16_to_cpu(mailbox[4])); + "ignore (m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, + code, le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); break; } -- cgit v1.2.3 From 82163edcdfa4eb3d74516cc8e9f38dd3d039b67d Mon Sep 17 00:00:00 2001 From: Santosh Nayak Date: Sat, 23 Jun 2012 07:59:54 -0300 Subject: [media] dvb-core: Release semaphore on error path dvb_register_device() There is a missing "up_write()" here. Semaphore should be released before returning error value. Cc: stable@kernel.org Signed-off-by: Santosh Nayak Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvbdev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index 00a67326c193..39eab73b01ae 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -243,6 +243,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, if (minor == MAX_DVB_MINORS) { kfree(dvbdevfops); kfree(dvbdev); + up_write(&minor_rwsem); mutex_unlock(&dvbdev_register_lock); return -EINVAL; } -- cgit v1.2.3 From 4dab0e5fe8ceaaf267003b5d7aa0c31c1092bee0 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 5 Jul 2012 22:52:34 -0300 Subject: [media] em28xx: fix em28xx-rc load The logic that checks if a device has remote control is wrong. Due to that, the em28xx RC module is not loaded by default. Fix the logic, in order to make it work properly. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 92da7c28b6f0..862c6575c557 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2893,7 +2893,7 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); - if (dev->board.has_ir_i2c && !disable_ir) + if (dev->board.ir_codes && !disable_ir) request_module("em28xx-rc"); } -- cgit v1.2.3 From a7deca6fa79d5c65575532e780f3c93f6bf8ddad Mon Sep 17 00:00:00 2001 From: David Dillow Date: Mon, 18 Jun 2012 00:15:21 -0300 Subject: [media] cx231xx: don't DMA to random addresses Commit 7a6f6c29d264cdd2fe0eb3d923217eed5f0ad134 (cx231xx: use URB_NO_TRANSFER_DMA_MAP) was intended to avoid mapping the DMA buffer for URB twice. This works for the URBs allocated with usb_alloc_urb(), as those are allocated from cohernent DMA pools, but the flag was also added for the VBI and audio URBs, which have a manually allocated area. This leaves the random trash in the structure after allocation as the DMA address, corrupting memory and preventing VBI and audio from working. Letting the USB core map the buffers solves the problem. Cc: stable@kernel.org Signed-off-by: David Dillow Cc: Sri Deevi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx231xx/cx231xx-audio.c | 4 ++-- drivers/media/video/cx231xx/cx231xx-vbi.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c index 068f78dc5d13..b4c99c7270cf 100644 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ b/drivers/media/video/cx231xx/cx231xx-audio.c @@ -307,7 +307,7 @@ static int cx231xx_init_audio_isoc(struct cx231xx *dev) urb->context = dev; urb->pipe = usb_rcvisocpipe(dev->udev, dev->adev.end_point_addr); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = dev->adev.transfer_buffer[i]; urb->interval = 1; urb->complete = cx231xx_audio_isocirq; @@ -368,7 +368,7 @@ static int cx231xx_init_audio_bulk(struct cx231xx *dev) urb->context = dev; urb->pipe = usb_rcvbulkpipe(dev->udev, dev->adev.end_point_addr); - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_flags = 0; urb->transfer_buffer = dev->adev.transfer_buffer[i]; urb->complete = cx231xx_audio_bulkirq; urb->transfer_buffer_length = sb_size; diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c index 3d15314e1f88..ac7db52f404f 100644 --- a/drivers/media/video/cx231xx/cx231xx-vbi.c +++ b/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -448,7 +448,7 @@ int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, return -ENOMEM; } dev->vbi_mode.bulk_ctl.urb[i] = urb; - urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_flags = 0; dev->vbi_mode.bulk_ctl.transfer_buffer[i] = kzalloc(sb_size, GFP_KERNEL); -- cgit v1.2.3 From a65c3262a766311d4704a9ea29354480c5e8590d Mon Sep 17 00:00:00 2001 From: Kamil Debski Date: Tue, 26 Jun 2012 04:44:40 -0300 Subject: [media] s5p-mfc: Fixed setup of custom controls in decoder and encoder Fixed bugs in functions that initialize custom controls. Signed-off-by: Kamil Debski Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 1 + drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index 4dd32fc8fd82..feea867f318c 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -996,6 +996,7 @@ int s5p_mfc_dec_ctrls_setup(struct s5p_mfc_ctx *ctx) for (i = 0; i < NUM_CTRLS; i++) { if (IS_MFC51_PRIV(controls[i].id)) { + memset(&cfg, 0, sizeof(struct v4l2_ctrl_config)); cfg.ops = &s5p_mfc_dec_ctrl_ops; cfg.id = controls[i].id; cfg.min = controls[i].minimum; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index 03d83340e7fb..158b78989b89 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -1773,6 +1773,7 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx) } for (i = 0; i < NUM_CTRLS; i++) { if (IS_MFC51_PRIV(controls[i].id)) { + memset(&cfg, 0, sizeof(struct v4l2_ctrl_config)); cfg.ops = &s5p_mfc_enc_ctrl_ops; cfg.id = controls[i].id; cfg.min = controls[i].minimum; -- cgit v1.2.3 From ba50e7e16b86e25048290b682f7a4e09e069d321 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 1 Jul 2012 16:15:09 -0300 Subject: [media] cx25840: fix regression in HVR-1800 analog support The refactoring of the cx25840 driver to support the cx23888 caused breakage with the existing support for cx23885/cx23887 analog support. Rework the routines such that the new code is only used for the 888. Validated with the following boards: HVR-1800 retail (0070:7801) HVR-1800 OEM (0070:7809) HVR_1850 retail (0070:8541) Thanks to Steven Toth and Hauppauge for loaning me various boards to regression test with. Reported-by: Jonathan Thanks-to: Steven Toth Signed-off-by: Devin Heitmueler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index fc1ff69cffd0..a82b7045f8c9 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -84,7 +84,7 @@ MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); /* ----------------------------------------------------------------------- */ -static void cx23885_std_setup(struct i2c_client *client); +static void cx23888_std_setup(struct i2c_client *client); int cx25840_write(struct i2c_client *client, u16 addr, u8 value) { @@ -638,10 +638,13 @@ static void cx23885_initialize(struct i2c_client *client) finish_wait(&state->fw_wait, &wait); destroy_workqueue(q); - /* Call the cx23885 specific std setup func, we no longer rely on + /* Call the cx23888 specific std setup func, we no longer rely on * the generic cx24840 func. */ - cx23885_std_setup(client); + if (is_cx23888(state)) + cx23888_std_setup(client); + else + cx25840_std_setup(client); /* (re)set input */ set_input(client, state->vid_input, state->aud_input); @@ -1298,8 +1301,8 @@ static int set_v4lstd(struct i2c_client *client) } cx25840_and_or(client, 0x400, ~0xf, fmt); cx25840_and_or(client, 0x403, ~0x3, pal_m); - if (is_cx2388x(state)) - cx23885_std_setup(client); + if (is_cx23888(state)) + cx23888_std_setup(client); else cx25840_std_setup(client); if (!is_cx2583x(state)) @@ -1782,8 +1785,8 @@ static int cx25840_s_video_routing(struct v4l2_subdev *sd, struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (is_cx2388x(state)) - cx23885_std_setup(client); + if (is_cx23888(state)) + cx23888_std_setup(client); return set_input(client, input, state->aud_input); } @@ -1794,8 +1797,8 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd, struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (is_cx2388x(state)) - cx23885_std_setup(client); + if (is_cx23888(state)) + cx23888_std_setup(client); return set_input(client, state->vid_input, input); } @@ -4939,7 +4942,7 @@ void cx23885_dif_setup(struct i2c_client *client, u32 ifHz) } } -static void cx23885_std_setup(struct i2c_client *client) +static void cx23888_std_setup(struct i2c_client *client) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; -- cgit v1.2.3 From e6d0db1d47c0ac4d61c842a95988cbe8712df447 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 1 Jul 2012 16:15:10 -0300 Subject: [media] cx25840: fix regression in analog support hue/saturation controls Fix regression in HVR-1800 analog support hue/saturation controls. The changes made for the cx23888 caused regressions in the analog support for cx23885/cx23887 based boards (partly due to changes in the locations of the hue/saturation controls). As a result the wrong registers were being overwritten. Add code to use the correct registers if it's a cx23888 Validated with the following boards: HVR-1800 retail (0070:7801) HVR-1800 OEM (0070:7809) HVR-1850 retail (0070:8541) Thanks to Steven Toth and Hauppauge for loaning me various boards to regression test with. Reported-by: Jonathan Thanks-to: Steven Toth Signed-off-by: Devin Heitmueler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 33 +++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index a82b7045f8c9..e12b3b0d7f35 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1106,9 +1106,23 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write4(client, 0x410, 0xffff0dbf); cx25840_write4(client, 0x414, 0x00137d03); - cx25840_write4(client, 0x418, 0x01008080); + + /* on the 887, 0x418 is HSCALE_CTRL, on the 888 it is + CHROMA_CTRL */ + if (is_cx23888(state)) + cx25840_write4(client, 0x418, 0x01008080); + else + cx25840_write4(client, 0x418, 0x01000000); + cx25840_write4(client, 0x41c, 0x00000000); - cx25840_write4(client, 0x420, 0x001c3e0f); + + /* on the 887, 0x420 is CHROMA_CTRL, on the 888 it is + CRUSH_CTRL */ + if (is_cx23888(state)) + cx25840_write4(client, 0x420, 0x001c3e0f); + else + cx25840_write4(client, 0x420, 0x001c8282); + cx25840_write4(client, 0x42c, 0x42600000); cx25840_write4(client, 0x430, 0x0000039b); cx25840_write4(client, 0x438, 0x00000000); @@ -1315,6 +1329,7 @@ static int set_v4lstd(struct i2c_client *client) static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); + struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { @@ -1327,12 +1342,20 @@ static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_SATURATION: - cx25840_write(client, 0x420, ctrl->val << 1); - cx25840_write(client, 0x421, ctrl->val << 1); + if (is_cx23888(state)) { + cx25840_write(client, 0x418, ctrl->val << 1); + cx25840_write(client, 0x419, ctrl->val << 1); + } else { + cx25840_write(client, 0x420, ctrl->val << 1); + cx25840_write(client, 0x421, ctrl->val << 1); + } break; case V4L2_CID_HUE: - cx25840_write(client, 0x422, ctrl->val); + if (is_cx23888(state)) + cx25840_write(client, 0x41a, ctrl->val); + else + cx25840_write(client, 0x422, ctrl->val); break; default: -- cgit v1.2.3 From d90133ec58d4fad822ff322557b6885c5b77f127 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 1 Jul 2012 16:15:11 -0300 Subject: [media] cx25840: fix regression in HVR-1800 analog audio The refactoring of the cx25840 driver to support the cx23888 caused breakage with the existing support for cx23885/cx23887 analog audio support. Tweak the code so that it only uses the code if it really is a cx23888 instead of applying it to all cx2388x based devices. Validated with the following boards: HVR-1800 retail (0070:7801) HVR-1800 OEM (0070:7809) HVR_1850 retail (0070:8541) Thanks to Steven Toth and Hauppauge for loaning me various boards to regression test with. Reported-by: Jonathan Thanks-to: Steven Toth Signed-off-by: Devin Heitmueler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index e12b3b0d7f35..7dc7bb155dff 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1250,7 +1250,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write4(client, 0x8d0, 0x1f063870); } - if (is_cx2388x(state)) { + if (is_cx23888(state)) { /* HVR1850 */ /* AUD_IO_CTRL - I2S Input, Parallel1*/ /* - Channel 1 src - Parallel1 (Merlin out) */ -- cgit v1.2.3 From b5c5c17babfaf83f274ca8f0a7660284882d8d4d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 1 Jul 2012 16:15:12 -0300 Subject: [media] cx25840: fix vsrc/hsrc usage on cx23888 designs The location of the vsrc/hsrc registers moved in the cx23888, causing the s_mbus call to fail prematurely indicating that "720x480 is not a valid size". The function bailed out before many pertinent registers were set related to the scaler (causing unexpected results in video rendering when doing raw video capture). Use the correct registers for the cx23888. Validated with the following boards: HVR-1800 retail (0070:7801) HVR-1800 OEM (0070:7809) HVR-1850 retail (0070:8541) Thanks to Steven Toth and Hauppauge for loaning me various boards to regression test with. Reported-by: Jonathan Thanks-to: Steven Toth Signed-off-by: Devin Heitmueler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 7dc7bb155dff..d8eac3e30a7e 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1380,11 +1380,21 @@ static int cx25840_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt fmt->field = V4L2_FIELD_INTERLACED; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; - Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; + if (is_cx23888(state)) { + Vsrc = (cx25840_read(client, 0x42a) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x429) & 0xf0) >> 4; + } else { + Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; + Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; + } - Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; - Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; + if (is_cx23888(state)) { + Hsrc = (cx25840_read(client, 0x426) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x425) & 0xf0) >> 4; + } else { + Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; + Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; + } Vlines = fmt->height + (is_50Hz ? 4 : 7); -- cgit v1.2.3 From d214ddc86807fb1995fd3400347a202826332710 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 1 Jul 2012 16:15:13 -0300 Subject: [media] cx23885: make analog support work for HVR_1250 (cx23885 variant) The analog support in the cx23885 driver was completely broken for the HVR-1250. Add the necessary code. Note that this only implements analog for the composite and s-video inputs. The tuner input continues to be non-functional due to a lack of analog support in the mt2131 driver. Validated with the following boards: HVR-1250 (0070:7911) Thanks to Steven Toth and Hauppauge for loaning me various boards to regression test with. Thanks-to: Steven Toth Signed-off-by: Devin Heitmueler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx23885/cx23885-cards.c | 31 +++++++++++++++++++++-------- drivers/media/video/cx23885/cx23885-video.c | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 13739e002a63..f8664f2ccfda 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -127,22 +127,37 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_HAUPPAUGE_HVR1250] = { .name = "Hauppauge WinTV-HVR1250", + .porta = CX23885_ANALOG_VIDEO, .portc = CX23885_MPEG_DVB, +#ifdef MT2131_NO_ANALOG_SUPPORT_YET + .tuner_type = TUNER_PHILIPS_TDA8290, + .tuner_addr = 0x42, /* 0x84 >> 1 */ + .tuner_bus = 1, +#endif + .force_bff = 1, .input = {{ +#ifdef MT2131_NO_ANALOG_SUPPORT_YET .type = CX23885_VMUX_TELEVISION, - .vmux = 0, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1, + .amux = CX25840_AUDIO8, .gpio0 = 0xff00, }, { - .type = CX23885_VMUX_DEBUG, - .vmux = 0, - .gpio0 = 0xff01, - }, { +#endif .type = CX23885_VMUX_COMPOSITE1, - .vmux = 1, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN6_CH1, + .amux = CX25840_AUDIO7, .gpio0 = 0xff02, }, { .type = CX23885_VMUX_SVIDEO, - .vmux = 2, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN8_CH1 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, .gpio0 = 0xff02, } }, }, @@ -1526,10 +1541,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) */ switch (dev->board) { case CX23885_BOARD_TEVII_S470: - case CX23885_BOARD_HAUPPAUGE_HVR1250: /* Currently only enabled for the integrated IR controller */ if (!enable_885_ir) break; + case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1700: diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index c654bdc7ccb2..4d05689df426 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -505,6 +505,7 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) || (dev->board == CX23885_BOARD_MPX885) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) { /* Configure audio routing */ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, -- cgit v1.2.3 From 0ac60acb5491df565141c0e3a87d7148a865fe36 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 1 Jul 2012 16:15:14 -0300 Subject: [media] cx23885: add support for HVR-1255 analog (cx23888 variant) Get the HVR-1255 analog support working for all supported inputs. This includes introduction of a new board profile for an OEM variant which doesn't have all the same inputs as the retail version of the board. Validated with the following boards: HVR-1255 (0070:2259) Thanks to Steven Toth and Hauppauge for loaning me various boards to regression test with. Thanks-to: Steven Toth Signed-off-by: Devin Heitmueler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx23885/cx23885-cards.c | 56 ++++++++++++++++++++++++++++- drivers/media/video/cx23885/cx23885-dvb.c | 6 ++++ drivers/media/video/cx23885/cx23885-video.c | 8 ++++- drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 69 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index f8664f2ccfda..bf79003bd8f6 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -282,7 +282,55 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_HAUPPAUGE_HVR1255] = { .name = "Hauppauge WinTV-HVR1255", + .porta = CX23885_ANALOG_VIDEO, .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, + .tuner_addr = 0x42, /* 0x84 >> 1 */ + .force_bff = 1, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1 | + CX25840_DIF_ON, + .amux = CX25840_AUDIO8, + }, { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN6_CH1, + .amux = CX25840_AUDIO7, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN8_CH1 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, + } }, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1255_22111] = { + .name = "Hauppauge WinTV-HVR1255", + .porta = CX23885_ANALOG_VIDEO, + .portc = CX23885_MPEG_DVB, + .tuner_type = TUNER_ABSENT, + .tuner_addr = 0x42, /* 0x84 >> 1 */ + .force_bff = 1, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN5_CH2 | + CX25840_VIN2_CH1 | + CX25840_DIF_ON, + .amux = CX25840_AUDIO8, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_VIN7_CH3 | + CX25840_VIN4_CH2 | + CX25840_VIN8_CH1 | + CX25840_SVIDEO_ON, + .amux = CX25840_AUDIO7, + } }, }, [CX23885_BOARD_HAUPPAUGE_HVR1210] = { .name = "Hauppauge WinTV-HVR1210", @@ -639,7 +687,7 @@ struct cx23885_subid cx23885_subids[] = { }, { .subvendor = 0x0070, .subdevice = 0x2259, - .card = CX23885_BOARD_HAUPPAUGE_HVR1255, + .card = CX23885_BOARD_HAUPPAUGE_HVR1255_22111, }, { .subvendor = 0x0070, .subdevice = 0x2291, @@ -1145,6 +1193,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1270: case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1210: /* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */ /* GPIO-6 I2C Gate which can isolate the demod from the bus */ @@ -1282,6 +1331,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1400: case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1210: /* FIXME: Implement me */ break; @@ -1439,6 +1489,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1270: case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: @@ -1526,6 +1577,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1270: case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: case CX23885_BOARD_HAUPPAUGE_HVR1290: @@ -1554,6 +1606,8 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1270: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_MYGICA_X8506: diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index a80a92c47455..cd542684ba02 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -712,6 +712,7 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(s5h1411_attach, &hcw_s5h1411_config, @@ -721,6 +722,11 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[1].i2c_adap, &hauppauge_tda18271_config); } + + tda18271_attach(&dev->ts1.analog_fe, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_tda18271_config); + break; case CX23885_BOARD_HAUPPAUGE_HVR1800: i2c_bus = &dev->i2c_bus[0]; diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 4d05689df426..22f8e7fbd665 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -506,6 +506,8 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1800) || (dev->board == CX23885_BOARD_MPX885) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1250) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111) || (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850)) { /* Configure audio routing */ v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, @@ -1579,7 +1581,9 @@ static int cx23885_set_freq_via_ops(struct cx23885_dev *dev, fe = vfe->dvb.frontend; - if (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) + if ((dev->board == CX23885_BOARD_HAUPPAUGE_HVR1850) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255) || + (dev->board == CX23885_BOARD_HAUPPAUGE_HVR1255_22111)) fe = &dev->ts1.analog_fe; if (fe && fe->ops.tuner_ops.set_analog_params) { @@ -1609,6 +1613,8 @@ int cx23885_set_frequency(struct file *file, void *priv, int ret; switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1255_22111: case CX23885_BOARD_HAUPPAUGE_HVR1850: ret = cx23885_set_freq_via_ops(dev, f); break; diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index d884784a1c85..13c37ec07ae7 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -90,6 +90,7 @@ #define CX23885_BOARD_MYGICA_X8507 33 #define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34 #define CX23885_BOARD_TEVII_S471 35 +#define CX23885_BOARD_HAUPPAUGE_HVR1255_22111 36 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 -- cgit v1.2.3 From c6cff169268bba9de687acf317ac24aa038cc263 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 1 Jul 2012 21:38:03 -0300 Subject: [media] cx23885: Silence unknown command warnings I am seeing a constant stream of warnings on my cx23885 based card: cx23885_tuner_callback(): Unknown command 0x2. Add a check in cx23885_tuner_callback to silence it. Signed-off-by: Anton Blanchard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx23885/cx23885-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index bf79003bd8f6..080e11157e5f 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -963,7 +963,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) struct cx23885_dev *dev = port->dev; u32 bitmask = 0; - if (command == XC2028_RESET_CLK) + if ((command == XC2028_RESET_CLK) || (command == XC2028_I2C_FLUSH)) return 0; if (command != 0) { -- cgit v1.2.3 From 57f4422f7bd83bb0a092e3f9aea0d9c8ac59045e Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 1 Jul 2012 21:58:00 -0300 Subject: [media] winbond-cir: Fix txandrx module info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We aren't getting any module info for the txandx option because of a typo: parm: txandrx:bool Signed-off-by: Anton Blanchard Acked-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/winbond-cir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 342c2c8c1ddf..eca17b564cc2 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -232,7 +232,7 @@ MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver"); static bool txandrx; /* default = 0 */ module_param(txandrx, bool, 0444); -MODULE_PARM_DESC(invert, "Allow simultaneous TX and RX"); +MODULE_PARM_DESC(txandrx, "Allow simultaneous TX and RX"); static unsigned int wake_sc = 0x800F040C; module_param(wake_sc, uint, 0644); -- cgit v1.2.3 From 8299d62843d4dcc5d769ce901799b8e7806ec93f Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Sun, 1 Jul 2012 21:58:52 -0300 Subject: [media] winbond-cir: Initialise timeout, driver_type and allowed_protos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to set a timeout so we can go idle on no activity. Signed-off-by: Anton Blanchard Acked-by: David Härdeman Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/winbond-cir.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index eca17b564cc2..54ee34872d14 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1032,6 +1032,8 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) data->dev->tx_ir = wbcir_tx; data->dev->priv = data; data->dev->dev.parent = &device->dev; + data->dev->timeout = MS_TO_NS(100); + data->dev->allowed_protos = RC_TYPE_ALL; if (!request_region(data->wbase, WAKEUP_IOMEM_LEN, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", -- cgit v1.2.3 From 7103180b4385de908d69815dbd6807879d371cbf Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 5 Jul 2012 14:31:33 -0300 Subject: [media] omap3isp: preview: Fix output size computation depending on input format The preview engine crops 4 columns and 4 lines when CFA is enabled. Commit b2da46e52fe7871cba36e1a435844502c0eccf39 ("omap3isp: preview: Add support for greyscale input") inverted the condition by mistake, fix this. Reported-by: Florian Neuhaus Signed-off-by: Laurent Pinchart Tested-by: Florian Neuhaus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 8a4935ecc655..a48a747b6fcc 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -1102,7 +1102,7 @@ static void preview_config_input_size(struct isp_prev_device *prev, u32 active) unsigned int elv = prev->crop.top + prev->crop.height - 1; u32 features; - if (format->code == V4L2_MBUS_FMT_Y10_1X10) { + if (format->code != V4L2_MBUS_FMT_Y10_1X10) { sph -= 2; eph += 2; slv -= 2; -- cgit v1.2.3 From 5aedc1094041335598c6aa73c5cf882f30886cd7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 6 Jul 2012 08:41:25 -0300 Subject: [media] omap3isp: preview: Fix contrast and brightness handling Commit bac387efbb88cf0e8df6f46a38387897cea464ee ("omap3isp: preview: Simplify configuration parameters access") added three fields to the preview_update structure, but failed to properly update the related initializers. Fix this. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/omap3isp/isppreview.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index a48a747b6fcc..dd91da26f1b0 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -888,12 +888,12 @@ static const struct preview_update update_attrs[] = { preview_config_contrast, NULL, offsetof(struct prev_params, contrast), - 0, true, + 0, 0, true, }, /* OMAP3ISP_PREV_BRIGHTNESS */ { preview_config_brightness, NULL, offsetof(struct prev_params, brightness), - 0, true, + 0, 0, true, }, }; -- cgit v1.2.3 From 4e39da01027eb73556c1ad416945e37f2771464e Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 4 Jun 2012 13:15:56 -0300 Subject: [media] s5p-fimc: Add missing FIMC-LITE file operations locking commit 5126f2590bee412e3053de851cb07f531e4be36a "v4l2-dev: add flag to have the core lock all file operations" introduced an additional bit flag (V4L2_FL_LOCK_ALL_FOPS) that should be set by drivers that use the v4l2 core lock for all file operations. Since this driver has been merged at the same time as the core changes it doesn't set this flags and thus its all file operations except IOCTL are not properly serialized. Fix this by adding file ops locking in the driver. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/s5p-fimc/fimc-lite.c | 61 +++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c index 4d269b8fa1ef..74ff310db30c 100644 --- a/drivers/media/video/s5p-fimc/fimc-lite.c +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -453,34 +453,42 @@ static int fimc_lite_open(struct file *file) struct fimc_lite *fimc = video_drvdata(file); int ret; + if (mutex_lock_interruptible(&fimc->lock)) + return -ERESTARTSYS; + set_bit(ST_FLITE_IN_USE, &fimc->state); ret = pm_runtime_get_sync(&fimc->pdev->dev); if (ret < 0) - return ret; - - if (++fimc->ref_count != 1 || fimc->out_path != FIMC_IO_DMA) - return 0; + goto done; ret = v4l2_fh_open(file); if (ret < 0) - return ret; + goto done; - ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, - true); - if (ret < 0) { - pm_runtime_put_sync(&fimc->pdev->dev); - fimc->ref_count--; - v4l2_fh_release(file); - clear_bit(ST_FLITE_IN_USE, &fimc->state); - } + if (++fimc->ref_count == 1 && fimc->out_path == FIMC_IO_DMA) { + ret = fimc_pipeline_initialize(&fimc->pipeline, + &fimc->vfd->entity, true); + if (ret < 0) { + pm_runtime_put_sync(&fimc->pdev->dev); + fimc->ref_count--; + v4l2_fh_release(file); + clear_bit(ST_FLITE_IN_USE, &fimc->state); + } - fimc_lite_clear_event_counters(fimc); + fimc_lite_clear_event_counters(fimc); + } +done: + mutex_unlock(&fimc->lock); return ret; } static int fimc_lite_close(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); + int ret; + + if (mutex_lock_interruptible(&fimc->lock)) + return -ERESTARTSYS; if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) { clear_bit(ST_FLITE_IN_USE, &fimc->state); @@ -494,20 +502,39 @@ static int fimc_lite_close(struct file *file) if (fimc->ref_count == 0) vb2_queue_release(&fimc->vb_queue); - return v4l2_fh_release(file); + ret = v4l2_fh_release(file); + + mutex_unlock(&fimc->lock); + return ret; } static unsigned int fimc_lite_poll(struct file *file, struct poll_table_struct *wait) { struct fimc_lite *fimc = video_drvdata(file); - return vb2_poll(&fimc->vb_queue, file, wait); + int ret; + + if (mutex_lock_interruptible(&fimc->lock)) + return POLL_ERR; + + ret = vb2_poll(&fimc->vb_queue, file, wait); + mutex_unlock(&fimc->lock); + + return ret; } static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma) { struct fimc_lite *fimc = video_drvdata(file); - return vb2_mmap(&fimc->vb_queue, vma); + int ret; + + if (mutex_lock_interruptible(&fimc->lock)) + return -ERESTARTSYS; + + ret = vb2_mmap(&fimc->vb_queue, vma); + mutex_unlock(&fimc->lock); + + return ret; } static const struct v4l2_file_operations fimc_lite_fops = { -- cgit v1.2.3 From 476a7eeb60e70ddab138e7cb4bc44ef5ac20782e Mon Sep 17 00:00:00 2001 From: Shinya Kuribayashi Date: Sat, 7 Jul 2012 13:37:42 +0300 Subject: hwspinlock/core: use global ID to register hwspinlocks on multiple devices Commit 300bab9770 (hwspinlock/core: register a bank of hwspinlocks in a single API call, 2011-09-06) introduced 'hwspin_lock_register_single()' to register numerous (a bank of) hwspinlock instances in a single API, 'hwspin_lock_register()'. At which time, 'hwspin_lock_register()' accidentally passes 'local IDs' to 'hwspin_lock_register_single()', despite that ..._single() requires 'global IDs' to register hwspinlocks. We have to convert into global IDs by supplying the missing 'base_id'. Cc: stable Signed-off-by: Shinya Kuribayashi [ohad: fix error path of hwspin_lock_register, too] Signed-off-by: Ohad Ben-Cohen --- drivers/hwspinlock/hwspinlock_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 61c9cf15fa52..1201a15784c3 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -345,7 +345,7 @@ int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, spin_lock_init(&hwlock->lock); hwlock->bank = bank; - ret = hwspin_lock_register_single(hwlock, i); + ret = hwspin_lock_register_single(hwlock, base_id + i); if (ret) goto reg_failed; } @@ -354,7 +354,7 @@ int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, reg_failed: while (--i >= 0) - hwspin_lock_unregister_single(i); + hwspin_lock_unregister_single(base_id + i); return ret; } EXPORT_SYMBOL_GPL(hwspin_lock_register); -- cgit v1.2.3 From d1e09ebf426ff34b4b6bbd6212b820edeb992bd4 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Sat, 7 Jul 2012 15:13:47 -0700 Subject: RDMA/ocrdma: Fix assignment of max_srq_sge in device query We want to set attr->max_srq_sge to dev->attr.max_srq_sge, not to itself. This was detected by Coverity (CID 709210). Signed-off-by: Roland Dreier --- drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 2e2e7aecc990..b2f9784beb4a 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -97,7 +97,7 @@ int ocrdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr) min(dev->attr.max_ord_per_qp, dev->attr.max_ird_per_qp); attr->max_qp_init_rd_atom = dev->attr.max_ord_per_qp; attr->max_srq = (dev->attr.max_qp - 1); - attr->max_srq_sge = attr->max_srq_sge; + attr->max_srq_sge = dev->attr.max_srq_sge; attr->max_srq_wr = dev->attr.max_rqe; attr->local_ca_ack_delay = dev->attr.local_ca_ack_delay; attr->max_fast_reg_page_list_len = 0; -- cgit v1.2.3 From 8ce5c9f27d2e2ce415d903d916e848a356d4c0c0 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Fri, 6 Jul 2012 20:07:35 +0000 Subject: r6040: remove duplicate call to the pci_set_drvdata pci_set_drvdata is called twice at the remove path of driver, call it once. Signed-off-by: Devendra Naga Signed-off-by: David S. Miller --- drivers/net/ethernet/rdc/r6040.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index d1827e887f4e..9acc026f62e8 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1256,7 +1256,6 @@ static void __devexit r6040_remove_one(struct pci_dev *pdev) kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); netif_napi_del(&lp->napi); - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, lp->base); pci_release_regions(pdev); free_netdev(dev); -- cgit v1.2.3 From 4af1c0488da97293c09d37ca49543566406073e6 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:41 +0000 Subject: net/mlx4_core: Change resource tracking mechanism to use red-black tree Change the data structure used for managing the SRIOV resource tracking mechanism from radix tree to red-black tree. This is preparation step for supporting resource IDs which are 64bit long, such as network flow steering rules. Such IDs can't be used as radix-tree keys on 32bit architectures and hence the reason for the change. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 3 +- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 106 +++++++++++++++------ 2 files changed, 77 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index e5d20220762c..1a2f37285ef5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -509,7 +510,7 @@ struct slave_list { struct mlx4_resource_tracker { spinlock_t lock; /* tree for each resources */ - struct radix_tree_root res_tree[MLX4_NUM_OF_RESOURCE_TYPE]; + struct rb_root res_tree[MLX4_NUM_OF_RESOURCE_TYPE]; /* num_of_slave's lists, one per slave */ struct slave_list *slave_list; }; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 766b8c5a235e..80c03c802301 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -57,6 +57,7 @@ struct mac_res { struct res_common { struct list_head list; + struct rb_node node; u32 res_id; int owner; int state; @@ -189,6 +190,49 @@ struct res_xrcdn { int port; }; +static void *res_tracker_lookup(struct rb_root *root, u64 res_id) +{ + struct rb_node *node = root->rb_node; + + while (node) { + struct res_common *res = container_of(node, struct res_common, + node); + + if (res_id < res->res_id) + node = node->rb_left; + else if (res_id > res->res_id) + node = node->rb_right; + else + return res; + } + return NULL; +} + +static int res_tracker_insert(struct rb_root *root, struct res_common *res) +{ + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct res_common *this = container_of(*new, struct res_common, + node); + + parent = *new; + if (res->res_id < this->res_id) + new = &((*new)->rb_left); + else if (res->res_id > this->res_id) + new = &((*new)->rb_right); + else + return -EEXIST; + } + + /* Add new node and rebalance tree. */ + rb_link_node(&res->node, parent, new); + rb_insert_color(&res->node, root); + + return 0; +} + /* For Debug uses */ static const char *ResourceType(enum mlx4_resource rt) { @@ -228,8 +272,7 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev) mlx4_dbg(dev, "Started init_resource_tracker: %ld slaves\n", dev->num_slaves); for (i = 0 ; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) - INIT_RADIX_TREE(&priv->mfunc.master.res_tracker.res_tree[i], - GFP_ATOMIC|__GFP_NOWARN); + priv->mfunc.master.res_tracker.res_tree[i] = RB_ROOT; spin_lock_init(&priv->mfunc.master.res_tracker.lock); return 0 ; @@ -277,8 +320,8 @@ static void *find_res(struct mlx4_dev *dev, int res_id, { struct mlx4_priv *priv = mlx4_priv(dev); - return radix_tree_lookup(&priv->mfunc.master.res_tracker.res_tree[type], - res_id); + return res_tracker_lookup(&priv->mfunc.master.res_tracker.res_tree[type], + res_id); } static int get_res(struct mlx4_dev *dev, int slave, int res_id, @@ -523,7 +566,7 @@ static int add_res_range(struct mlx4_dev *dev, int slave, int base, int count, struct mlx4_priv *priv = mlx4_priv(dev); struct res_common **res_arr; struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; - struct radix_tree_root *root = &tracker->res_tree[type]; + struct rb_root *root = &tracker->res_tree[type]; res_arr = kzalloc(count * sizeof *res_arr, GFP_KERNEL); if (!res_arr) @@ -546,7 +589,7 @@ static int add_res_range(struct mlx4_dev *dev, int slave, int base, int count, err = -EEXIST; goto undo; } - err = radix_tree_insert(root, base + i, res_arr[i]); + err = res_tracker_insert(root, res_arr[i]); if (err) goto undo; list_add_tail(&res_arr[i]->list, @@ -559,7 +602,7 @@ static int add_res_range(struct mlx4_dev *dev, int slave, int base, int count, undo: for (--i; i >= base; --i) - radix_tree_delete(&tracker->res_tree[type], i); + rb_erase(&res_arr[i]->node, root); spin_unlock_irq(mlx4_tlock(dev)); @@ -695,7 +738,7 @@ static int rem_res_range(struct mlx4_dev *dev, int slave, int base, int count, spin_lock_irq(mlx4_tlock(dev)); for (i = base; i < base + count; ++i) { - r = radix_tree_lookup(&tracker->res_tree[type], i); + r = res_tracker_lookup(&tracker->res_tree[type], i); if (!r) { err = -ENOENT; goto out; @@ -710,8 +753,8 @@ static int rem_res_range(struct mlx4_dev *dev, int slave, int base, int count, } for (i = base; i < base + count; ++i) { - r = radix_tree_lookup(&tracker->res_tree[type], i); - radix_tree_delete(&tracker->res_tree[type], i); + r = res_tracker_lookup(&tracker->res_tree[type], i); + rb_erase(&r->node, &tracker->res_tree[type]); list_del(&r->list); kfree(r); } @@ -733,7 +776,7 @@ static int qp_res_start_move_to(struct mlx4_dev *dev, int slave, int qpn, int err = 0; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[RES_QP], qpn); + r = res_tracker_lookup(&tracker->res_tree[RES_QP], qpn); if (!r) err = -ENOENT; else if (r->com.owner != slave) @@ -797,7 +840,7 @@ static int mr_res_start_move_to(struct mlx4_dev *dev, int slave, int index, int err = 0; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[RES_MPT], index); + r = res_tracker_lookup(&tracker->res_tree[RES_MPT], index); if (!r) err = -ENOENT; else if (r->com.owner != slave) @@ -850,7 +893,7 @@ static int eq_res_start_move_to(struct mlx4_dev *dev, int slave, int index, int err = 0; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[RES_EQ], index); + r = res_tracker_lookup(&tracker->res_tree[RES_EQ], index); if (!r) err = -ENOENT; else if (r->com.owner != slave) @@ -898,7 +941,7 @@ static int cq_res_start_move_to(struct mlx4_dev *dev, int slave, int cqn, int err; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[RES_CQ], cqn); + r = res_tracker_lookup(&tracker->res_tree[RES_CQ], cqn); if (!r) err = -ENOENT; else if (r->com.owner != slave) @@ -952,7 +995,7 @@ static int srq_res_start_move_to(struct mlx4_dev *dev, int slave, int index, int err = 0; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[RES_SRQ], index); + r = res_tracker_lookup(&tracker->res_tree[RES_SRQ], index); if (!r) err = -ENOENT; else if (r->com.owner != slave) @@ -1001,7 +1044,7 @@ static void res_abort_move(struct mlx4_dev *dev, int slave, struct res_common *r; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[type], id); + r = res_tracker_lookup(&tracker->res_tree[type], id); if (r && (r->owner == slave)) r->state = r->from_state; spin_unlock_irq(mlx4_tlock(dev)); @@ -1015,7 +1058,7 @@ static void res_end_move(struct mlx4_dev *dev, int slave, struct res_common *r; spin_lock_irq(mlx4_tlock(dev)); - r = radix_tree_lookup(&tracker->res_tree[type], id); + r = res_tracker_lookup(&tracker->res_tree[type], id); if (r && (r->owner == slave)) r->state = r->to_state; spin_unlock_irq(mlx4_tlock(dev)); @@ -2817,8 +2860,8 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave) switch (state) { case RES_QP_RESERVED: spin_lock_irq(mlx4_tlock(dev)); - radix_tree_delete(&tracker->res_tree[RES_QP], - qp->com.res_id); + rb_erase(&qp->com.node, + &tracker->res_tree[RES_QP]); list_del(&qp->com.list); spin_unlock_irq(mlx4_tlock(dev)); kfree(qp); @@ -2888,8 +2931,8 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave) case RES_SRQ_ALLOCATED: __mlx4_srq_free_icm(dev, srqn); spin_lock_irq(mlx4_tlock(dev)); - radix_tree_delete(&tracker->res_tree[RES_SRQ], - srqn); + rb_erase(&srq->com.node, + &tracker->res_tree[RES_SRQ]); list_del(&srq->com.list); spin_unlock_irq(mlx4_tlock(dev)); kfree(srq); @@ -2954,8 +2997,8 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave) case RES_CQ_ALLOCATED: __mlx4_cq_free_icm(dev, cqn); spin_lock_irq(mlx4_tlock(dev)); - radix_tree_delete(&tracker->res_tree[RES_CQ], - cqn); + rb_erase(&cq->com.node, + &tracker->res_tree[RES_CQ]); list_del(&cq->com.list); spin_unlock_irq(mlx4_tlock(dev)); kfree(cq); @@ -3017,8 +3060,8 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave) case RES_MPT_RESERVED: __mlx4_mr_release(dev, mpt->key); spin_lock_irq(mlx4_tlock(dev)); - radix_tree_delete(&tracker->res_tree[RES_MPT], - mptn); + rb_erase(&mpt->com.node, + &tracker->res_tree[RES_MPT]); list_del(&mpt->com.list); spin_unlock_irq(mlx4_tlock(dev)); kfree(mpt); @@ -3086,8 +3129,8 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave) __mlx4_free_mtt_range(dev, base, mtt->order); spin_lock_irq(mlx4_tlock(dev)); - radix_tree_delete(&tracker->res_tree[RES_MTT], - base); + rb_erase(&mtt->com.node, + &tracker->res_tree[RES_MTT]); list_del(&mtt->com.list); spin_unlock_irq(mlx4_tlock(dev)); kfree(mtt); @@ -3133,8 +3176,8 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave) switch (state) { case RES_EQ_RESERVED: spin_lock_irq(mlx4_tlock(dev)); - radix_tree_delete(&tracker->res_tree[RES_EQ], - eqn); + rb_erase(&eq->com.node, + &tracker->res_tree[RES_EQ]); list_del(&eq->com.list); spin_unlock_irq(mlx4_tlock(dev)); kfree(eq); @@ -3191,7 +3234,8 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave) list_for_each_entry_safe(counter, tmp, counter_list, com.list) { if (counter->com.owner == slave) { index = counter->com.res_id; - radix_tree_delete(&tracker->res_tree[RES_COUNTER], index); + rb_erase(&counter->com.node, + &tracker->res_tree[RES_COUNTER]); list_del(&counter->com.list); kfree(counter); __mlx4_counter_free(dev, index); @@ -3220,7 +3264,7 @@ static void rem_slave_xrcdns(struct mlx4_dev *dev, int slave) list_for_each_entry_safe(xrcd, tmp, xrcdn_list, com.list) { if (xrcd->com.owner == slave) { xrcdn = xrcd->com.res_id; - radix_tree_delete(&tracker->res_tree[RES_XRCD], xrcdn); + rb_erase(&xrcd->com.node, &tracker->res_tree[RES_XRCD]); list_del(&xrcd->com.list); kfree(xrcd); __mlx4_xrcd_free(dev, xrcdn); -- cgit v1.2.3 From aa1ec3dde1d818dcc94e307e25df98242aff5538 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:42 +0000 Subject: net/mlx4_core: Change resource tracking ID to be 64 bit Currently the IDs used by the resource tracker are of type u32, so far this was ok since all the different resources we were tracking could be encoded in 32bit. As a preparation step for tracking of resources whose IDs need > 32 bits such as network flow steering rules, who are 64 bit in size, move to use 64 bit based resource IDs. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 2 +- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 26 +++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 1a2f37285ef5..a425a984758f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1033,7 +1033,7 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port); /* resource tracker functions*/ int mlx4_get_slave_from_resource_id(struct mlx4_dev *dev, enum mlx4_resource resource_type, - int resource_id, int *slave); + u64 resource_id, int *slave); void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave_id); int mlx4_init_resource_tracker(struct mlx4_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 80c03c802301..6bdac2955f8b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -58,7 +58,7 @@ struct mac_res { struct res_common { struct list_head list; struct rb_node node; - u32 res_id; + u64 res_id; int owner; int state; int from_state; @@ -324,7 +324,7 @@ static void *find_res(struct mlx4_dev *dev, int res_id, res_id); } -static int get_res(struct mlx4_dev *dev, int slave, int res_id, +static int get_res(struct mlx4_dev *dev, int slave, u64 res_id, enum mlx4_resource type, void *res) { @@ -350,7 +350,7 @@ static int get_res(struct mlx4_dev *dev, int slave, int res_id, r->from_state = r->state; r->state = RES_ANY_BUSY; - mlx4_dbg(dev, "res %s id 0x%x to busy\n", + mlx4_dbg(dev, "res %s id 0x%llx to busy\n", ResourceType(type), r->res_id); if (res) @@ -363,7 +363,7 @@ exit: int mlx4_get_slave_from_resource_id(struct mlx4_dev *dev, enum mlx4_resource type, - int res_id, int *slave) + u64 res_id, int *slave) { struct res_common *r; @@ -384,7 +384,7 @@ int mlx4_get_slave_from_resource_id(struct mlx4_dev *dev, return err; } -static void put_res(struct mlx4_dev *dev, int slave, int res_id, +static void put_res(struct mlx4_dev *dev, int slave, u64 res_id, enum mlx4_resource type) { struct res_common *r; @@ -516,7 +516,7 @@ static struct res_common *alloc_xrcdn_tr(int id) return &ret->com; } -static struct res_common *alloc_tr(int id, enum mlx4_resource type, int slave, +static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave, int extra) { struct res_common *ret; @@ -558,7 +558,7 @@ static struct res_common *alloc_tr(int id, enum mlx4_resource type, int slave, return ret; } -static int add_res_range(struct mlx4_dev *dev, int slave, int base, int count, +static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, enum mlx4_resource type, int extra) { int i; @@ -727,10 +727,10 @@ static int remove_ok(struct res_common *res, enum mlx4_resource type, int extra) } } -static int rem_res_range(struct mlx4_dev *dev, int slave, int base, int count, +static int rem_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, enum mlx4_resource type, int extra) { - int i; + u64 i; int err; struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; @@ -784,7 +784,7 @@ static int qp_res_start_move_to(struct mlx4_dev *dev, int slave, int qpn, else { switch (state) { case RES_QP_BUSY: - mlx4_dbg(dev, "%s: failed RES_QP, 0x%x\n", + mlx4_dbg(dev, "%s: failed RES_QP, 0x%llx\n", __func__, r->com.res_id); err = -EBUSY; break; @@ -793,7 +793,7 @@ static int qp_res_start_move_to(struct mlx4_dev *dev, int slave, int qpn, if (r->com.state == RES_QP_MAPPED && !alloc) break; - mlx4_dbg(dev, "failed RES_QP, 0x%x\n", r->com.res_id); + mlx4_dbg(dev, "failed RES_QP, 0x%llx\n", r->com.res_id); err = -EINVAL; break; @@ -802,7 +802,7 @@ static int qp_res_start_move_to(struct mlx4_dev *dev, int slave, int qpn, r->com.state == RES_QP_HW) break; else { - mlx4_dbg(dev, "failed RES_QP, 0x%x\n", + mlx4_dbg(dev, "failed RES_QP, 0x%llx\n", r->com.res_id); err = -EINVAL; } @@ -2794,7 +2794,7 @@ static int _move_all_busy(struct mlx4_dev *dev, int slave, if (r->state == RES_ANY_BUSY) { if (print) mlx4_dbg(dev, - "%s id 0x%x is busy\n", + "%s id 0x%llx is busy\n", ResourceType(type), r->res_id); ++busy; -- cgit v1.2.3 From 6d19993788e080edb557178cc6aba2d963edce4e Mon Sep 17 00:00:00 2001 From: Yevgeny Petrilin Date: Thu, 5 Jul 2012 04:03:43 +0000 Subject: net/mlx4_en: Re-design multicast attachments flow Currently, for every change in the net device multicast list, the driver detaches all the addresses from the HW device, and then attaches the updated list. This behavior is wrong from two aspects: first, it causes a load of firmware commands and second, there is period of time where the correct addresses are not attached, which turned into packet loss. To improve - a copy of the multicast list is saved by the driver. For every change in the multicast list, the multicast list copy is used to find the delta between those two lists and add or remove multicast addresses as needed. Reported-by: Shawn Bohrer Cc: Shawn Bohrer Signed-off-by: Hadar Hen Zion Signed-off-by: Yevgeny Petrilin Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 143 +++++++++++++++++++------ drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 16 ++- 2 files changed, 124 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 073b85b45fc5..bedcbb30d38f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -170,33 +170,81 @@ static void mlx4_en_do_set_mac(struct work_struct *work) static void mlx4_en_clear_list(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_mc_list *tmp, *mc_to_del; - kfree(priv->mc_addrs); - priv->mc_addrs = NULL; - priv->mc_addrs_cnt = 0; + list_for_each_entry_safe(mc_to_del, tmp, &priv->mc_list, list) { + list_del(&mc_to_del->list); + kfree(mc_to_del); + } } static void mlx4_en_cache_mclist(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); struct netdev_hw_addr *ha; - char *mc_addrs; - int mc_addrs_cnt = netdev_mc_count(dev); - int i; + struct mlx4_en_mc_list *tmp; - mc_addrs = kmalloc(mc_addrs_cnt * ETH_ALEN, GFP_ATOMIC); - if (!mc_addrs) { - en_err(priv, "failed to allocate multicast list\n"); - return; - } - i = 0; - netdev_for_each_mc_addr(ha, dev) - memcpy(mc_addrs + i++ * ETH_ALEN, ha->addr, ETH_ALEN); mlx4_en_clear_list(dev); - priv->mc_addrs = mc_addrs; - priv->mc_addrs_cnt = mc_addrs_cnt; + netdev_for_each_mc_addr(ha, dev) { + tmp = kzalloc(sizeof(struct mlx4_en_mc_list), GFP_ATOMIC); + if (!tmp) { + en_err(priv, "failed to allocate multicast list\n"); + mlx4_en_clear_list(dev); + return; + } + memcpy(tmp->addr, ha->addr, ETH_ALEN); + list_add_tail(&tmp->list, &priv->mc_list); + } } +static void update_mclist_flags(struct mlx4_en_priv *priv, + struct list_head *dst, + struct list_head *src) +{ + struct mlx4_en_mc_list *dst_tmp, *src_tmp, *new_mc; + bool found; + + /* Find all the entries that should be removed from dst, + * These are the entries that are not found in src + */ + list_for_each_entry(dst_tmp, dst, list) { + found = false; + list_for_each_entry(src_tmp, src, list) { + if (!memcmp(dst_tmp->addr, src_tmp->addr, ETH_ALEN)) { + found = true; + break; + } + } + if (!found) + dst_tmp->action = MCLIST_REM; + } + + /* Add entries that exist in src but not in dst + * mark them as need to add + */ + list_for_each_entry(src_tmp, src, list) { + found = false; + list_for_each_entry(dst_tmp, dst, list) { + if (!memcmp(dst_tmp->addr, src_tmp->addr, ETH_ALEN)) { + dst_tmp->action = MCLIST_NONE; + found = true; + break; + } + } + if (!found) { + new_mc = kmalloc(sizeof(struct mlx4_en_mc_list), + GFP_KERNEL); + if (!new_mc) { + en_err(priv, "Failed to allocate current multicast list\n"); + return; + } + memcpy(new_mc, src_tmp, + sizeof(struct mlx4_en_mc_list)); + new_mc->action = MCLIST_ADD; + list_add_tail(&new_mc->list, dst); + } + } +} static void mlx4_en_set_multicast(struct net_device *dev) { @@ -214,6 +262,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) mcast_task); struct mlx4_en_dev *mdev = priv->mdev; struct net_device *dev = priv->dev; + struct mlx4_en_mc_list *mclist, *tmp; u64 mcast_addr = 0; u8 mc_list[16] = {0}; int err; @@ -336,7 +385,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags |= MLX4_EN_FLAG_MC_PROMISC; } } else { - int i; /* Disable Multicast promisc */ if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, @@ -351,13 +399,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) if (err) en_err(priv, "Failed disabling multicast filter\n"); - /* Detach our qp from all the multicast addresses */ - for (i = 0; i < priv->mc_addrs_cnt; i++) { - memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); - mc_list[5] = priv->port; - mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, - mc_list, MLX4_PROT_ETH); - } /* Flush mcast filter and init it with broadcast address */ mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, ETH_BCAST, 1, MLX4_MCAST_CONFIG); @@ -367,13 +408,8 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) netif_tx_lock_bh(dev); mlx4_en_cache_mclist(dev); netif_tx_unlock_bh(dev); - for (i = 0; i < priv->mc_addrs_cnt; i++) { - mcast_addr = - mlx4_en_mac_to_u64(priv->mc_addrs + i * ETH_ALEN); - memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); - mc_list[5] = priv->port; - mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, - mc_list, 0, MLX4_PROT_ETH); + list_for_each_entry(mclist, &priv->mc_list, list) { + mcast_addr = mlx4_en_mac_to_u64(mclist->addr); mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, mcast_addr, 0, MLX4_MCAST_CONFIG); } @@ -381,6 +417,38 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) 0, MLX4_MCAST_ENABLE); if (err) en_err(priv, "Failed enabling multicast filter\n"); + + update_mclist_flags(priv, &priv->curr_list, &priv->mc_list); + list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { + if (mclist->action == MCLIST_REM) { + /* detach this address and delete from list */ + memcpy(&mc_list[10], mclist->addr, ETH_ALEN); + mc_list[5] = priv->port; + err = mlx4_multicast_detach(mdev->dev, + &priv->rss_map.indir_qp, + mc_list, + MLX4_PROT_ETH); + if (err) + en_err(priv, "Fail to detach multicast address\n"); + + /* remove from list */ + list_del(&mclist->list); + kfree(mclist); + } + + if (mclist->action == MCLIST_ADD) { + /* attach the address */ + memcpy(&mc_list[10], mclist->addr, ETH_ALEN); + mc_list[5] = priv->port; + err = mlx4_multicast_attach(mdev->dev, + &priv->rss_map.indir_qp, + mc_list, 0, + MLX4_PROT_ETH); + if (err) + en_err(priv, "Fail to attach multicast address\n"); + + } + } } out: mutex_unlock(&mdev->state_lock); @@ -605,6 +673,9 @@ int mlx4_en_start_port(struct net_device *dev) return 0; } + INIT_LIST_HEAD(&priv->mc_list); + INIT_LIST_HEAD(&priv->curr_list); + /* Calculate Rx buf size */ dev->mtu = min(dev->mtu, priv->max_mtu); mlx4_en_calc_rx_buf(dev); @@ -760,6 +831,7 @@ void mlx4_en_stop_port(struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_mc_list *mclist, *tmp; int i; u8 mc_list[16] = {0}; @@ -781,13 +853,18 @@ void mlx4_en_stop_port(struct net_device *dev) mc_list[5] = priv->port; mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, MLX4_PROT_ETH); - for (i = 0; i < priv->mc_addrs_cnt; i++) { - memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); + list_for_each_entry(mclist, &priv->curr_list, list) { + memcpy(&mc_list[10], mclist->addr, ETH_ALEN); mc_list[5] = priv->port; mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, MLX4_PROT_ETH); } mlx4_en_clear_list(dev); + list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { + list_del(&mclist->list); + kfree(mclist); + } + /* Flush multicast filter */ mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 225c20d47900..1bb00cd22d42 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -404,6 +404,18 @@ struct mlx4_en_perf_stats { #define NUM_PERF_COUNTERS 6 }; +enum mlx4_en_mclist_act { + MCLIST_NONE, + MCLIST_REM, + MCLIST_ADD, +}; + +struct mlx4_en_mc_list { + struct list_head list; + enum mlx4_en_mclist_act action; + u8 addr[ETH_ALEN]; +}; + struct mlx4_en_frag_info { u16 frag_size; u16 frag_prefix_size; @@ -489,8 +501,8 @@ struct mlx4_en_priv { struct mlx4_en_pkt_stats pkstats; struct mlx4_en_port_stats port_stats; u64 stats_bitmap; - char *mc_addrs; - int mc_addrs_cnt; + struct list_head mc_list; + struct list_head curr_list; struct mlx4_en_stat_out_mbox hw_stats; int vids[128]; bool wol; -- cgit v1.2.3 From c96d97f4d127b61def87b3ee056bec20cfc265d1 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:44 +0000 Subject: net/mlx4: Set steering mode according to device capabilities Instead of checking the firmware supported steering mode in various places in the code, add a dedicated field in the mlx4 device capabilities structure which is written once during the initialization flow and read across the code. This also set the grounds for add new steering modes. Currently two modes are supported, and are named after the ConnectX HW versions A0 and B0. A0 steering uses mac_index, vlan_index and priority to steer traffic into pre-defined range of QPs. B0 steering uses Ethernet L2 hashing rules and is enabled only if the firmware supports both unicast and multicast B0 steering, The current steering modes are relevant for Ethernet traffic only, such that Infiniband steering remains untouched. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 108 ++++++++++++++++--------- drivers/net/ethernet/mellanox/mlx4/fw.c | 2 +- drivers/net/ethernet/mellanox/mlx4/main.c | 16 +++- drivers/net/ethernet/mellanox/mlx4/mcg.c | 70 ++++++++-------- drivers/net/ethernet/mellanox/mlx4/port.c | 9 +-- 5 files changed, 124 insertions(+), 81 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index bedcbb30d38f..44ff7cdb15e5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -265,7 +265,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) struct mlx4_en_mc_list *mclist, *tmp; u64 mcast_addr = 0; u8 mc_list[16] = {0}; - int err; + int err = 0; mutex_lock(&mdev->state_lock); if (!mdev->device_up) { @@ -300,16 +300,36 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags |= MLX4_EN_FLAG_PROMISC; /* Enable promiscouos mode */ - if (!(mdev->dev->caps.flags & - MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, - priv->base_qpn, 1); - else - err = mlx4_unicast_promisc_add(mdev->dev, priv->base_qpn, + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_unicast_promisc_add(mdev->dev, + priv->base_qpn, priv->port); - if (err) - en_err(priv, "Failed enabling " - "promiscuous mode\n"); + if (err) + en_err(priv, "Failed enabling unicast promiscuous mode\n"); + + /* Add the default qp number as multicast + * promisc + */ + if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { + err = mlx4_multicast_promisc_add(mdev->dev, + priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed enabling multicast promiscuous mode\n"); + priv->flags |= MLX4_EN_FLAG_MC_PROMISC; + } + break; + + case MLX4_STEERING_MODE_A0: + err = mlx4_SET_PORT_qpn_calc(mdev->dev, + priv->port, + priv->base_qpn, + 1); + if (err) + en_err(priv, "Failed enabling promiscuous mode\n"); + break; + } /* Disable port multicast filter (unconditionally) */ err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, @@ -318,15 +338,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) en_err(priv, "Failed disabling " "multicast filter\n"); - /* Add the default qp number as multicast promisc */ - if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { - err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, - priv->port); - if (err) - en_err(priv, "Failed entering multicast promisc mode\n"); - priv->flags |= MLX4_EN_FLAG_MC_PROMISC; - } - /* Disable port VLAN filter */ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv); if (err) @@ -345,22 +356,31 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags &= ~MLX4_EN_FLAG_PROMISC; /* Disable promiscouos mode */ - if (!(mdev->dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, - priv->base_qpn, 0); - else - err = mlx4_unicast_promisc_remove(mdev->dev, priv->base_qpn, + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_unicast_promisc_remove(mdev->dev, + priv->base_qpn, priv->port); - if (err) - en_err(priv, "Failed disabling promiscuous mode\n"); + if (err) + en_err(priv, "Failed disabling unicast promiscuous mode\n"); + /* Disable Multicast promisc */ + if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { + err = mlx4_multicast_promisc_remove(mdev->dev, + priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed disabling multicast promiscuous mode\n"); + priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + } + break; - /* Disable Multicast promisc */ - if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { - err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, - priv->port); + case MLX4_STEERING_MODE_A0: + err = mlx4_SET_PORT_qpn_calc(mdev->dev, + priv->port, + priv->base_qpn, 0); if (err) - en_err(priv, "Failed disabling multicast promiscuous mode\n"); - priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + en_err(priv, "Failed disabling promiscuous mode\n"); + break; } /* Enable port VLAN filter */ @@ -378,8 +398,16 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Add the default qp number as multicast promisc */ if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { - err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, - priv->port); + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_multicast_promisc_add(mdev->dev, + priv->base_qpn, + priv->port); + break; + + case MLX4_STEERING_MODE_A0: + break; + } if (err) en_err(priv, "Failed entering multicast promisc mode\n"); priv->flags |= MLX4_EN_FLAG_MC_PROMISC; @@ -387,8 +415,16 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) } else { /* Disable Multicast promisc */ if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { - err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, - priv->port); + switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: + err = mlx4_multicast_promisc_remove(mdev->dev, + priv->base_qpn, + priv->port); + break; + + case MLX4_STEERING_MODE_A0: + break; + } if (err) en_err(priv, "Failed disabling multicast promiscuous mode\n"); priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 9c83bb8151ea..40e048bac024 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1124,7 +1124,7 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET); - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) + if (dev->caps.steering_mode == MLX4_STEERING_MODE_B0) MLX4_PUT(inbox, (u8) (1 << 3), INIT_HCA_UC_STEERING_OFFSET); MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 14d9c762b60f..f8125a82c0cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -244,7 +244,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.reserved_srqs = dev_cap->reserved_srqs; dev->caps.max_sq_desc_sz = dev_cap->max_sq_desc_sz; dev->caps.max_rq_desc_sz = dev_cap->max_rq_desc_sz; - dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); /* * Subtract 1 from the limit because we need to allocate a * spare CQE so the HCA HW can tell the difference between an @@ -275,6 +274,21 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_gso_sz = dev_cap->max_gso_sz; dev->caps.max_rss_tbl_sz = dev_cap->max_rss_tbl_sz; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { + dev->caps.steering_mode = MLX4_STEERING_MODE_B0; + } else { + dev->caps.steering_mode = MLX4_STEERING_MODE_A0; + + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) + mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " + "set to use B0 steering. Falling back to A0 steering mode.\n"); + } + mlx4_dbg(dev, "Steering mode is: %s\n", + mlx4_steering_mode_str(dev->caps.steering_mode)); + dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); + /* Sense port always allowed on supported devices for ConnectX1 and 2 */ if (dev->pdev->device != 0x1003) dev->caps.flags |= MLX4_DEV_CAP_FLAG_SENSE_SUPPORT; diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index f4a8f98e402a..319c9d45d59a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -868,36 +868,50 @@ static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp, int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; - if (prot == MLX4_PROT_ETH) - gid[7] |= (MLX4_MC_STEER << 1); + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_A0: + if (prot == MLX4_PROT_ETH) + return 0; - if (mlx4_is_mfunc(dev)) - return mlx4_QP_ATTACH(dev, qp, gid, 1, - block_mcast_loopback, prot); + case MLX4_STEERING_MODE_B0: + if (prot == MLX4_PROT_ETH) + gid[7] |= (MLX4_MC_STEER << 1); - return mlx4_qp_attach_common(dev, qp, gid, block_mcast_loopback, - prot, MLX4_MC_STEER); + if (mlx4_is_mfunc(dev)) + return mlx4_QP_ATTACH(dev, qp, gid, 1, + block_mcast_loopback, prot); + return mlx4_qp_attach_common(dev, qp, gid, + block_mcast_loopback, prot, + MLX4_MC_STEER); + + default: + return -EINVAL; + } } EXPORT_SYMBOL_GPL(mlx4_multicast_attach); int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_A0: + if (prot == MLX4_PROT_ETH) + return 0; - if (prot == MLX4_PROT_ETH) - gid[7] |= (MLX4_MC_STEER << 1); + case MLX4_STEERING_MODE_B0: + if (prot == MLX4_PROT_ETH) + gid[7] |= (MLX4_MC_STEER << 1); - if (mlx4_is_mfunc(dev)) - return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot); + if (mlx4_is_mfunc(dev)) + return mlx4_QP_ATTACH(dev, qp, gid, 0, 0, prot); + + return mlx4_qp_detach_common(dev, qp, gid, prot, + MLX4_MC_STEER); - return mlx4_qp_detach_common(dev, qp, gid, prot, MLX4_MC_STEER); + default: + return -EINVAL; + } } EXPORT_SYMBOL_GPL(mlx4_multicast_detach); @@ -905,10 +919,6 @@ int mlx4_unicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (prot == MLX4_PROT_ETH) gid[7] |= (MLX4_UC_STEER << 1); @@ -924,10 +934,6 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_attach); int mlx4_unicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], enum mlx4_protocol prot) { - if (prot == MLX4_PROT_ETH && - !(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (prot == MLX4_PROT_ETH) gid[7] |= (MLX4_UC_STEER << 1); @@ -968,9 +974,6 @@ static int mlx4_PROMISC(struct mlx4_dev *dev, u32 qpn, int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 1, port); @@ -980,9 +983,6 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_add); int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_MC_STEER, 0, port); @@ -992,9 +992,6 @@ EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove); int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 1, port); @@ -1004,9 +1001,6 @@ EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add); int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) { - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) - return 0; - if (mlx4_is_mfunc(dev)) return mlx4_PROMISC(dev, qpn, MLX4_UC_STEER, 0, port); diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index a8fb52992c64..58de7237f57a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -155,7 +155,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) return err; } - if (!(dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER)) { + if (dev->caps.steering_mode == MLX4_STEERING_MODE_A0) { *qpn = info->base_qpn + index; return 0; } @@ -206,7 +206,7 @@ void mlx4_put_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int qpn) (unsigned long long) mac); mlx4_unregister_mac(dev, port, mac); - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) { + if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) { entry = radix_tree_lookup(&info->mac_tree, qpn); if (entry) { mlx4_dbg(dev, "Releasing qp: port %d, mac 0x%llx," @@ -359,7 +359,7 @@ int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac) int index = qpn - info->base_qpn; int err = 0; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) { + if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) { entry = radix_tree_lookup(&info->mac_tree, qpn); if (!entry) return -EINVAL; @@ -803,8 +803,7 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, u32 m_promisc = (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) ? MCAST_DIRECT : MCAST_DEFAULT; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER && - dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER) + if (dev->caps.steering_mode != MLX4_STEERING_MODE_A0) return 0; mailbox = mlx4_alloc_cmd_mailbox(dev); -- cgit v1.2.3 From 8fcfb4db74352d3d447b7a559ad54f7577074d19 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:45 +0000 Subject: net/mlx4_core: Add firmware commands to support device managed flow steering Add support for firmware commands to attach/detach a new device managed steering mode. Such network steering rules allow the user to provide an L2/L3/L4 flow specification to the firmware and have the device to steer traffic that matches that specification to the provided QP. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cmd.c | 19 ++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mcg.c | 29 ++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4.h | 10 ++++++++ .../net/ethernet/mellanox/mlx4/resource_tracker.c | 24 ++++++++++++++++++ 4 files changed, 82 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 842c8ce9494e..7e94987d030c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1080,6 +1080,25 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = NULL }, + /* flow steering commands */ + { + .opcode = MLX4_QP_FLOW_STEERING_ATTACH, + .has_inbox = true, + .has_outbox = false, + .out_is_imm = true, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_QP_FLOW_STEERING_ATTACH_wrapper + }, + { + .opcode = MLX4_QP_FLOW_STEERING_DETACH, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_QP_FLOW_STEERING_DETACH_wrapper + }, }; static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 319c9d45d59a..3c59a33a98a5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -62,6 +62,35 @@ int mlx4_get_qp_per_mgm(struct mlx4_dev *dev) return 4 * (mlx4_get_mgm_entry_size(dev) / 16 - 2); } +static int mlx4_QP_FLOW_STEERING_ATTACH(struct mlx4_dev *dev, + struct mlx4_cmd_mailbox *mailbox, + u32 size, + u64 *reg_id) +{ + u64 imm; + int err = 0; + + err = mlx4_cmd_imm(dev, mailbox->dma, &imm, size, 0, + MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + return err; + *reg_id = imm; + + return err; +} + +static int mlx4_QP_FLOW_STEERING_DETACH(struct mlx4_dev *dev, u64 regid) +{ + int err = 0; + + err = mlx4_cmd(dev, regid, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + return err; +} + static int mlx4_READ_ENTRY(struct mlx4_dev *dev, int index, struct mlx4_cmd_mailbox *mailbox) { diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index a425a984758f..c07e882e8369 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1118,6 +1118,16 @@ int mlx4_QUERY_IF_STAT_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *inbox, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd); +int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd); +int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd); int mlx4_get_mgm_entry_size(struct mlx4_dev *dev); int mlx4_get_qp_per_mgm(struct mlx4_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 6bdac2955f8b..a8ca960f4620 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2738,6 +2738,30 @@ ex_put: return err; } +int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, + vhcr->in_modifier, 0, + MLX4_QP_FLOW_STEERING_ATTACH, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); +} + +int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, + struct mlx4_vhcr *vhcr, + struct mlx4_cmd_mailbox *inbox, + struct mlx4_cmd_mailbox *outbox, + struct mlx4_cmd_info *cmd) +{ + return mlx4_cmd(dev, vhcr->in_param, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); +} + enum { BUSY_MAX_RETRIES = 10 }; -- cgit v1.2.3 From 0ff1fb654bec0cff62ddf81a8a8edec4263604a0 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:46 +0000 Subject: {NET, IB}/mlx4: Add device managed flow steering firmware API The driver is modified to support three operation modes. If supported by firmware use the device managed flow steering API, that which we call device managed steering mode. Else, if the firmware supports the B0 steering mode use it, and finally, if none of the above, use the A0 steering mode. When the steering mode is device managed, the code is modified such that L2 based rules set by the mlx4_en driver for Ethernet unicast and multicast, and the IB stack multicast attach calls done through the mlx4_ib driver are all routed to use the device managed API. When attaching rule using device managed flow steering API, the firmware returns a 64 bit registration id, which is to be provided during detach. Currently the firmware is always programmed during HCA initialization to use standard L2 hashing. Future work should be done to allow configuring the flow-steering hash function with common, non proprietary means. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/infiniband/hw/mlx4/main.c | 62 +++- drivers/infiniband/hw/mlx4/mlx4_ib.h | 1 + drivers/infiniband/hw/mlx4/qp.c | 1 + drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 21 +- drivers/net/ethernet/mellanox/mlx4/fw.c | 91 ++++- drivers/net/ethernet/mellanox/mlx4/fw.h | 3 + drivers/net/ethernet/mellanox/mlx4/main.c | 56 +++- drivers/net/ethernet/mellanox/mlx4/mcg.c | 365 ++++++++++++++++++++- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 13 + drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 2 + drivers/net/ethernet/mellanox/mlx4/port.c | 98 ++++-- drivers/net/ethernet/mellanox/mlx4/profile.c | 12 +- .../net/ethernet/mellanox/mlx4/resource_tracker.c | 6 + 13 files changed, 655 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 3530c41fcd1f..8a3a2037b005 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -718,26 +718,53 @@ int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, return ret; } +struct mlx4_ib_steering { + struct list_head list; + u64 reg_id; + union ib_gid gid; +}; + static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { int err; struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); struct mlx4_ib_qp *mqp = to_mqp(ibqp); + u64 reg_id; + struct mlx4_ib_steering *ib_steering = NULL; + + if (mdev->dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + ib_steering = kmalloc(sizeof(*ib_steering), GFP_KERNEL); + if (!ib_steering) + return -ENOMEM; + } - err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, - !!(mqp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), - MLX4_PROT_IB_IPV6); + err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, mqp->port, + !!(mqp->flags & + MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), + MLX4_PROT_IB_IPV6, ®_id); if (err) - return err; + goto err_malloc; err = add_gid_entry(ibqp, gid); if (err) goto err_add; + if (ib_steering) { + memcpy(ib_steering->gid.raw, gid->raw, 16); + ib_steering->reg_id = reg_id; + mutex_lock(&mqp->mutex); + list_add(&ib_steering->list, &mqp->steering_rules); + mutex_unlock(&mqp->mutex); + } return 0; err_add: - mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6); + mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, + MLX4_PROT_IB_IPV6, reg_id); +err_malloc: + kfree(ib_steering); + return err; } @@ -765,9 +792,30 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) u8 mac[6]; struct net_device *ndev; struct mlx4_ib_gid_entry *ge; + u64 reg_id = 0; + + if (mdev->dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + struct mlx4_ib_steering *ib_steering; + + mutex_lock(&mqp->mutex); + list_for_each_entry(ib_steering, &mqp->steering_rules, list) { + if (!memcmp(ib_steering->gid.raw, gid->raw, 16)) { + list_del(&ib_steering->list); + break; + } + } + mutex_unlock(&mqp->mutex); + if (&ib_steering->list == &mqp->steering_rules) { + pr_err("Couldn't find reg_id for mgid. Steering rule is left attached\n"); + return -EINVAL; + } + reg_id = ib_steering->reg_id; + kfree(ib_steering); + } - err = mlx4_multicast_detach(mdev->dev, - &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6); + err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, + MLX4_PROT_IB_IPV6, reg_id); if (err) return err; diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index ff36655d23d3..42df4f7a6a5b 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -163,6 +163,7 @@ struct mlx4_ib_qp { u8 state; int mlx_type; struct list_head gid_list; + struct list_head steering_rules; }; struct mlx4_ib_srq { diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 8d4ed24aef93..6af19f6c2b11 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -495,6 +495,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, spin_lock_init(&qp->sq.lock); spin_lock_init(&qp->rq.lock); INIT_LIST_HEAD(&qp->gid_list); + INIT_LIST_HEAD(&qp->steering_rules); qp->state = IB_QPS_RESET; if (init_attr->sq_sig_type == IB_SIGNAL_ALL_WR) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 44ff7cdb15e5..eb5ed8e39873 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -463,7 +463,8 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) err = mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, - MLX4_PROT_ETH); + MLX4_PROT_ETH, + mclist->reg_id); if (err) en_err(priv, "Fail to detach multicast address\n"); @@ -475,11 +476,14 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) if (mclist->action == MCLIST_ADD) { /* attach the address */ memcpy(&mc_list[10], mclist->addr, ETH_ALEN); + /* needed for B0 steering support */ mc_list[5] = priv->port; err = mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, - mc_list, 0, - MLX4_PROT_ETH); + mc_list, + priv->port, 0, + MLX4_PROT_ETH, + &mclist->reg_id); if (err) en_err(priv, "Fail to attach multicast address\n"); @@ -827,9 +831,10 @@ int mlx4_en_start_port(struct net_device *dev) /* Attach rx QP to bradcast address */ memset(&mc_list[10], 0xff, ETH_ALEN); - mc_list[5] = priv->port; + mc_list[5] = priv->port; /* needed for B0 steering support */ if (mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, mc_list, - 0, MLX4_PROT_ETH)) + priv->port, 0, MLX4_PROT_ETH, + &priv->broadcast_id)) mlx4_warn(mdev, "Failed Attaching Broadcast\n"); /* Must redo promiscuous mode setup. */ @@ -886,14 +891,14 @@ void mlx4_en_stop_port(struct net_device *dev) /* Detach All multicasts */ memset(&mc_list[10], 0xff, ETH_ALEN); - mc_list[5] = priv->port; + mc_list[5] = priv->port; /* needed for B0 steering support */ mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, - MLX4_PROT_ETH); + MLX4_PROT_ETH, priv->broadcast_id); list_for_each_entry(mclist, &priv->curr_list, list) { memcpy(&mc_list[10], mclist->addr, ETH_ALEN); mc_list[5] = priv->port; mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, - mc_list, MLX4_PROT_ETH); + mc_list, MLX4_PROT_ETH, mclist->reg_id); } mlx4_en_clear_list(dev); list_for_each_entry_safe(mclist, tmp, &priv->curr_list, list) { diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 40e048bac024..1d70657058a5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -123,7 +123,8 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) static const char * const fname[] = { [0] = "RSS support", [1] = "RSS Toeplitz Hash Function support", - [2] = "RSS XOR Hash Function support" + [2] = "RSS XOR Hash Function support", + [3] = "Device manage flow steering support" }; int i; @@ -391,6 +392,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_RSVD_XRC_OFFSET 0x66 #define QUERY_DEV_CAP_MAX_XRC_OFFSET 0x67 #define QUERY_DEV_CAP_MAX_COUNTERS_OFFSET 0x68 +#define QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET 0x76 +#define QUERY_DEV_CAP_FLOW_STEERING_MAX_QP_OFFSET 0x77 #define QUERY_DEV_CAP_RDMARC_ENTRY_SZ_OFFSET 0x80 #define QUERY_DEV_CAP_QPC_ENTRY_SZ_OFFSET 0x82 #define QUERY_DEV_CAP_AUX_ENTRY_SZ_OFFSET 0x84 @@ -474,6 +477,12 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev_cap->num_ports = field & 0xf; MLX4_GET(field, outbox, QUERY_DEV_CAP_MAX_MSG_SZ_OFFSET); dev_cap->max_msg_sz = 1 << (field & 0x1f); + MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_RANGE_EN_OFFSET); + if (field & 0x80) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_FS_EN; + dev_cap->fs_log_max_ucast_qp_range_size = field & 0x1f; + MLX4_GET(field, outbox, QUERY_DEV_CAP_FLOW_STEERING_MAX_QP_OFFSET); + dev_cap->fs_max_num_qp_per_entry = field; MLX4_GET(stat_rate, outbox, QUERY_DEV_CAP_RATE_SUPPORT_OFFSET); dev_cap->stat_rate_support = stat_rate; MLX4_GET(ext_flags, outbox, QUERY_DEV_CAP_EXT_FLAGS_OFFSET); @@ -1061,6 +1070,15 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) #define INIT_HCA_LOG_MC_HASH_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x16) #define INIT_HCA_UC_STEERING_OFFSET (INIT_HCA_MCAST_OFFSET + 0x18) #define INIT_HCA_LOG_MC_TABLE_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x1b) +#define INIT_HCA_DEVICE_MANAGED_FLOW_STEERING_EN 0x6 +#define INIT_HCA_FS_PARAM_OFFSET 0x1d0 +#define INIT_HCA_FS_BASE_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x00) +#define INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x12) +#define INIT_HCA_FS_LOG_TABLE_SZ_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x1b) +#define INIT_HCA_FS_ETH_BITS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x21) +#define INIT_HCA_FS_ETH_NUM_ADDRS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x22) +#define INIT_HCA_FS_IB_BITS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x25) +#define INIT_HCA_FS_IB_NUM_ADDRS_OFFSET (INIT_HCA_FS_PARAM_OFFSET + 0x26) #define INIT_HCA_TPT_OFFSET 0x0f0 #define INIT_HCA_DMPT_BASE_OFFSET (INIT_HCA_TPT_OFFSET + 0x00) #define INIT_HCA_LOG_MPT_SZ_OFFSET (INIT_HCA_TPT_OFFSET + 0x0b) @@ -1119,14 +1137,44 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) MLX4_PUT(inbox, param->rdmarc_base, INIT_HCA_RDMARC_BASE_OFFSET); MLX4_PUT(inbox, param->log_rd_per_qp, INIT_HCA_LOG_RD_OFFSET); - /* multicast attributes */ - - MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); - MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); - MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET); - if (dev->caps.steering_mode == MLX4_STEERING_MODE_B0) - MLX4_PUT(inbox, (u8) (1 << 3), INIT_HCA_UC_STEERING_OFFSET); - MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + /* steering attributes */ + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + *(inbox + INIT_HCA_FLAGS_OFFSET / 4) |= + cpu_to_be32(1 << + INIT_HCA_DEVICE_MANAGED_FLOW_STEERING_EN); + + MLX4_PUT(inbox, param->mc_base, INIT_HCA_FS_BASE_OFFSET); + MLX4_PUT(inbox, param->log_mc_entry_sz, + INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_table_sz, + INIT_HCA_FS_LOG_TABLE_SZ_OFFSET); + /* Enable Ethernet flow steering + * with udp unicast and tcp unicast + */ + MLX4_PUT(inbox, param->fs_hash_enable_bits, + INIT_HCA_FS_ETH_BITS_OFFSET); + MLX4_PUT(inbox, (u16) MLX4_FS_NUM_OF_L2_ADDR, + INIT_HCA_FS_ETH_NUM_ADDRS_OFFSET); + /* Enable IPoIB flow steering + * with udp unicast and tcp unicast + */ + MLX4_PUT(inbox, param->fs_hash_enable_bits, + INIT_HCA_FS_IB_BITS_OFFSET); + MLX4_PUT(inbox, (u16) MLX4_FS_NUM_OF_L2_ADDR, + INIT_HCA_FS_IB_NUM_ADDRS_OFFSET); + } else { + MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); + MLX4_PUT(inbox, param->log_mc_entry_sz, + INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_hash_sz, + INIT_HCA_LOG_MC_HASH_SZ_OFFSET); + MLX4_PUT(inbox, param->log_mc_table_sz, + INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + if (dev->caps.steering_mode == MLX4_STEERING_MODE_B0) + MLX4_PUT(inbox, (u8) (1 << 3), + INIT_HCA_UC_STEERING_OFFSET); + } /* TPT attributes */ @@ -1188,15 +1236,24 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev, MLX4_GET(param->rdmarc_base, outbox, INIT_HCA_RDMARC_BASE_OFFSET); MLX4_GET(param->log_rd_per_qp, outbox, INIT_HCA_LOG_RD_OFFSET); - /* multicast attributes */ + /* steering attributes */ + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { - MLX4_GET(param->mc_base, outbox, INIT_HCA_MC_BASE_OFFSET); - MLX4_GET(param->log_mc_entry_sz, outbox, - INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); - MLX4_GET(param->log_mc_hash_sz, outbox, - INIT_HCA_LOG_MC_HASH_SZ_OFFSET); - MLX4_GET(param->log_mc_table_sz, outbox, - INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + MLX4_GET(param->mc_base, outbox, INIT_HCA_FS_BASE_OFFSET); + MLX4_GET(param->log_mc_entry_sz, outbox, + INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET); + MLX4_GET(param->log_mc_table_sz, outbox, + INIT_HCA_FS_LOG_TABLE_SZ_OFFSET); + } else { + MLX4_GET(param->mc_base, outbox, INIT_HCA_MC_BASE_OFFSET); + MLX4_GET(param->log_mc_entry_sz, outbox, + INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); + MLX4_GET(param->log_mc_hash_sz, outbox, + INIT_HCA_LOG_MC_HASH_SZ_OFFSET); + MLX4_GET(param->log_mc_table_sz, outbox, + INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); + } /* TPT attributes */ diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 64c0399e4b78..83fcbbf1b169 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -78,6 +78,8 @@ struct mlx4_dev_cap { u16 wavelength[MLX4_MAX_PORTS + 1]; u64 trans_code[MLX4_MAX_PORTS + 1]; u16 stat_rate_support; + int fs_log_max_ucast_qp_range_size; + int fs_max_num_qp_per_entry; u64 flags; u64 flags2; int reserved_uars; @@ -165,6 +167,7 @@ struct mlx4_init_hca_param { u8 log_mpt_sz; u8 log_uar_sz; u8 uar_page_sz; /* log pg sz in 4k chunks */ + u8 fs_hash_enable_bits; }; struct mlx4_init_ib_param { diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index f8125a82c0cb..42645166bae2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -91,7 +91,9 @@ module_param_named(log_num_mgm_entry_size, MODULE_PARM_DESC(log_num_mgm_entry_size, "log mgm size, that defines the num" " of qp per mcg, for example:" " 10 gives 248.range: 9<=" - " log_num_mgm_entry_size <= 12"); + " log_num_mgm_entry_size <= 12." + " Not in use with device managed" + " flow steering"); #define MLX4_VF (1 << 0) @@ -274,20 +276,27 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.max_gso_sz = dev_cap->max_gso_sz; dev->caps.max_rss_tbl_sz = dev_cap->max_rss_tbl_sz; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && - dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { - dev->caps.steering_mode = MLX4_STEERING_MODE_B0; + if (dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_FS_EN) { + dev->caps.steering_mode = MLX4_STEERING_MODE_DEVICE_MANAGED; + dev->caps.num_qp_per_mgm = dev_cap->fs_max_num_qp_per_entry; + dev->caps.fs_log_max_ucast_qp_range_size = + dev_cap->fs_log_max_ucast_qp_range_size; } else { - dev->caps.steering_mode = MLX4_STEERING_MODE_A0; + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { + dev->caps.steering_mode = MLX4_STEERING_MODE_B0; + } else { + dev->caps.steering_mode = MLX4_STEERING_MODE_A0; - if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || - dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) - mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " - "set to use B0 steering. Falling back to A0 steering mode.\n"); + if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || + dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) + mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " + "set to use B0 steering. Falling back to A0 steering mode.\n"); + } + dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); } mlx4_dbg(dev, "Steering mode is: %s\n", mlx4_steering_mode_str(dev->caps.steering_mode)); - dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); /* Sense port always allowed on supported devices for ConnectX1 and 2 */ if (dev->pdev->device != 0x1003) @@ -982,9 +991,11 @@ static int mlx4_init_icm(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap, } /* - * It's not strictly required, but for simplicity just map the - * whole multicast group table now. The table isn't very big - * and it's a lot easier than trying to track ref counts. + * For flow steering device managed mode it is required to use + * mlx4_init_icm_table. For B0 steering mode it's not strictly + * required, but for simplicity just map the whole multicast + * group table now. The table isn't very big and it's a lot + * easier than trying to track ref counts. */ err = mlx4_init_icm_table(dev, &priv->mcg_table.table, init_hca->mc_base, @@ -1220,7 +1231,26 @@ static int mlx4_init_hca(struct mlx4_dev *dev) goto err_stop_fw; } + priv->fs_hash_mode = MLX4_FS_L2_HASH; + + switch (priv->fs_hash_mode) { + case MLX4_FS_L2_HASH: + init_hca.fs_hash_enable_bits = 0; + break; + + case MLX4_FS_L2_L3_L4_HASH: + /* Enable flow steering with + * udp unicast and tcp unicast + */ + init_hca.fs_hash_enable_bits = + MLX4_FS_UDP_UC_EN | MLX4_FS_TCP_UC_EN; + break; + } + profile = default_profile; + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) + profile.num_mcg = MLX4_FS_NUM_MCG; icm_size = mlx4_make_profile(dev, &profile, &dev_cap, &init_hca); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 3c59a33a98a5..768a2a4530e8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -41,6 +41,7 @@ #define MGM_QPN_MASK 0x00FFFFFF #define MGM_BLCK_LB_BIT 30 +#define MLX4_MAC_MASK 0xffffffffffffULL static const u8 zero_gid[16]; /* automatically initialized to 0 */ @@ -54,7 +55,12 @@ struct mlx4_mgm { int mlx4_get_mgm_entry_size(struct mlx4_dev *dev) { - return min((1 << mlx4_log_num_mgm_entry_size), MLX4_MAX_MGM_ENTRY_SIZE); + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) + return 1 << MLX4_FS_MGM_LOG_ENTRY_SIZE; + else + return min((1 << mlx4_log_num_mgm_entry_size), + MLX4_MAX_MGM_ENTRY_SIZE); } int mlx4_get_qp_per_mgm(struct mlx4_dev *dev) @@ -643,6 +649,311 @@ static int find_entry(struct mlx4_dev *dev, u8 port, return err; } +struct mlx4_net_trans_rule_hw_ctrl { + __be32 ctrl; + __be32 vf_vep_port; + __be32 qpn; + __be32 reserved; +}; + +static void trans_rule_ctrl_to_hw(struct mlx4_net_trans_rule *ctrl, + struct mlx4_net_trans_rule_hw_ctrl *hw) +{ + static const u8 __promisc_mode[] = { + [MLX4_FS_PROMISC_NONE] = 0x0, + [MLX4_FS_PROMISC_UPLINK] = 0x1, + [MLX4_FS_PROMISC_FUNCTION_PORT] = 0x2, + [MLX4_FS_PROMISC_ALL_MULTI] = 0x3, + }; + + u32 dw = 0; + + dw = ctrl->queue_mode == MLX4_NET_TRANS_Q_LIFO ? 1 : 0; + dw |= ctrl->exclusive ? (1 << 2) : 0; + dw |= ctrl->allow_loopback ? (1 << 3) : 0; + dw |= __promisc_mode[ctrl->promisc_mode] << 8; + dw |= ctrl->priority << 16; + + hw->ctrl = cpu_to_be32(dw); + hw->vf_vep_port = cpu_to_be32(ctrl->port); + hw->qpn = cpu_to_be32(ctrl->qpn); +} + +struct mlx4_net_trans_rule_hw_ib { + u8 size; + u8 rsvd1; + __be16 id; + u32 rsvd2; + __be32 qpn; + __be32 qpn_mask; + u8 dst_gid[16]; + u8 dst_gid_msk[16]; +} __packed; + +struct mlx4_net_trans_rule_hw_eth { + u8 size; + u8 rsvd; + __be16 id; + u8 rsvd1[6]; + u8 dst_mac[6]; + u16 rsvd2; + u8 dst_mac_msk[6]; + u16 rsvd3; + u8 src_mac[6]; + u16 rsvd4; + u8 src_mac_msk[6]; + u8 rsvd5; + u8 ether_type_enable; + __be16 ether_type; + __be16 vlan_id_msk; + __be16 vlan_id; +} __packed; + +struct mlx4_net_trans_rule_hw_tcp_udp { + u8 size; + u8 rsvd; + __be16 id; + __be16 rsvd1[3]; + __be16 dst_port; + __be16 rsvd2; + __be16 dst_port_msk; + __be16 rsvd3; + __be16 src_port; + __be16 rsvd4; + __be16 src_port_msk; +} __packed; + +struct mlx4_net_trans_rule_hw_ipv4 { + u8 size; + u8 rsvd; + __be16 id; + __be32 rsvd1; + __be32 dst_ip; + __be32 dst_ip_msk; + __be32 src_ip; + __be32 src_ip_msk; +} __packed; + +struct _rule_hw { + union { + struct { + u8 size; + u8 rsvd; + __be16 id; + }; + struct mlx4_net_trans_rule_hw_eth eth; + struct mlx4_net_trans_rule_hw_ib ib; + struct mlx4_net_trans_rule_hw_ipv4 ipv4; + struct mlx4_net_trans_rule_hw_tcp_udp tcp_udp; + }; +}; + +static int parse_trans_rule(struct mlx4_dev *dev, struct mlx4_spec_list *spec, + struct _rule_hw *rule_hw) +{ + static const u16 __sw_id_hw[] = { + [MLX4_NET_TRANS_RULE_ID_ETH] = 0xE001, + [MLX4_NET_TRANS_RULE_ID_IB] = 0xE005, + [MLX4_NET_TRANS_RULE_ID_IPV6] = 0xE003, + [MLX4_NET_TRANS_RULE_ID_IPV4] = 0xE002, + [MLX4_NET_TRANS_RULE_ID_TCP] = 0xE004, + [MLX4_NET_TRANS_RULE_ID_UDP] = 0xE006 + }; + + static const size_t __rule_hw_sz[] = { + [MLX4_NET_TRANS_RULE_ID_ETH] = + sizeof(struct mlx4_net_trans_rule_hw_eth), + [MLX4_NET_TRANS_RULE_ID_IB] = + sizeof(struct mlx4_net_trans_rule_hw_ib), + [MLX4_NET_TRANS_RULE_ID_IPV6] = 0, + [MLX4_NET_TRANS_RULE_ID_IPV4] = + sizeof(struct mlx4_net_trans_rule_hw_ipv4), + [MLX4_NET_TRANS_RULE_ID_TCP] = + sizeof(struct mlx4_net_trans_rule_hw_tcp_udp), + [MLX4_NET_TRANS_RULE_ID_UDP] = + sizeof(struct mlx4_net_trans_rule_hw_tcp_udp) + }; + if (spec->id > MLX4_NET_TRANS_RULE_NUM) { + mlx4_err(dev, "Invalid network rule id. id = %d\n", spec->id); + return -EINVAL; + } + memset(rule_hw, 0, __rule_hw_sz[spec->id]); + rule_hw->id = cpu_to_be16(__sw_id_hw[spec->id]); + rule_hw->size = __rule_hw_sz[spec->id] >> 2; + + switch (spec->id) { + case MLX4_NET_TRANS_RULE_ID_ETH: + memcpy(rule_hw->eth.dst_mac, spec->eth.dst_mac, ETH_ALEN); + memcpy(rule_hw->eth.dst_mac_msk, spec->eth.dst_mac_msk, + ETH_ALEN); + memcpy(rule_hw->eth.src_mac, spec->eth.src_mac, ETH_ALEN); + memcpy(rule_hw->eth.src_mac_msk, spec->eth.src_mac_msk, + ETH_ALEN); + if (spec->eth.ether_type_enable) { + rule_hw->eth.ether_type_enable = 1; + rule_hw->eth.ether_type = spec->eth.ether_type; + } + rule_hw->eth.vlan_id = spec->eth.vlan_id; + rule_hw->eth.vlan_id_msk = spec->eth.vlan_id_msk; + break; + + case MLX4_NET_TRANS_RULE_ID_IB: + rule_hw->ib.qpn = spec->ib.r_qpn; + rule_hw->ib.qpn_mask = spec->ib.qpn_msk; + memcpy(&rule_hw->ib.dst_gid, &spec->ib.dst_gid, 16); + memcpy(&rule_hw->ib.dst_gid_msk, &spec->ib.dst_gid_msk, 16); + break; + + case MLX4_NET_TRANS_RULE_ID_IPV6: + return -EOPNOTSUPP; + + case MLX4_NET_TRANS_RULE_ID_IPV4: + rule_hw->ipv4.src_ip = spec->ipv4.src_ip; + rule_hw->ipv4.src_ip_msk = spec->ipv4.src_ip_msk; + rule_hw->ipv4.dst_ip = spec->ipv4.dst_ip; + rule_hw->ipv4.dst_ip_msk = spec->ipv4.dst_ip_msk; + break; + + case MLX4_NET_TRANS_RULE_ID_TCP: + case MLX4_NET_TRANS_RULE_ID_UDP: + rule_hw->tcp_udp.dst_port = spec->tcp_udp.dst_port; + rule_hw->tcp_udp.dst_port_msk = spec->tcp_udp.dst_port_msk; + rule_hw->tcp_udp.src_port = spec->tcp_udp.src_port; + rule_hw->tcp_udp.src_port_msk = spec->tcp_udp.src_port_msk; + break; + + default: + return -EINVAL; + } + + return __rule_hw_sz[spec->id]; +} + +static void mlx4_err_rule(struct mlx4_dev *dev, char *str, + struct mlx4_net_trans_rule *rule) +{ +#define BUF_SIZE 256 + struct mlx4_spec_list *cur; + char buf[BUF_SIZE]; + int len = 0; + + mlx4_err(dev, "%s", str); + len += snprintf(buf + len, BUF_SIZE - len, + "port = %d prio = 0x%x qp = 0x%x ", + rule->port, rule->priority, rule->qpn); + + list_for_each_entry(cur, &rule->list, list) { + switch (cur->id) { + case MLX4_NET_TRANS_RULE_ID_ETH: + len += snprintf(buf + len, BUF_SIZE - len, + "dmac = %pM ", &cur->eth.dst_mac); + if (cur->eth.ether_type) + len += snprintf(buf + len, BUF_SIZE - len, + "ethertype = 0x%x ", + be16_to_cpu(cur->eth.ether_type)); + if (cur->eth.vlan_id) + len += snprintf(buf + len, BUF_SIZE - len, + "vlan-id = %d ", + be16_to_cpu(cur->eth.vlan_id)); + break; + + case MLX4_NET_TRANS_RULE_ID_IPV4: + if (cur->ipv4.src_ip) + len += snprintf(buf + len, BUF_SIZE - len, + "src-ip = %pI4 ", + &cur->ipv4.src_ip); + if (cur->ipv4.dst_ip) + len += snprintf(buf + len, BUF_SIZE - len, + "dst-ip = %pI4 ", + &cur->ipv4.dst_ip); + break; + + case MLX4_NET_TRANS_RULE_ID_TCP: + case MLX4_NET_TRANS_RULE_ID_UDP: + if (cur->tcp_udp.src_port) + len += snprintf(buf + len, BUF_SIZE - len, + "src-port = %d ", + be16_to_cpu(cur->tcp_udp.src_port)); + if (cur->tcp_udp.dst_port) + len += snprintf(buf + len, BUF_SIZE - len, + "dst-port = %d ", + be16_to_cpu(cur->tcp_udp.dst_port)); + break; + + case MLX4_NET_TRANS_RULE_ID_IB: + len += snprintf(buf + len, BUF_SIZE - len, + "dst-gid = %pI6\n", cur->ib.dst_gid); + len += snprintf(buf + len, BUF_SIZE - len, + "dst-gid-mask = %pI6\n", + cur->ib.dst_gid_msk); + break; + + case MLX4_NET_TRANS_RULE_ID_IPV6: + break; + + default: + break; + } + } + len += snprintf(buf + len, BUF_SIZE - len, "\n"); + mlx4_err(dev, "%s", buf); + + if (len >= BUF_SIZE) + mlx4_err(dev, "Network rule error message was truncated, print buffer is too small.\n"); +} + +int mlx4_flow_attach(struct mlx4_dev *dev, + struct mlx4_net_trans_rule *rule, u64 *reg_id) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_spec_list *cur; + u32 size = 0; + int ret; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + memset(mailbox->buf, 0, sizeof(struct mlx4_net_trans_rule_hw_ctrl)); + trans_rule_ctrl_to_hw(rule, mailbox->buf); + + size += sizeof(struct mlx4_net_trans_rule_hw_ctrl); + + list_for_each_entry(cur, &rule->list, list) { + ret = parse_trans_rule(dev, cur, mailbox->buf + size); + if (ret < 0) { + mlx4_free_cmd_mailbox(dev, mailbox); + return -EINVAL; + } + size += ret; + } + + ret = mlx4_QP_FLOW_STEERING_ATTACH(dev, mailbox, size >> 2, reg_id); + if (ret == -ENOMEM) + mlx4_err_rule(dev, + "mcg table is full. Fail to register network rule.\n", + rule); + else if (ret) + mlx4_err_rule(dev, "Fail to register network rule.\n", rule); + + mlx4_free_cmd_mailbox(dev, mailbox); + + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_flow_attach); + +int mlx4_flow_detach(struct mlx4_dev *dev, u64 reg_id) +{ + int err; + + err = mlx4_QP_FLOW_STEERING_DETACH(dev, reg_id); + if (err) + mlx4_err(dev, "Fail to detach network rule. registration id = 0x%llx\n", + reg_id); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_flow_detach); + int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot, enum mlx4_steer_type steer) @@ -895,7 +1206,8 @@ static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp, } int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - int block_mcast_loopback, enum mlx4_protocol prot) + u8 port, int block_mcast_loopback, + enum mlx4_protocol prot, u64 *reg_id) { switch (dev->caps.steering_mode) { @@ -914,6 +1226,42 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], block_mcast_loopback, prot, MLX4_MC_STEER); + case MLX4_STEERING_MODE_DEVICE_MANAGED: { + struct mlx4_spec_list spec = { {NULL} }; + __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); + + struct mlx4_net_trans_rule rule = { + .queue_mode = MLX4_NET_TRANS_Q_FIFO, + .exclusive = 0, + .promisc_mode = MLX4_FS_PROMISC_NONE, + .priority = MLX4_DOMAIN_NIC, + }; + + rule.allow_loopback = ~block_mcast_loopback; + rule.port = port; + rule.qpn = qp->qpn; + INIT_LIST_HEAD(&rule.list); + + switch (prot) { + case MLX4_PROT_ETH: + spec.id = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(spec.eth.dst_mac, &gid[10], ETH_ALEN); + memcpy(spec.eth.dst_mac_msk, &mac_mask, ETH_ALEN); + break; + + case MLX4_PROT_IB_IPV6: + spec.id = MLX4_NET_TRANS_RULE_ID_IB; + memcpy(spec.ib.dst_gid, gid, 16); + memset(&spec.ib.dst_gid_msk, 0xff, 16); + break; + default: + return -EINVAL; + } + list_add_tail(&spec.list, &rule.list); + + return mlx4_flow_attach(dev, &rule, reg_id); + } + default: return -EINVAL; } @@ -921,7 +1269,7 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], EXPORT_SYMBOL_GPL(mlx4_multicast_attach); int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - enum mlx4_protocol prot) + enum mlx4_protocol prot, u64 reg_id) { switch (dev->caps.steering_mode) { case MLX4_STEERING_MODE_A0: @@ -938,6 +1286,9 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], return mlx4_qp_detach_common(dev, qp, gid, prot, MLX4_MC_STEER); + case MLX4_STEERING_MODE_DEVICE_MANAGED: + return mlx4_flow_detach(dev, reg_id); + default: return -EINVAL; } @@ -1042,6 +1393,10 @@ int mlx4_init_mcg_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int err; + /* No need for mcg_table when fw managed the mcg table*/ + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) + return 0; err = mlx4_bitmap_init(&priv->mcg_table.bitmap, dev->caps.num_amgms, dev->caps.num_amgms - 1, 0, 0); if (err) @@ -1054,5 +1409,7 @@ int mlx4_init_mcg_table(struct mlx4_dev *dev) void mlx4_cleanup_mcg_table(struct mlx4_dev *dev) { - mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap); + if (dev->caps.steering_mode != + MLX4_STEERING_MODE_DEVICE_MANAGED) + mlx4_bitmap_cleanup(&mlx4_priv(dev)->mcg_table.bitmap); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index c07e882e8369..0084967be19e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -54,6 +54,17 @@ #define DRV_VERSION "1.1" #define DRV_RELDATE "Dec, 2011" +#define MLX4_FS_UDP_UC_EN (1 << 1) +#define MLX4_FS_TCP_UC_EN (1 << 2) +#define MLX4_FS_NUM_OF_L2_ADDR 8 +#define MLX4_FS_MGM_LOG_ENTRY_SIZE 7 +#define MLX4_FS_NUM_MCG (1 << 17) + +enum { + MLX4_FS_L2_HASH = 0, + MLX4_FS_L2_L3_L4_HASH, +}; + #define MLX4_NUM_UP 8 #define MLX4_NUM_TC 8 #define MLX4_RATELIMIT_UNITS 3 /* 100 Mbps */ @@ -704,6 +715,7 @@ struct mlx4_set_port_rqp_calc_context { struct mlx4_mac_entry { u64 mac; + u64 reg_id; }; struct mlx4_port_info { @@ -777,6 +789,7 @@ struct mlx4_priv { struct mutex bf_mutex; struct io_mapping *bf_mapping; int reserved_mtts; + int fs_hash_mode; }; static inline struct mlx4_priv *mlx4_priv(struct mlx4_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 1bb00cd22d42..2d6dabe7f55d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -414,6 +414,7 @@ struct mlx4_en_mc_list { struct list_head list; enum mlx4_en_mclist_act action; u8 addr[ETH_ALEN]; + u64 reg_id; }; struct mlx4_en_frag_info { @@ -503,6 +504,7 @@ struct mlx4_en_priv { u64 stats_bitmap; struct list_head mc_list; struct list_head curr_list; + u64 broadcast_id; struct mlx4_en_stat_out_mbox hw_stats; int vids[128]; bool wol; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 58de7237f57a..a51d1b9bf1d1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -75,21 +75,54 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table) table->total = 0; } -static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) +static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, + u64 mac, int *qpn, u64 *reg_id) { - struct mlx4_qp qp; - u8 gid[16] = {0}; __be64 be_mac; int err; - qp.qpn = *qpn; - - mac &= 0xffffffffffffULL; + mac &= MLX4_MAC_MASK; be_mac = cpu_to_be64(mac << 16); - memcpy(&gid[10], &be_mac, ETH_ALEN); - gid[5] = port; - err = mlx4_unicast_attach(dev, &qp, gid, 0, MLX4_PROT_ETH); + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: { + struct mlx4_qp qp; + u8 gid[16] = {0}; + + qp.qpn = *qpn; + memcpy(&gid[10], &be_mac, ETH_ALEN); + gid[5] = port; + + err = mlx4_unicast_attach(dev, &qp, gid, 0, MLX4_PROT_ETH); + break; + } + case MLX4_STEERING_MODE_DEVICE_MANAGED: { + struct mlx4_spec_list spec_eth = { {NULL} }; + __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); + + struct mlx4_net_trans_rule rule = { + .queue_mode = MLX4_NET_TRANS_Q_FIFO, + .exclusive = 0, + .allow_loopback = 1, + .promisc_mode = MLX4_FS_PROMISC_NONE, + .priority = MLX4_DOMAIN_NIC, + }; + + rule.port = port; + rule.qpn = *qpn; + INIT_LIST_HEAD(&rule.list); + + spec_eth.id = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(spec_eth.eth.dst_mac, &be_mac, ETH_ALEN); + memcpy(spec_eth.eth.dst_mac_msk, &mac_mask, ETH_ALEN); + list_add_tail(&spec_eth.list, &rule.list); + + err = mlx4_flow_attach(dev, &rule, reg_id); + break; + } + default: + return -EINVAL; + } if (err) mlx4_warn(dev, "Failed Attaching Unicast\n"); @@ -97,19 +130,30 @@ static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) } static void mlx4_uc_steer_release(struct mlx4_dev *dev, u8 port, - u64 mac, int qpn) + u64 mac, int qpn, u64 reg_id) { - struct mlx4_qp qp; - u8 gid[16] = {0}; - __be64 be_mac; + switch (dev->caps.steering_mode) { + case MLX4_STEERING_MODE_B0: { + struct mlx4_qp qp; + u8 gid[16] = {0}; + __be64 be_mac; - qp.qpn = qpn; - mac &= 0xffffffffffffULL; - be_mac = cpu_to_be64(mac << 16); - memcpy(&gid[10], &be_mac, ETH_ALEN); - gid[5] = port; + qp.qpn = qpn; + mac &= MLX4_MAC_MASK; + be_mac = cpu_to_be64(mac << 16); + memcpy(&gid[10], &be_mac, ETH_ALEN); + gid[5] = port; - mlx4_unicast_detach(dev, &qp, gid, MLX4_PROT_ETH); + mlx4_unicast_detach(dev, &qp, gid, MLX4_PROT_ETH); + break; + } + case MLX4_STEERING_MODE_DEVICE_MANAGED: { + mlx4_flow_detach(dev, reg_id); + break; + } + default: + mlx4_err(dev, "Invalid steering mode.\n"); + } } static int validate_index(struct mlx4_dev *dev, @@ -144,6 +188,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) struct mlx4_mac_entry *entry; int index = 0; int err = 0; + u64 reg_id; mlx4_dbg(dev, "Registering MAC: 0x%llx for adding\n", (unsigned long long) mac); @@ -167,7 +212,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) goto qp_err; } - err = mlx4_uc_steer_add(dev, port, mac, qpn); + err = mlx4_uc_steer_add(dev, port, mac, qpn, ®_id); if (err) goto steer_err; @@ -177,6 +222,7 @@ int mlx4_get_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn) goto alloc_err; } entry->mac = mac; + entry->reg_id = reg_id; err = radix_tree_insert(&info->mac_tree, *qpn, entry); if (err) goto insert_err; @@ -186,7 +232,7 @@ insert_err: kfree(entry); alloc_err: - mlx4_uc_steer_release(dev, port, mac, *qpn); + mlx4_uc_steer_release(dev, port, mac, *qpn, reg_id); steer_err: mlx4_qp_release_range(dev, *qpn, 1); @@ -212,7 +258,8 @@ void mlx4_put_eth_qp(struct mlx4_dev *dev, u8 port, u64 mac, int qpn) mlx4_dbg(dev, "Releasing qp: port %d, mac 0x%llx," " qpn %d\n", port, (unsigned long long) mac, qpn); - mlx4_uc_steer_release(dev, port, entry->mac, qpn); + mlx4_uc_steer_release(dev, port, entry->mac, + qpn, entry->reg_id); mlx4_qp_release_range(dev, qpn, 1); radix_tree_delete(&info->mac_tree, qpn); kfree(entry); @@ -363,11 +410,14 @@ int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac) entry = radix_tree_lookup(&info->mac_tree, qpn); if (!entry) return -EINVAL; - mlx4_uc_steer_release(dev, port, entry->mac, qpn); + mlx4_uc_steer_release(dev, port, entry->mac, + qpn, entry->reg_id); mlx4_unregister_mac(dev, port, entry->mac); entry->mac = new_mac; + entry->reg_id = 0; mlx4_register_mac(dev, port, new_mac); - err = mlx4_uc_steer_add(dev, port, entry->mac, &qpn); + err = mlx4_uc_steer_add(dev, port, entry->mac, + &qpn, &entry->reg_id); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/profile.c b/drivers/net/ethernet/mellanox/mlx4/profile.c index b83bc928d52a..9ee4725363d5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/profile.c +++ b/drivers/net/ethernet/mellanox/mlx4/profile.c @@ -237,13 +237,19 @@ u64 mlx4_make_profile(struct mlx4_dev *dev, init_hca->mtt_base = profile[i].start; break; case MLX4_RES_MCG: - dev->caps.num_mgms = profile[i].num >> 1; - dev->caps.num_amgms = profile[i].num >> 1; init_hca->mc_base = profile[i].start; init_hca->log_mc_entry_sz = ilog2(mlx4_get_mgm_entry_size(dev)); init_hca->log_mc_table_sz = profile[i].log_num; - init_hca->log_mc_hash_sz = profile[i].log_num - 1; + if (dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + dev->caps.num_mgms = profile[i].num; + } else { + init_hca->log_mc_hash_sz = + profile[i].log_num - 1; + dev->caps.num_mgms = profile[i].num >> 1; + dev->caps.num_amgms = profile[i].num >> 1; + } break; default: break; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index a8ca960f4620..5a6f3555d806 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -2744,6 +2744,9 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + if (dev->caps.steering_mode != + MLX4_STEERING_MODE_DEVICE_MANAGED) + return -EOPNOTSUPP; return mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, vhcr->in_modifier, 0, MLX4_QP_FLOW_STEERING_ATTACH, @@ -2757,6 +2760,9 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + if (dev->caps.steering_mode != + MLX4_STEERING_MODE_DEVICE_MANAGED) + return -EOPNOTSUPP; return mlx4_cmd(dev, vhcr->in_param, 0, 0, MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); -- cgit v1.2.3 From 1b9c6b064ef4fbc6f55485a8b5aab7ce889592c2 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:47 +0000 Subject: net/mlx4_core: Add resource tracking for device managed flow steering rules As with other device resources, the resource tracker is needed for supporting device managed flow steering rules under SRIOV: make sure virtual functions delete only rules created by them, and clean all rules attached by a crashed VF. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/mlx4.h | 1 + .../net/ethernet/mellanox/mlx4/resource_tracker.c | 132 +++++++++++++++++++-- 2 files changed, 125 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 0084967be19e..d2c436b10fbf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -149,6 +149,7 @@ enum mlx4_resource { RES_VLAN, RES_EQ, RES_COUNTER, + RES_FS_RULE, MLX4_NUM_OF_RESOURCE_TYPE }; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 5a6f3555d806..c3fa91986190 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -190,6 +190,15 @@ struct res_xrcdn { int port; }; +enum res_fs_rule_states { + RES_FS_RULE_BUSY = RES_ANY_BUSY, + RES_FS_RULE_ALLOCATED, +}; + +struct res_fs_rule { + struct res_common com; +}; + static void *res_tracker_lookup(struct rb_root *root, u64 res_id) { struct rb_node *node = root->rb_node; @@ -245,6 +254,7 @@ static const char *ResourceType(enum mlx4_resource rt) case RES_MAC: return "RES_MAC"; case RES_EQ: return "RES_EQ"; case RES_COUNTER: return "RES_COUNTER"; + case RES_FS_RULE: return "RES_FS_RULE"; case RES_XRCD: return "RES_XRCD"; default: return "Unknown resource type !!!"; }; @@ -516,6 +526,20 @@ static struct res_common *alloc_xrcdn_tr(int id) return &ret->com; } +static struct res_common *alloc_fs_rule_tr(u64 id) +{ + struct res_fs_rule *ret; + + ret = kzalloc(sizeof *ret, GFP_KERNEL); + if (!ret) + return NULL; + + ret->com.res_id = id; + ret->com.state = RES_FS_RULE_ALLOCATED; + + return &ret->com; +} + static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave, int extra) { @@ -549,6 +573,9 @@ static struct res_common *alloc_tr(u64 id, enum mlx4_resource type, int slave, case RES_XRCD: ret = alloc_xrcdn_tr(id); break; + case RES_FS_RULE: + ret = alloc_fs_rule_tr(id); + break; default: return NULL; } @@ -681,6 +708,16 @@ static int remove_xrcdn_ok(struct res_xrcdn *res) return 0; } +static int remove_fs_rule_ok(struct res_fs_rule *res) +{ + if (res->com.state == RES_FS_RULE_BUSY) + return -EBUSY; + else if (res->com.state != RES_FS_RULE_ALLOCATED) + return -EPERM; + + return 0; +} + static int remove_cq_ok(struct res_cq *res) { if (res->com.state == RES_CQ_BUSY) @@ -722,6 +759,8 @@ static int remove_ok(struct res_common *res, enum mlx4_resource type, int extra) return remove_counter_ok((struct res_counter *)res); case RES_XRCD: return remove_xrcdn_ok((struct res_xrcdn *)res); + case RES_FS_RULE: + return remove_fs_rule_ok((struct res_fs_rule *)res); default: return -EINVAL; } @@ -2744,14 +2783,28 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + int err; + if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) return -EOPNOTSUPP; - return mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, - vhcr->in_modifier, 0, - MLX4_QP_FLOW_STEERING_ATTACH, - MLX4_CMD_TIME_CLASS_A, - MLX4_CMD_NATIVE); + + err = mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, + vhcr->in_modifier, 0, + MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + if (err) + return err; + + err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, 0); + if (err) { + mlx4_err(dev, "Fail to add flow steering resources.\n "); + /* detach rule*/ + mlx4_cmd(dev, vhcr->out_param, 0, 0, + MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + } + return err; } int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, @@ -2760,12 +2813,22 @@ int mlx4_QP_FLOW_STEERING_DETACH_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + int err; + if (dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) return -EOPNOTSUPP; - return mlx4_cmd(dev, vhcr->in_param, 0, 0, - MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, - MLX4_CMD_NATIVE); + + err = rem_res_range(dev, slave, vhcr->in_param, 1, RES_FS_RULE, 0); + if (err) { + mlx4_err(dev, "Fail to remove flow steering resources.\n "); + return err; + } + + err = mlx4_cmd(dev, vhcr->in_param, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + return err; } enum { @@ -3177,6 +3240,58 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave) spin_unlock_irq(mlx4_tlock(dev)); } +static void rem_slave_fs_rule(struct mlx4_dev *dev, int slave) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource_tracker *tracker = + &priv->mfunc.master.res_tracker; + struct list_head *fs_rule_list = + &tracker->slave_list[slave].res_list[RES_FS_RULE]; + struct res_fs_rule *fs_rule; + struct res_fs_rule *tmp; + int state; + u64 base; + int err; + + err = move_all_busy(dev, slave, RES_FS_RULE); + if (err) + mlx4_warn(dev, "rem_slave_fs_rule: Could not move all mtts to busy for slave %d\n", + slave); + + spin_lock_irq(mlx4_tlock(dev)); + list_for_each_entry_safe(fs_rule, tmp, fs_rule_list, com.list) { + spin_unlock_irq(mlx4_tlock(dev)); + if (fs_rule->com.owner == slave) { + base = fs_rule->com.res_id; + state = fs_rule->com.from_state; + while (state != 0) { + switch (state) { + case RES_FS_RULE_ALLOCATED: + /* detach rule */ + err = mlx4_cmd(dev, base, 0, 0, + MLX4_QP_FLOW_STEERING_DETACH, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); + + spin_lock_irq(mlx4_tlock(dev)); + rb_erase(&fs_rule->com.node, + &tracker->res_tree[RES_FS_RULE]); + list_del(&fs_rule->com.list); + spin_unlock_irq(mlx4_tlock(dev)); + kfree(fs_rule); + state = 0; + break; + + default: + state = 0; + } + } + } + spin_lock_irq(mlx4_tlock(dev)); + } + spin_unlock_irq(mlx4_tlock(dev)); +} + static void rem_slave_eqs(struct mlx4_dev *dev, int slave) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -3318,5 +3433,6 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave) rem_slave_mtts(dev, slave); rem_slave_counters(dev, slave); rem_slave_xrcdns(dev, slave); + rem_slave_fs_rule(dev, slave); mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex); } -- cgit v1.2.3 From 592e49dda8122ab621cdc59cc429bdb968ee6364 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:48 +0000 Subject: net/mlx4: Implement promiscuous mode with device managed flow-steering The device managed flow steering API has three promiscuous modes: 1. Uplink - captures all the packets that arrive to the port. 2. Allmulti - captures all multicast packets arriving to the port. 3. Function port - for future use, this mode is not implemented yet. Use these modes with the flow_attach and flow_detach firmware commands according to the promiscuous state of the netdevice. Signed-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 41 ++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mcg.c | 60 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index eb5ed8e39873..b7945a80ad15 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -301,6 +301,16 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Enable promiscouos mode */ switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_add(mdev->dev, + priv->port, + priv->base_qpn, + MLX4_FS_PROMISC_UPLINK); + if (err) + en_err(priv, "Failed enabling promiscuous mode\n"); + priv->flags |= MLX4_EN_FLAG_MC_PROMISC; + break; + case MLX4_STEERING_MODE_B0: err = mlx4_unicast_promisc_add(mdev->dev, priv->base_qpn, @@ -357,6 +367,15 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Disable promiscouos mode */ switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_UPLINK); + if (err) + en_err(priv, "Failed disabling promiscuous mode\n"); + priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + break; + case MLX4_STEERING_MODE_B0: err = mlx4_unicast_promisc_remove(mdev->dev, priv->base_qpn, @@ -399,6 +418,13 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Add the default qp number as multicast promisc */ if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_add(mdev->dev, + priv->port, + priv->base_qpn, + MLX4_FS_PROMISC_ALL_MULTI); + break; + case MLX4_STEERING_MODE_B0: err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, @@ -416,6 +442,12 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) /* Disable Multicast promisc */ if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { switch (mdev->dev->caps.steering_mode) { + case MLX4_STEERING_MODE_DEVICE_MANAGED: + err = mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_ALL_MULTI); + break; + case MLX4_STEERING_MODE_B0: err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, @@ -839,6 +871,15 @@ int mlx4_en_start_port(struct net_device *dev) /* Must redo promiscuous mode setup. */ priv->flags &= ~(MLX4_EN_FLAG_PROMISC | MLX4_EN_FLAG_MC_PROMISC); + if (mdev->dev->caps.steering_mode == + MLX4_STEERING_MODE_DEVICE_MANAGED) { + mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_UPLINK); + mlx4_flow_steer_promisc_remove(mdev->dev, + priv->port, + MLX4_FS_PROMISC_ALL_MULTI); + } /* Schedule multicast task to populate multicast list */ queue_work(mdev->workqueue, &priv->mcast_task); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 768a2a4530e8..bc62f536ffae 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1295,6 +1295,66 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], } EXPORT_SYMBOL_GPL(mlx4_multicast_detach); +int mlx4_flow_steer_promisc_add(struct mlx4_dev *dev, u8 port, + u32 qpn, enum mlx4_net_trans_promisc_mode mode) +{ + struct mlx4_net_trans_rule rule; + u64 *regid_p; + + switch (mode) { + case MLX4_FS_PROMISC_UPLINK: + case MLX4_FS_PROMISC_FUNCTION_PORT: + regid_p = &dev->regid_promisc_array[port]; + break; + case MLX4_FS_PROMISC_ALL_MULTI: + regid_p = &dev->regid_allmulti_array[port]; + break; + default: + return -1; + } + + if (*regid_p != 0) + return -1; + + rule.promisc_mode = mode; + rule.port = port; + rule.qpn = qpn; + INIT_LIST_HEAD(&rule.list); + mlx4_err(dev, "going promisc on %x\n", port); + + return mlx4_flow_attach(dev, &rule, regid_p); +} +EXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_add); + +int mlx4_flow_steer_promisc_remove(struct mlx4_dev *dev, u8 port, + enum mlx4_net_trans_promisc_mode mode) +{ + int ret; + u64 *regid_p; + + switch (mode) { + case MLX4_FS_PROMISC_UPLINK: + case MLX4_FS_PROMISC_FUNCTION_PORT: + regid_p = &dev->regid_promisc_array[port]; + break; + case MLX4_FS_PROMISC_ALL_MULTI: + regid_p = &dev->regid_allmulti_array[port]; + break; + default: + return -1; + } + + if (*regid_p == 0) + return -1; + + ret = mlx4_flow_detach(dev, *regid_p); + if (ret == 0) + *regid_p = 0; + + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_flow_steer_promisc_remove); + int mlx4_unicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int block_mcast_loopback, enum mlx4_protocol prot) -- cgit v1.2.3 From 820672812f8284143f933da8ccc60e296230d25d Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:49 +0000 Subject: net/mlx4_en: Manage flow steering rules with ethtool Implement the ethtool APIs for attaching L2/L3/L4 based flow steering rules to the netdevice RX rings. Added set_rxnfc callback and enhanced the existing get_rxnfc callback. Signed-off-by: Hadar Hen Zion Signed-off-by: Amir Vadai Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 382 ++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 7 + 2 files changed, 389 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 72901ce2b088..3e72a2076fb5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -38,6 +38,10 @@ #include "mlx4_en.h" #include "en_port.h" +#define EN_ETHTOOL_QP_ATTACH (1ull << 63) +#define EN_ETHTOOL_MAC_MASK 0xffffffffffffULL +#define EN_ETHTOOL_SHORT_MASK cpu_to_be16(0xffff) +#define EN_ETHTOOL_WORD_MASK cpu_to_be32(0xffffffff) static void mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) @@ -599,16 +603,369 @@ static int mlx4_en_set_rxfh_indir(struct net_device *dev, return err; } +#define all_zeros_or_all_ones(field) \ + ((field) == 0 || (field) == (__force typeof(field))-1) + +static int mlx4_en_validate_flow(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct ethtool_usrip4_spec *l3_mask; + struct ethtool_tcpip4_spec *l4_mask; + struct ethhdr *eth_mask; + u64 full_mac = ~0ull; + u64 zero_mac = 0; + + if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) + return -EINVAL; + + switch (cmd->fs.flow_type & ~FLOW_EXT) { + case TCP_V4_FLOW: + case UDP_V4_FLOW: + if (cmd->fs.m_u.tcp_ip4_spec.tos) + return -EINVAL; + l4_mask = &cmd->fs.m_u.tcp_ip4_spec; + /* don't allow mask which isn't all 0 or 1 */ + if (!all_zeros_or_all_ones(l4_mask->ip4src) || + !all_zeros_or_all_ones(l4_mask->ip4dst) || + !all_zeros_or_all_ones(l4_mask->psrc) || + !all_zeros_or_all_ones(l4_mask->pdst)) + return -EINVAL; + break; + case IP_USER_FLOW: + l3_mask = &cmd->fs.m_u.usr_ip4_spec; + if (l3_mask->l4_4_bytes || l3_mask->tos || l3_mask->proto || + cmd->fs.h_u.usr_ip4_spec.ip_ver != ETH_RX_NFC_IP4 || + (!l3_mask->ip4src && !l3_mask->ip4dst) || + !all_zeros_or_all_ones(l3_mask->ip4src) || + !all_zeros_or_all_ones(l3_mask->ip4dst)) + return -EINVAL; + break; + case ETHER_FLOW: + eth_mask = &cmd->fs.m_u.ether_spec; + /* source mac mask must not be set */ + if (memcmp(eth_mask->h_source, &zero_mac, ETH_ALEN)) + return -EINVAL; + + /* dest mac mask must be ff:ff:ff:ff:ff:ff */ + if (memcmp(eth_mask->h_dest, &full_mac, ETH_ALEN)) + return -EINVAL; + + if (!all_zeros_or_all_ones(eth_mask->h_proto)) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if ((cmd->fs.flow_type & FLOW_EXT)) { + if (cmd->fs.m_ext.vlan_etype || + !(cmd->fs.m_ext.vlan_tci == 0 || + cmd->fs.m_ext.vlan_tci == cpu_to_be16(0xfff))) + return -EINVAL; + } + + return 0; +} + +static int add_ip_rule(struct mlx4_en_priv *priv, + struct ethtool_rxnfc *cmd, + struct list_head *list_h) +{ + struct mlx4_spec_list *spec_l3; + struct ethtool_usrip4_spec *l3_mask = &cmd->fs.m_u.usr_ip4_spec; + + spec_l3 = kzalloc(sizeof *spec_l3, GFP_KERNEL); + if (!spec_l3) { + en_err(priv, "Fail to alloc ethtool rule.\n"); + return -ENOMEM; + } + + spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; + spec_l3->ipv4.src_ip = cmd->fs.h_u.usr_ip4_spec.ip4src; + if (l3_mask->ip4src) + spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK; + spec_l3->ipv4.dst_ip = cmd->fs.h_u.usr_ip4_spec.ip4dst; + if (l3_mask->ip4dst) + spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK; + list_add_tail(&spec_l3->list, list_h); + + return 0; +} + +static int add_tcp_udp_rule(struct mlx4_en_priv *priv, + struct ethtool_rxnfc *cmd, + struct list_head *list_h, int proto) +{ + struct mlx4_spec_list *spec_l3; + struct mlx4_spec_list *spec_l4; + struct ethtool_tcpip4_spec *l4_mask = &cmd->fs.m_u.tcp_ip4_spec; + + spec_l3 = kzalloc(sizeof *spec_l3, GFP_KERNEL); + spec_l4 = kzalloc(sizeof *spec_l4, GFP_KERNEL); + if (!spec_l4 || !spec_l3) { + en_err(priv, "Fail to alloc ethtool rule.\n"); + kfree(spec_l3); + kfree(spec_l4); + return -ENOMEM; + } + + spec_l3->id = MLX4_NET_TRANS_RULE_ID_IPV4; + + if (proto == TCP_V4_FLOW) { + spec_l4->id = MLX4_NET_TRANS_RULE_ID_TCP; + spec_l3->ipv4.src_ip = cmd->fs.h_u.tcp_ip4_spec.ip4src; + spec_l3->ipv4.dst_ip = cmd->fs.h_u.tcp_ip4_spec.ip4dst; + spec_l4->tcp_udp.src_port = cmd->fs.h_u.tcp_ip4_spec.psrc; + spec_l4->tcp_udp.dst_port = cmd->fs.h_u.tcp_ip4_spec.pdst; + } else { + spec_l4->id = MLX4_NET_TRANS_RULE_ID_UDP; + spec_l3->ipv4.src_ip = cmd->fs.h_u.udp_ip4_spec.ip4src; + spec_l3->ipv4.dst_ip = cmd->fs.h_u.udp_ip4_spec.ip4dst; + spec_l4->tcp_udp.src_port = cmd->fs.h_u.udp_ip4_spec.psrc; + spec_l4->tcp_udp.dst_port = cmd->fs.h_u.udp_ip4_spec.pdst; + } + + if (l4_mask->ip4src) + spec_l3->ipv4.src_ip_msk = EN_ETHTOOL_WORD_MASK; + if (l4_mask->ip4dst) + spec_l3->ipv4.dst_ip_msk = EN_ETHTOOL_WORD_MASK; + + if (l4_mask->psrc) + spec_l4->tcp_udp.src_port_msk = EN_ETHTOOL_SHORT_MASK; + if (l4_mask->pdst) + spec_l4->tcp_udp.dst_port_msk = EN_ETHTOOL_SHORT_MASK; + + list_add_tail(&spec_l3->list, list_h); + list_add_tail(&spec_l4->list, list_h); + + return 0; +} + +static int mlx4_en_ethtool_to_net_trans_rule(struct net_device *dev, + struct ethtool_rxnfc *cmd, + struct list_head *rule_list_h) +{ + int err; + u64 mac; + __be64 be_mac; + struct ethhdr *eth_spec; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_spec_list *spec_l2; + __be64 mac_msk = cpu_to_be64(EN_ETHTOOL_MAC_MASK << 16); + + err = mlx4_en_validate_flow(dev, cmd); + if (err) + return err; + + spec_l2 = kzalloc(sizeof *spec_l2, GFP_KERNEL); + if (!spec_l2) + return -ENOMEM; + + mac = priv->mac & EN_ETHTOOL_MAC_MASK; + be_mac = cpu_to_be64(mac << 16); + + spec_l2->id = MLX4_NET_TRANS_RULE_ID_ETH; + memcpy(spec_l2->eth.dst_mac_msk, &mac_msk, ETH_ALEN); + if ((cmd->fs.flow_type & ~FLOW_EXT) != ETHER_FLOW) + memcpy(spec_l2->eth.dst_mac, &be_mac, ETH_ALEN); + + if ((cmd->fs.flow_type & FLOW_EXT) && cmd->fs.m_ext.vlan_tci) { + spec_l2->eth.vlan_id = cmd->fs.h_ext.vlan_tci; + spec_l2->eth.vlan_id_msk = cpu_to_be16(0xfff); + } + + list_add_tail(&spec_l2->list, rule_list_h); + + switch (cmd->fs.flow_type & ~FLOW_EXT) { + case ETHER_FLOW: + eth_spec = &cmd->fs.h_u.ether_spec; + memcpy(&spec_l2->eth.dst_mac, eth_spec->h_dest, ETH_ALEN); + spec_l2->eth.ether_type = eth_spec->h_proto; + if (eth_spec->h_proto) + spec_l2->eth.ether_type_enable = 1; + break; + case IP_USER_FLOW: + err = add_ip_rule(priv, cmd, rule_list_h); + break; + case TCP_V4_FLOW: + err = add_tcp_udp_rule(priv, cmd, rule_list_h, TCP_V4_FLOW); + break; + case UDP_V4_FLOW: + err = add_tcp_udp_rule(priv, cmd, rule_list_h, UDP_V4_FLOW); + break; + } + + return err; +} + +static int mlx4_en_flow_replace(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + int err; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct ethtool_flow_id *loc_rule; + struct mlx4_spec_list *spec, *tmp_spec; + u32 qpn; + u64 reg_id; + + struct mlx4_net_trans_rule rule = { + .queue_mode = MLX4_NET_TRANS_Q_FIFO, + .exclusive = 0, + .allow_loopback = 1, + .promisc_mode = MLX4_FS_PROMISC_NONE, + }; + + rule.port = priv->port; + rule.priority = MLX4_DOMAIN_ETHTOOL | cmd->fs.location; + INIT_LIST_HEAD(&rule.list); + + /* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */ + if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC) + return -EINVAL; + else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) { + qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1); + } else { + if (cmd->fs.ring_cookie >= priv->rx_ring_num) { + en_warn(priv, "rxnfc: RX ring (%llu) doesn't exist.\n", + cmd->fs.ring_cookie); + return -EINVAL; + } + qpn = priv->rss_map.qps[cmd->fs.ring_cookie].qpn; + if (!qpn) { + en_warn(priv, "rxnfc: RX ring (%llu) is inactive.\n", + cmd->fs.ring_cookie); + return -EINVAL; + } + } + rule.qpn = qpn; + err = mlx4_en_ethtool_to_net_trans_rule(dev, cmd, &rule.list); + if (err) + goto out_free_list; + + loc_rule = &priv->ethtool_rules[cmd->fs.location]; + if (loc_rule->id) { + err = mlx4_flow_detach(priv->mdev->dev, loc_rule->id); + if (err) { + en_err(priv, "Fail to detach network rule at location %d. registration id = %llx\n", + cmd->fs.location, loc_rule->id); + goto out_free_list; + } + loc_rule->id = 0; + memset(&loc_rule->flow_spec, 0, + sizeof(struct ethtool_rx_flow_spec)); + } + err = mlx4_flow_attach(priv->mdev->dev, &rule, ®_id); + if (err) { + en_err(priv, "Fail to attach network rule at location %d.\n", + cmd->fs.location); + goto out_free_list; + } + loc_rule->id = reg_id; + memcpy(&loc_rule->flow_spec, &cmd->fs, + sizeof(struct ethtool_rx_flow_spec)); + +out_free_list: + list_for_each_entry_safe(spec, tmp_spec, &rule.list, list) { + list_del(&spec->list); + kfree(spec); + } + return err; +} + +static int mlx4_en_flow_detach(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + int err = 0; + struct ethtool_flow_id *rule; + struct mlx4_en_priv *priv = netdev_priv(dev); + + if (cmd->fs.location >= MAX_NUM_OF_FS_RULES) + return -EINVAL; + + rule = &priv->ethtool_rules[cmd->fs.location]; + if (!rule->id) { + err = -ENOENT; + goto out; + } + + err = mlx4_flow_detach(priv->mdev->dev, rule->id); + if (err) { + en_err(priv, "Fail to detach network rule at location %d. registration id = 0x%llx\n", + cmd->fs.location, rule->id); + goto out; + } + rule->id = 0; + memset(&rule->flow_spec, 0, sizeof(struct ethtool_rx_flow_spec)); +out: + return err; + +} + +static int mlx4_en_get_flow(struct net_device *dev, struct ethtool_rxnfc *cmd, + int loc) +{ + int err = 0; + struct ethtool_flow_id *rule; + struct mlx4_en_priv *priv = netdev_priv(dev); + + if (loc < 0 || loc >= MAX_NUM_OF_FS_RULES) + return -EINVAL; + + rule = &priv->ethtool_rules[loc]; + if (rule->id) + memcpy(&cmd->fs, &rule->flow_spec, + sizeof(struct ethtool_rx_flow_spec)); + else + err = -ENOENT; + + return err; +} + +static int mlx4_en_get_num_flows(struct mlx4_en_priv *priv) +{ + + int i, res = 0; + for (i = 0; i < MAX_NUM_OF_FS_RULES; i++) { + if (priv->ethtool_rules[i].id) + res++; + } + return res; + +} + static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, u32 *rule_locs) { struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; int err = 0; + int i = 0, priority = 0; + + if ((cmd->cmd == ETHTOOL_GRXCLSRLCNT || + cmd->cmd == ETHTOOL_GRXCLSRULE || + cmd->cmd == ETHTOOL_GRXCLSRLALL) && + mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) + return -EINVAL; switch (cmd->cmd) { case ETHTOOL_GRXRINGS: cmd->data = priv->rx_ring_num; break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = mlx4_en_get_num_flows(priv); + break; + case ETHTOOL_GRXCLSRULE: + err = mlx4_en_get_flow(dev, cmd, cmd->fs.location); + break; + case ETHTOOL_GRXCLSRLALL: + while ((!err || err == -ENOENT) && priority < cmd->rule_cnt) { + err = mlx4_en_get_flow(dev, cmd, i); + if (!err) + rule_locs[priority++] = i; + i++; + } + err = 0; + break; default: err = -EOPNOTSUPP; break; @@ -617,6 +974,30 @@ static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, return err; } +static int mlx4_en_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + int err = 0; + struct mlx4_en_priv *priv = netdev_priv(dev); + struct mlx4_en_dev *mdev = priv->mdev; + + if (mdev->dev->caps.steering_mode != MLX4_STEERING_MODE_DEVICE_MANAGED) + return -EINVAL; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + err = mlx4_en_flow_replace(dev, cmd); + break; + case ETHTOOL_SRXCLSRLDEL: + err = mlx4_en_flow_detach(dev, cmd); + break; + default: + en_warn(priv, "Unsupported ethtool command. (%d)\n", cmd->cmd); + return -EINVAL; + } + + return err; +} + const struct ethtool_ops mlx4_en_ethtool_ops = { .get_drvinfo = mlx4_en_get_drvinfo, .get_settings = mlx4_en_get_settings, @@ -637,6 +1018,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { .get_ringparam = mlx4_en_get_ringparam, .set_ringparam = mlx4_en_set_ringparam, .get_rxnfc = mlx4_en_get_rxnfc, + .set_rxnfc = mlx4_en_set_rxnfc, .get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size, .get_rxfh_indir = mlx4_en_get_rxfh_indir, .set_rxfh_indir = mlx4_en_set_rxfh_indir, diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 2d6dabe7f55d..8882e70493fe 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -75,6 +75,7 @@ #define STAMP_SHIFT 31 #define STAMP_VAL 0x7fffffff #define STATS_DELAY (HZ / 4) +#define MAX_NUM_OF_FS_RULES 256 /* Typical TSO descriptor with 16 gather entries is 352 bytes... */ #define MAX_DESC_SIZE 512 @@ -435,6 +436,11 @@ struct mlx4_en_frag_info { #endif +struct ethtool_flow_id { + struct ethtool_rx_flow_spec flow_spec; + u64 id; +}; + struct mlx4_en_priv { struct mlx4_en_dev *mdev; struct mlx4_en_port_profile *prof; @@ -444,6 +450,7 @@ struct mlx4_en_priv { struct net_device_stats ret_stats; struct mlx4_en_port_state port_state; spinlock_t stats_lock; + struct ethtool_flow_id ethtool_rules[MAX_NUM_OF_FS_RULES]; unsigned long last_moder_packets[MAX_RX_RINGS]; unsigned long last_moder_tx_packets; -- cgit v1.2.3 From cabdc8ee3768ceb6367e88c6fe84a66dd667bdf9 Mon Sep 17 00:00:00 2001 From: Hadar Hen Zion Date: Thu, 5 Jul 2012 04:03:50 +0000 Subject: net/mlx4_en: Add support for drop action through ethtool The drop action is implemented by allocating a QP and keeping it in a reset state such that the HW drops any packets which are steered to that QP. When a drop action is requested, we attach the relevant flow to that QP. Sign-off-by: Hadar Hen Zion Signed-off-by: Or Gerlitz Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/en_ethtool.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 9 +++++++- drivers/net/ethernet/mellanox/mlx4/en_rx.c | 30 +++++++++++++++++++++++++ drivers/net/ethernet/mellanox/mlx4/mlx4_en.h | 3 +++ 4 files changed, 42 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 3e72a2076fb5..dd6a77b21149 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -821,7 +821,7 @@ static int mlx4_en_flow_replace(struct net_device *dev, /* Allow direct QP attaches if the EN_ETHTOOL_QP_ATTACH flag is set */ if (cmd->fs.ring_cookie == RX_CLS_FLOW_DISC) - return -EINVAL; + qpn = priv->drop_qp.qpn; else if (cmd->fs.ring_cookie & EN_ETHTOOL_QP_ATTACH) { qpn = cmd->fs.ring_cookie & (EN_ETHTOOL_QP_ATTACH - 1); } else { diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index b7945a80ad15..94375a8c6d42 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -796,6 +796,10 @@ int mlx4_en_start_port(struct net_device *dev) goto mac_err; } + err = mlx4_en_create_drop_qp(priv); + if (err) + goto rss_err; + /* Configure tx cq's and rings */ for (i = 0; i < priv->tx_ring_num; i++) { /* Configure cq */ @@ -895,7 +899,8 @@ tx_err: mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[tx_index]); mlx4_en_deactivate_cq(priv, &priv->tx_cq[tx_index]); } - + mlx4_en_destroy_drop_qp(priv); +rss_err: mlx4_en_release_rss_steer(priv); mac_err: mlx4_put_eth_qp(mdev->dev, priv->port, priv->mac, priv->base_qpn); @@ -950,6 +955,8 @@ void mlx4_en_stop_port(struct net_device *dev) /* Flush multicast filter */ mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG); + mlx4_en_destroy_drop_qp(priv); + /* Free TX Rings */ for (i = 0; i < priv->tx_ring_num; i++) { mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[i]); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index d49a7ac3187d..a04cbf767eb5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -844,6 +844,36 @@ out: return err; } +int mlx4_en_create_drop_qp(struct mlx4_en_priv *priv) +{ + int err; + u32 qpn; + + err = mlx4_qp_reserve_range(priv->mdev->dev, 1, 1, &qpn); + if (err) { + en_err(priv, "Failed reserving drop qpn\n"); + return err; + } + err = mlx4_qp_alloc(priv->mdev->dev, qpn, &priv->drop_qp); + if (err) { + en_err(priv, "Failed allocating drop qp\n"); + mlx4_qp_release_range(priv->mdev->dev, qpn, 1); + return err; + } + + return 0; +} + +void mlx4_en_destroy_drop_qp(struct mlx4_en_priv *priv) +{ + u32 qpn; + + qpn = priv->drop_qp.qpn; + mlx4_qp_remove(priv->mdev->dev, &priv->drop_qp); + mlx4_qp_free(priv->mdev->dev, &priv->drop_qp); + mlx4_qp_release_range(priv->mdev->dev, qpn, 1); +} + /* Allocate rx qp's and configure them according to rss map */ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) { diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 8882e70493fe..a12632150b34 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -500,6 +500,7 @@ struct mlx4_en_priv { struct mlx4_en_rx_ring rx_ring[MAX_RX_RINGS]; struct mlx4_en_cq *tx_cq; struct mlx4_en_cq rx_cq[MAX_RX_RINGS]; + struct mlx4_qp drop_qp; struct work_struct mcast_task; struct work_struct mac_task; struct work_struct watchdog_task; @@ -586,6 +587,8 @@ void mlx4_en_unmap_buffer(struct mlx4_buf *buf); void mlx4_en_calc_rx_buf(struct net_device *dev); int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv); void mlx4_en_release_rss_steer(struct mlx4_en_priv *priv); +int mlx4_en_create_drop_qp(struct mlx4_en_priv *priv); +void mlx4_en_destroy_drop_qp(struct mlx4_en_priv *priv); int mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring); void mlx4_en_rx_irq(struct mlx4_cq *mcq); -- cgit v1.2.3 From 95162d652477f6e3d04687f5d39d443fcc64d8d7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Jul 2012 04:31:01 +0000 Subject: asix: avoid copies in tx path I noticed excess calls to skb_copy_expand() or memmove() in asix driver. This driver needs to push 4 bytes in front of frame (packet_len) and maybe add 4 bytes after the end (if padlen is 4) So it should set needed_headroom & needed_tailroom to avoid copies. But its not enough, because many packets are cloned before entering asix_tx_fixup() and this driver use skb_cloned() as a lazy way to check if it can push and put additional bytes in frame. Avoid skb_copy_expand() expensive call, using following rules : - We are allowed to push 4 bytes in headroom if skb_header_cloned() is false (and if we have 4 bytes of headroom) - We are allowed to put 4 bytes at tail if skb_cloned() is false (and if we have 4 bytes of tailroom) TCP packets for example are cloned, but skb_header_release() was called in tcp stack, allowing us to use headroom for our needs. Signed-off-by: Eric Dumazet Cc: Greg Kroah-Hartman Cc: Allan Chou Cc: Trond Wuellner Cc: Grant Grundler Cc: Paul Stewart Cc: Ming Lei Tested-by: Ming Lei Signed-off-by: David S. Miller --- drivers/net/usb/asix.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 3ae80eccd0ef..6564c32d3af0 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -358,14 +358,30 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, padlen = ((skb->len + 4) & (dev->maxpacket - 1)) ? 0 : 4; - if ((!skb_cloned(skb)) && - ((headroom + tailroom) >= (4 + padlen))) { - if ((headroom < 4) || (tailroom < padlen)) { + /* We need to push 4 bytes in front of frame (packet_len) + * and maybe add 4 bytes after the end (if padlen is 4) + * + * Avoid skb_copy_expand() expensive call, using following rules : + * - We are allowed to push 4 bytes in headroom if skb_header_cloned() + * is false (and if we have 4 bytes of headroom) + * - We are allowed to put 4 bytes at tail if skb_cloned() + * is false (and if we have 4 bytes of tailroom) + * + * TCP packets for example are cloned, but skb_header_release() + * was called in tcp stack, allowing us to use headroom for our needs. + */ + if (!skb_header_cloned(skb) && + !(padlen && skb_cloned(skb)) && + headroom + tailroom >= 4 + padlen) { + /* following should not happen, but better be safe */ + if (headroom < 4 || + tailroom < padlen) { skb->data = memmove(skb->head + 4, skb->data, skb->len); skb_set_tail_pointer(skb, skb->len); } } else { struct sk_buff *skb2; + skb2 = skb_copy_expand(skb, 4, padlen, flags); dev_kfree_skb_any(skb); skb = skb2; @@ -373,8 +389,8 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, return NULL; } + packet_len = ((skb->len ^ 0x0000ffff) << 16) + skb->len; skb_push(skb, 4); - packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); cpu_to_le32s(&packet_len); skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); @@ -880,6 +896,8 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->netdev_ops = &ax88172_netdev_ops; dev->net->ethtool_ops = &ax88172_ethtool_ops; + dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ + dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, @@ -1075,6 +1093,8 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->netdev_ops = &ax88772_netdev_ops; dev->net->ethtool_ops = &ax88772_ethtool_ops; + dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */ + dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */ embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0); -- cgit v1.2.3 From 6ef1b512f4e6f936d89aa20be3d97a7ec7c290ac Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 22 Jun 2012 10:52:34 -0700 Subject: [SCSI] libsas: fix taskfile corruption in sas_ata_qc_fill_rtf fill_result_tf() grabs the taskfile flags from the originating qc which sas_ata_qc_fill_rtf() promptly overwrites. The presence of an ata_taskfile in the sata_device makes it tempting to just copy the full contents in sas_ata_qc_fill_rtf(). However, libata really only wants the fis contents and expects the other portions of the taskfile to not be touched by ->qc_fill_rtf. To that end store a fis buffer in the sata_device and use ata_tf_from_fis() like every other ->qc_fill_rtf() implementation. Cc: Reported-by: Praveen Murali Tested-by: Praveen Murali Signed-off-by: Dan Williams Signed-off-by: James Bottomley --- drivers/scsi/aic94xx/aic94xx_task.c | 2 +- drivers/scsi/libsas/sas_ata.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index 532d212b6b2c..393e7ce8e95a 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -201,7 +201,7 @@ static void asd_get_response_tasklet(struct asd_ascb *ascb, if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) { resp->frame_len = le16_to_cpu(*(__le16 *)(r+6)); - memcpy(&resp->ending_fis[0], r+16, 24); + memcpy(&resp->ending_fis[0], r+16, ATA_RESP_FIS_SIZE); ts->buf_valid_size = sizeof(*resp); } } diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 441d88ad99a7..d109cc3a17b6 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -139,12 +139,12 @@ static void sas_ata_task_done(struct sas_task *task) if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || ((stat->stat == SAM_STAT_CHECK_CONDITION && dev->sata_dev.command_set == ATAPI_COMMAND_SET))) { - ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + memcpy(dev->sata_dev.fis, resp->ending_fis, ATA_RESP_FIS_SIZE); if (!link->sactive) { - qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + qc->err_mask |= ac_err_mask(dev->sata_dev.fis[2]); } else { - link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.tf.command); + link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]); if (unlikely(link->eh_info.err_mask)) qc->flags |= ATA_QCFLAG_FAILED; } @@ -161,8 +161,8 @@ static void sas_ata_task_done(struct sas_task *task) qc->flags |= ATA_QCFLAG_FAILED; } - dev->sata_dev.tf.feature = 0x04; /* status err */ - dev->sata_dev.tf.command = ATA_ERR; + dev->sata_dev.fis[3] = 0x04; /* status err */ + dev->sata_dev.fis[2] = ATA_ERR; } } @@ -269,7 +269,7 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) { struct domain_device *dev = qc->ap->private_data; - memcpy(&qc->result_tf, &dev->sata_dev.tf, sizeof(qc->result_tf)); + ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf); return true; } -- cgit v1.2.3 From a77171806515fb5e2288219ddb47af1f0b1328e7 Mon Sep 17 00:00:00 2001 From: Eddie Wai Date: Fri, 29 Jun 2012 16:37:35 -0700 Subject: [SCSI] bnx2i: Removed the reference to the netdev->base_addr The netdev->base_addr parameter has been deprecated in the L2 bnx2 driver. This is used by bnx2i for the BARn iomapping. This patch will directly reference the pci_resource_start instead of using the deprecated netdev->base_addr. This patch is actually a critical bug fix as the 1G bnx2 driver no longer supports the netdev->base_addr in the current kernel of the scsi tree. This means that Broadcom's 1G Linux iSCSI offload solution would not work at all without this patch. Signed-off-by: Eddie Wai Reviewed-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/bnx2i/bnx2i.h | 1 + drivers/scsi/bnx2i/bnx2i_hwi.c | 3 +-- drivers/scsi/bnx2i/bnx2i_iscsi.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/bnx2i/bnx2i.h b/drivers/scsi/bnx2i/bnx2i.h index 0c53c28dc3d3..7e77cf620291 100644 --- a/drivers/scsi/bnx2i/bnx2i.h +++ b/drivers/scsi/bnx2i/bnx2i.h @@ -350,6 +350,7 @@ struct bnx2i_hba { struct pci_dev *pcidev; struct net_device *netdev; void __iomem *regview; + resource_size_t reg_base; u32 age; unsigned long cnic_dev_type; diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index ece47e502282..86a12b48e477 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -2724,7 +2724,6 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep) goto arm_cq; } - reg_base = ep->hba->netdev->base_addr; if ((test_bit(BNX2I_NX2_DEV_5709, &ep->hba->cnic_dev_type)) && (ep->hba->mail_queue_access == BNX2I_MQ_BIN_MODE)) { config2 = REG_RD(ep->hba, BNX2_MQ_CONFIG2); @@ -2740,7 +2739,7 @@ int bnx2i_map_ep_dbell_regs(struct bnx2i_endpoint *ep) /* 5709 device in normal node and 5706/5708 devices */ reg_off = CTX_OFFSET + (MB_KERNEL_CTX_SIZE * cid_num); - ep->qp.ctx_base = ioremap_nocache(reg_base + reg_off, + ep->qp.ctx_base = ioremap_nocache(ep->hba->reg_base + reg_off, MB_KERNEL_CTX_SIZE); if (!ep->qp.ctx_base) return -ENOMEM; diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index f8d516b53161..621538b8b544 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -811,13 +811,13 @@ struct bnx2i_hba *bnx2i_alloc_hba(struct cnic_dev *cnic) bnx2i_identify_device(hba); bnx2i_setup_host_queue_size(hba, shost); + hba->reg_base = pci_resource_start(hba->pcidev, 0); if (test_bit(BNX2I_NX2_DEV_5709, &hba->cnic_dev_type)) { - hba->regview = ioremap_nocache(hba->netdev->base_addr, - BNX2_MQ_CONFIG2); + hba->regview = pci_iomap(hba->pcidev, 0, BNX2_MQ_CONFIG2); if (!hba->regview) goto ioreg_map_err; } else if (test_bit(BNX2I_NX2_DEV_57710, &hba->cnic_dev_type)) { - hba->regview = ioremap_nocache(hba->netdev->base_addr, 4096); + hba->regview = pci_iomap(hba->pcidev, 0, 4096); if (!hba->regview) goto ioreg_map_err; } @@ -884,7 +884,7 @@ cid_que_err: bnx2i_free_mp_bdt(hba); mp_bdt_mem_err: if (hba->regview) { - iounmap(hba->regview); + pci_iounmap(hba->pcidev, hba->regview); hba->regview = NULL; } ioreg_map_err: @@ -910,7 +910,7 @@ void bnx2i_free_hba(struct bnx2i_hba *hba) pci_dev_put(hba->pcidev); if (hba->regview) { - iounmap(hba->regview); + pci_iounmap(hba->pcidev, hba->regview); hba->regview = NULL; } bnx2i_free_mp_bdt(hba); -- cgit v1.2.3 From 736f29cd6b7af9646a21a34866cd16277e03ee69 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Wed, 4 Jul 2012 18:13:48 +0530 Subject: OMAPDSS: Use PM notifiers for system suspend The current way how omapdss handles system suspend and resume is that omapdss device (a platform device, which is not part of the device hierarchy of the DSS HW devices, like DISPC and DSI, or panels.) uses the suspend and resume callbacks from platform_driver to handle system suspend. It does this by disabling all enabled panels on suspend, and resuming the previously disabled panels on resume. This presents a few problems. One is that as omapdss device is not related to the panel devices or the DSS HW devices, there's no ordering in the suspend process. This means that suspend could be first ran for DSS HW devices and panels, and only then for omapdss device. Currently this is not a problem, as DSS HW devices and panels do not handle suspend. Another, more pressing problem, is that when suspending or resuming, the runtime PM functions return -EACCES as runtime PM is disabled during system suspend. This causes the driver to print warnings, and operations to fail as they think that they failed to bring up the HW. This patch changes the omapdss suspend handling to use PM notifiers, which are called before suspend and after resume. This way we have a normally functioning system when we are suspending and resuming the panels. This patch, I believe, creates a problem that somebody could enable or disable a panel between PM_SUSPEND_PREPARE and the system suspend, and similarly the other way around in resume. I choose to ignore the problem for now, as it sounds rather unlikely, and if it happens, it's not fatal. In the long run the system suspend handling of omapdss and panels should be thought out properly. The current approach feels rather hacky. Perhaps the panel drivers should handle system suspend, or the users of omapdss (omapfb, omapdrm) should handle system suspend. Note that after this patch we could probably revert 0eaf9f52e94f756147dbfe1faf1f77a02378dbf9 (OMAPDSS: use sync versions of pm_runtime_put). But as I said, this patch may be temporary, so let's leave the sync version still in place. Signed-off-by: Tomi Valkeinen Reported-by: Jassi Brar Tested-by: Jassi Brar Tested-by: Joe Woodward Signed-off-by: Archit Taneja [fts: fixed 2 brace coding style issues] Signed-off-by: Florian Tobias Schandinat --- drivers/video/omap2/dss/core.c | 43 ++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c index 5066eee10ccf..58bd9c27369d 100644 --- a/drivers/video/omap2/dss/core.c +++ b/drivers/video/omap2/dss/core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include